MariaDB Galera Cluster

Материал из Национальной библиотеки им. Н. Э. Баумана
Последнее изменение этой страницы: 11:36, 2 июня 2017.
MariaDB Galera Cluster
180px
Разработчики: MariaDB Corporation Ab, MariaDB Foundation
Платформа: Linux
Локализация: Английский
Тип ПО: MySQL движок таблиц
Лицензия: GNU General Public License v.2
Веб-сайт https://mariadb.com/

Описание

Из описания MariaDB:

MariaDB — ответвление СУБД MySQL, разрабатываемое сообществом. MariaDB является фактически альтернативой MySQL СУБД, которую разрабатывает автор MySQL Michael "Monty" Widenius вместе с открытым сообществом. Основная цель проекта MariaDB - создание полностью бинарно совместимой с оригинальной MySQL версии СУБД, которая при этом будет иметь значительное количество улучшений в коде, влияющих на производительность. MariaDB разрабатывается как drop-in замена для MySQL, полностью имитируя поведение MySQL.

MariaDB Galera Cluster расширяет возможности СУБД MariaDB средствами для организации синхронной репликации данных между несколькими узлами . При синхронной репликации все узлы всегда содержат актуальные данные, т.е. гарантируется отсутствие потерянных транзакций, так как транзакция фиксируется только после распространения данных по всем узлам. При этом, в рамках транзакции операции выполняются сразу, задержка из-за ожидания подтверждения возникает только при выполнении операции "commit". На разных узлах транзакции могут выполняться в параллельном режиме. При выполнении транзакции обеспечивается гарантированная неизменность состояния кластера в целом, независимо от других выполняемых в данный момент транзакций.


К плюсам Galera относятся:

  • отсутствие единой точки отказа;
  • возможность развернуть решение в разных датацентрах для большей отказоустойчивости включая географически удаленные друг от друга;
  • синхронизация, отсутствие потери данных при репликации;
  • синхронизированная master-master репликация
  • возможность записи в любой узел и эмуляции master-slave топологии
  • лёгкость масштабирования: автоматическое очищение отключенных узлов и присоединение новых к кластеру;

Ограничения:

  • Galera Cluster работает только подсистемой innoDB/XtraDB (однако есть экспериментальная поддержка MyISAM);
  • при работе с транзацкиями нельзя блокировать доступ к таблицам.

Архитектура Galera Cluster

Galera предлагает master-master топологию, в которой каждый узел может модифицировать базу данных. Эти изменения отражаются на всех. В Galera Cluster возможно симулировать поведение «master-slave» топологии, однако это разделение топологий возможно только на уровне приложений: сама база данных использует только топологию «master-master» В Galera Cluster защита от возможной ситуации «split brain» тривиальна – алгоритм взвешенного опрашивания [1]. Достаточно использовать нечётное число узлов в кластере или назначить каждому узлу его «вес» - число, таким образом, что ни в каком случае не может возникнуть ситуации, что при разделении множества узлов на две части, сумма весов будет в обеих частях будет одинаковой. Если это условие выполняется, в этом случае меньшая часть при рассинхронизации войдёт в состояние NON-PRIMARY в котором запись в таблицу невозможна. Алгоритм репликации Репликация в Galera Cluster является синхронной: каждая транзакция на изменение базы данных выполняется параллельно на всех узлах кластера. Таким образом нивелируются риски потери данных и повышается скорость обновления данных.

[[|мини|слева|Рисунок 1: Схема синхронного режима репликации]]

Ко всему прочему, Galera использует несколько техник для того, чтобы уменьшить количество согласований транзакций, состояний гонок и конфликтов данных:

  • Групповая коммуникация. Это абстракция высокого уровня, которая описывает протокол взаимодействия узлов между собой. Этот протокол гарантирует корректность.
  • Множества записи. Таким образом группируются операции записи в некоторое множество записей, избегая ситуаций согласования всеми узлами одиночных записей.
  • Автомат состояний базы данных. Этот автомат обрабатывает транзакции чтения данных внутри локальной базы данных. Имплементация вначале выполняет транзакцию локально на некоторой копии базы данных, а затем передает уже множество всех операций чтения другим узлам для подтверждения и, возможно, получения транзакций обновления состояния.
  • Упорядочивание транзакций. Так, порядок транзакций, перед тем, как их подтвердить локально и передать другим узлам, меняется таким образом, чтобы как можно больше транзакций прошли тест согласования других узлов.

Подробнее об этом можно прочитать в документации Galera Cluster [2]

Установка и настройка MariaDB Galera Cluster

В данной работе для демонстрации работы MariaDB Galera Cluster используется следующая структура:

Файл:Http://withblue.ink/assets/docker-galera-arch.jpg
Структурная схема работы тестового стенда


Развертывание машин с CoreOS в данной статье происходит при помощи Vagrant. Используется кластер из трёх виртуальных машин, каждая из которых содержит Docker-контейнер с официальным образом mariadb версии 10.1[3][4].

Установка окружения

Установка CoreOS с помощью Vagrant

  1. Сперва создадим приватный ключ для того, чтобы обеспечить доступ на машины c CoreOS: по умолчанию вход по паролю в виртуальную машину запрещён.
  2. На Windows это можно сделать при помощи утилиты PuttyGen или аналогично тому, как это делается на ОС Linux, если у вас установлено окружение MinGW или MSYS.
  3. На OC Linux:
    ssh-keygen -q -b 2048 -t rsa -N <password> [-C mariadb-coreos-key] [-f private.key]
  4. Создадим Discovery Token для обнаружения узлами кластера  :
    1. Достаточно перейти по ссылке https://discovery.etcd.io/new
      Например, токен для данной работы: https://discovery.etcd.io/542163c8446342fee5921ca67dd05c5c
  5. Склонируем официальный репозиторий команды CoreOS с примером конфигурации Vagrantfile: git clone https://github.com/coreos/coreos-vagrant
    Из имеющих значение файлов здесь: Vagrantfile, config.rb.sample, user-data.sample.
    • user-data.sample – пример конфигурационного файла для системы CoreOS. Подробнее о конфигурационных файлах можно узнать, обратившись к официальной документации [5]. В частности, здесь указывается discovery token для кластеров CoreOS и публичный ключ для доступа к системе по протоколу SSH.
    • config.rb.sample – пример дополнительной конфигурации Vagrant-образа. Здесь, в частности, указываются такие параметры, как имя хоста машин, канал получения обновлений, версия ОС, количество машин для создания, выделяемые ресурсы и другое.
    • Vagrantfile – основной файл, описывающий шаги создания виртуальной машины с образом CoreOS.
  6. Настроим необходимые параметры:
    1. количество узлов должно быть нечётным и не менее трёх.
    2. клиентский порт MySQL должен быть доступен для пользователя.
    3. IP-адреса должны быть известны перед созданием образов
    4. Каждая машина должна иметь не менее 512 мб оперативной памяти.

Пример конфигурации (тройки user-data, config.rb и Vagrantfile) для создания трёх машин с образом CoreOS версии latest и источника обновлений stable:

user-data:

#cloud-config

---
coreos:
  etcd2:
    advertise-client-urls: http://$public_ipv4:2379
    initial-advertise-peer-urls: http://$private_ipv4:2380
    listen-client-urls: http://0.0.0.0:2379,http://0.0.0.0:4001
    listen-peer-urls: http://$private_ipv4:2380,http://$private_ipv4:7001
    discovery: https://discovery.etcd.io/<discovery_token>
  fleet:
    public-ip: "$public_ipv4"
  flannel:
    interface: "$public_ipv4"
  units:
  - name: etcd2.service
    command: start
  - name: fleet.service
    command: start
  - name: flanneld.service
    drop-ins:
    - name: 50-network-config.conf
      content: |
        [Service]
        ExecStartPre=/usr/bin/etcdctl set /coreos.com/network/config '{ "Network": "10.1.0.0/16" }'
    command: start
  - name: docker-tcp.socket
    command: start
    enable: true
    content: |
      [Unit]
      Description=Docker Socket for the API

      [Socket]
      ListenStream=2375
      Service=docker.service
      BindIPv6Only=both

      [Install]
      WantedBy=sockets.target
users:
- name: core
  ssh_authorized_keys:
  - <ssh-rsa public>

Листинг 1: Пример конфигурации образа CoreOS.

config.rb

# Size of the CoreOS cluster created by Vagrant
$num_instances=3

# Used to fetch a new discovery token for a cluster of size $num_instances
$new_discovery_Режим доступа="https://discovery.etcd.io/new?size=#{$num_instances}"

# Automatically replace the discovery token on 'vagrant up'

if File.exists?('user-data') && ARGV[0].eql?('up')
  require 'open-uri'
  require 'yaml'

  token = open($new_discovery_Режим доступа).read

  data = YAML.load(IO.readlines('user-data')[1..-1].join)

  if data.key? 'coreos' and data['coreos'].key? 'etcd'
    data['coreos']['etcd']['discovery'] = token
  end

  if data.key? 'coreos' and data['coreos'].key? 'etcd2'
    data['coreos']['etcd2']['discovery'] = token
  end

  # Fix for YAML.load() converting reboot-strategy from 'off' to `false`
  if data.key? 'coreos' and data['coreos'].key? 'update' and data['coreos']['update'].key? 'reboot-strategy'
    if data['coreos']['update']['reboot-strategy'] == false
      data['coreos']['update']['reboot-strategy'] = 'off'
    end
  end

  yaml = YAML.dump(data)
  File.open('user-data', 'w') { |file| file.write("#cloud-config\n\n#{yaml}") }
end

#
# coreos-vagrant is configured through a series of configuration
# options (global ruby variables) which are detailed below. To modify
# these options, first copy this file to "config.rb". Then simply
# uncomment the necessary lines, leaving the $, and replace everything
# after the equals sign..

# Change basename of the VM
# The default value is "core", which results in VMs named starting with
# "core-01" through to "core-${num_instances}".
$instance_name_prefix="mariadb-node"

# Change the version of CoreOS to be installed
# To deploy a specific version, simply set $image_version accordingly.
# For example, to deploy version 709.0.0, set $image_version="709.0.0".
# The default value is "current", which points to the current version
# of the selected channel
$image_version = "current"

# Official CoreOS channel from which updates should be downloaded
$update_channel='stable'

# Log the serial consoles of CoreOS VMs to log/
# Enable by setting value to true, disable with false
# WARNING: Serial logging is known to result in extremely high CPU usage with
# VirtualBox, so should only be used in debugging situations
#$enable_serial_logging=false

# Enable port forwarding of Docker TCP socket
# Set to the TCP port you want exposed on the *host* machine, default is 2375
# If 2375 is used, Vagrant will auto-increment (e.g. in the case of $num_instances > 1)
# You can then use the docker tool locally by setting the following env var:
#   export DOCKER_HOST='tcp://127.0.0.1:2375'
#$expose_docker_tcp=2375

# Enable NFS sharing of your home directory ($HOME) to CoreOS
# It will be mounted at the same path in the VM as on the host.
# Example: /Users/foobar -> /Users/foobar
#$share_home=false

# Customize VMs
#$vm_gui = false
#$vm_memory = 1024
#$vm_cpus = 1
#$vb_cpuexecutioncap = 100

# Share additional folders to the CoreOS VMs
# For example,
# $shared_folders = {'/path/on/host' => '/path/on/guest', '/home/foo/app' => '/app'}
# or, to map host folders to guest folders of the same name,
# $shared_folders = Hash[*['/home/foo/app1', '/home/foo/app2'].map{|d| [d, d]}.flatten]
#$shared_folders = {}

# Enable port forwarding from guest(s) to host machine, syntax is: { 80 => 8080 }, auto correction is enabled by default.
$forwarded_ports = { 3306 => 33060 }

Листинг 1: Пример дополнительных настроек для Vagrantfile.

Vagrantfile:

# -*- mode: ruby -*-
# # vi: set ft=ruby :

require 'fileutils'

Vagrant.require_version ">= 1.6.0"

CLOUD_CONFIG_PATH = File.join(File.dirname(__FILE__), "user-data")
CONFIG = File.join(File.dirname(__FILE__), "config.rb")
CURRENT_DIR = File.dirname(__FILE__)

# Defaults for config options defined in CONFIG
$num_instances = 1
$instance_name_prefix = "mariadb-node"
$update_channel = "stable"
$image_version = "current"
$enable_serial_logging = false
$share_home = false
$vm_gui = false
$vm_memory = 1024
$vm_cpus = 1
$vb_cpuexecutioncap = 100
$shared_folders = {}
$forwarded_ports = {}

# Attempt to apply the deprecated environment variable NUM_INSTANCES to
# $num_instances while allowing config.rb to override it
if ENV["NUM_INSTANCES"].to_i > 0 && ENV["NUM_INSTANCES"]
  $num_instances = ENV["NUM_INSTANCES"].to_i
end

if File.exist?(CONFIG)
  require CONFIG
end

# Use old vb_xxx config variables when set
def vm_gui
  $vb_gui.nil? ? $vm_gui : $vb_gui
end

def vm_memory
  $vb_memory.nil? ? $vm_memory : $vb_memory
end

def vm_cpus
  $vb_cpus.nil? ? $vm_cpus : $vb_cpus
end

$ips = *(1..$num_instances).map { |i| "172.17.8.#{i+100}" }
# bind by direct ips
File.open("tmp_mysql_server.cnf", "wb") do |f|
  f.write(File.read("#{CURRENT_DIR}/../config/mysql_server.cnf") % $ips.join(","))
end
# CONFIG

Vagrant.configure("2") do |config|
  # config.ssh.private_key_path = 'mariadb-coreos.key'
  # always use Vagrants insecure key
  config.ssh.insert_key = false
  # forward ssh agent to easily ssh into the different machines
  config.ssh.forward_agent = true

  config.vm.box = "coreos-%s" % $update_channel
  if $image_version != "current"
      config.vm.box_version = $image_version
  end
  config.vm.box_Режим доступа = "https://storage.googleapis.com/%s.release.core-os.net/amd64-usr/%s/coreos_production_vagrant.json" % [$update_channel, $image_version]

  ["vmware_fusion", "vmware_workstation"].each do |vmware|
    config.vm.provider vmware do |v, override|
      override.vm.box_Режим доступа = "https://storage.googleapis.com/%s.release.core-os.net/amd64-usr/%s/coreos_production_vagrant_vmware_fusion.json" % [$update_channel, $image_version]
    end
  end

  config.vm.provider :virtualbox do |v|
    # On VirtualBox, we don't have guest additions or a functional vboxsf
    # in CoreOS, so tell Vagrant that so it can be smarter.
    v.check_guest_additions = false
    v.functional_vboxsf     = false
  end

  # plugin conflict
  if Vagrant.has_plugin?("vagrant-vbguest") then
    config.vbguest.auto_update = false
  end

  (1..$num_instances).each do |i|
    config.vm.define vm_name = "%s-%02d" % [$instance_name_prefix, i] do |config|
      config.vm.hostname = vm_name

      if $enable_serial_logging
        logdir = File.join(File.dirname(__FILE__), "log")
        FileUtils.mkdir_p(logdir)

        serialFile = File.join(logdir, "%s-serial.txt" % vm_name)
        FileUtils.touch(serialFile)

        ["vmware_fusion", "vmware_workstation"].each do |vmware|
          config.vm.provider vmware do |v, override|
            v.vmx["serial0.present"] = "TRUE"
            v.vmx["serial0.fileType"] = "file"
            v.vmx["serial0.fileName"] = serialFile
            v.vmx["serial0.tryNoRxLoss"] = "FALSE"
          end
        end

        config.vm.provider :virtualbox do |vb, override|
          vb.customize ["modifyvm", :id, "--uart1", "0x3F8", "4"]
          vb.customize ["modifyvm", :id, "--uartmode1", serialFile]
        end
      end

      if $expose_docker_tcp
        config.vm.network "forwarded_port", guest: 2375, host: ($expose_docker_tcp + i - 1), host_ip: "127.0.0.1", auto_correct: true
      end

      $forwarded_ports.each do |guest, host|
        config.vm.network "forwarded_port", guest: guest, host: host, auto_correct: true
      end

      ["vmware_fusion", "vmware_workstation"].each do |vmware|
        config.vm.provider vmware do |v|
          v.gui = vm_gui
          v.vmx['memsize'] = vm_memory
          v.vmx['numvcpus'] = vm_cpus
        end
      end

      config.vm.provider :virtualbox do |vb|
        vb.gui = vm_gui
        vb.memory = vm_memory
        vb.cpus = vm_cpus
        vb.customize ["modifyvm", :id, "--cpuexecutioncap", "#{$vb_cpuexecutioncap}"]
      end

      
      config.vm.network :private_network, ip: $ips[i-1]

      # Uncomment below to enable NFS for sharing the host machine into the coreos-vagrant VM.
      #config.vm.synced_folder ".", "/home/core/share", id: "core", :nfs => true, :mount_options => ['nolock,vers=3,udp']
      $shared_folders.each_with_index do |(host_folder, guest_folder), index|
        config.vm.synced_folder host_folder.to_s, guest_folder.to_s, id: "core-share%02d" % index, nfs: true, mount_options: ['nolock,vers=3,udp']
      end

      if $share_home
        config.vm.synced_folder ENV['HOME'], ENV['HOME'], id: "home", :nfs => true, :mount_options => ['nolock,vers=3,udp']
      end

      if File.exist?(CLOUD_CONFIG_PATH)
        config.vm.provision :file, :source => "#{CLOUD_CONFIG_PATH}", :destination => "/tmp/vagrantfile-user-data"
        config.vm.provision :shell, :inline => "mv /tmp/vagrantfile-user-data /var/lib/coreos-vagrant/", :privileged => true
      end
      
    end
  end
end

Листинг 3: Пример конфигурационного файла Vagrantfile, описывающего создание виртуальных машин с помощью Vagrant.

  1. Создадим виртуальные машины командой: vagrant up

Осталось только проверить, что образы создались, все настройки успешно скопировались и доступ к виртуальным машинам возможен.

Настрокйка окружения и установка MariaDB с Docker

Для установки MariaDB в данной работе использовуются официальные подготовленные Docker-образы с MariaDB версии 10.1. Алгоритм создания кластера следующий:

  1. Создать контейнер с MariaDB, предоставив подготовленный конфигурационный файл
  2. Создать полноценный сервер СУБД внутри данного контейнера
  3. Создать кластер из единственного узла
  4. Создать остальные контейнеры с MariaDB и той же конфигурацией
  5. Присоединить узлы к кластеру, не инициализируя в самом начале полноценный сервер СУБД.

В конфигурации сервера будет использоваться следующая информация:

  • первый контейнер называется «mariadb-0»
  • название всех остальных – «mariadb-1»
  • пароль для пользователя «root»: «my-secret-pw»
  • конфигурационный файл будет иметь путь «/opt/local/etc/mysql.d/mysql_server.cnf»


  1. Для начала создадим вспомогательные скрипты, с помощью которых можно устанавливать и удалять Docker-контейнеры с MariaDB Galera Cluster, останавливать их и использовать встроенный MySQL-клиент для отладки и подготовки базы данных к работе.

Вот пример скриптов:


mariadb_delete.sh

#!/bin/sh
docker rm $(docker stop $(docker ps -a -q --filter name=mariadb --format="{{.ID}}")) ; true

Листинг 4: Пример скрипта, удаляюшего все образы с mariadb. mariadb_init_first.sh

  1. !/bin/sh

Листинг 5: Пример скрипта, устанавливающего MariaDB на первый узел.

docker run \
  --name mariadb-0 \
  -d \
  -v /opt/local/etc/mysql.conf.d:/etc/mysql/conf.d \
  -v /mnt/data/mariadb:/var/lib/mysql \
  -e MYSQL_INITDB_SKIP_TZINFO=yes \
  -e MYSQL_ROOT_PASSWORD=my-secret-pw \
  -p 3306:3306 \
  -p 4567:4567/udp \
  -p 4567-4568:4567-4568 \
  -p 4444:4444 \
  mariadb:10.1 \
  --wsrep-new-cluster \
  --wsrep_node_address=$(ip -4 addr ls eth1 | awk '/inet / {print $2}' | cut -d"/" -f1)

Листинг 6: Пример скрипта, устанавливающео MariaDB на все остальные узлы. mariadb_init_others.sh

#!/bin/sh
docker run \
  --name mariadb-1 \
  -d \
  -v /opt/local/etc/mysql.conf.d:/etc/mysql/conf.d \
  -v /mnt/data/mariadb:/var/lib/mysql \
  -p 3306:3306 \
  -p 4567:4567/udp \
  -p 4567-4568:4567-4568 \
  -p 4444:4444 \
  mariadb:10.1 \
  --wsrep_node_address=$(ip -4 addr ls eth1 | awk '/inet / {print $2}' | cut -d"/" -f1)

Листинг 7: Пример скрипта, останавливающего все машины с mariadb.

mariadb_stop.sh

#!/bin/sh
docker stop $(docker ps -a -q --filter name=mariadb --format="{{.ID}}")

Листинг 8: Пример скрипта, запускающего встроенный клиент MariaDB.

cli.sh

#!/bin/sh
# The default password in the sample above is "my-secret-pw"
export MYSQL_IP=$(ip -4 addr ls eth1 | awk '/inet / {print $2}' | cut -d"/" -f1)
docker run --rm -it mariadb:10.1 mysql -h $MYSQL_IP -u root -p


  1. Добавим конфигурацию сервера. В данном случае конфигурационный файл будет дополнен в момент выполнения работы Vagrant'а списком узлов кластера для автоматизации процесса создания кластера из узлов. В противном случае пришлось бы вручную добавлять каждый узел в кластер. Вот пример такой конфигурации:

Листинг 9: Пример конфигурации MariaDB Galer Cluster сервера СУБД.

mysql_server.cnf

#
# Galera Cluster: mandatory settings
#

[server]
bind-address=0.0.0.0
binlog_format=row
default_storage_engine=InnoDB
innodb_autoinc_lock_mode=2
innodb_locks_unsafe_for_binlog=1
query_cache_size=0
query_cache_type=0

[galera]
wsrep_on=ON
wsrep_provider="/usr/lib/galera/libgalera_smm.so"
wsrep_cluster_address="gcomm://%s"
wsrep-sst-method=rsync

#
# Optional setting
#

# Tune this value for your system, roughly 2x cores; see https://mariadb.com/kb/en/mariadb/galera-cluster-system-variables/#wsrep_slave_threads
# wsrep_slave_threads=1

# innodb_flush_log_at_trx_commit=0

Стоит заметить форматирующую строку «%s» в 18 строке – это делает конфигурационный файл – в том виде, в котором он есть сейчас – неверным.


  1. Изменим конфигурацию Vagrant-файла, добавив копирование скриптов на машины и их выполнение.
  2. Проверим работу кластера:

Листинг 10: Пример вывода состояния кластера

$ ./cli.sh 
$ <password input>

MariaDB [(none)]> SHOW STATUS LIKE 'wsrep_%';
+------------------------------+-------------------------------------------------------+
| Variable_name                | Value                                                 |
+------------------------------+-------------------------------------------------------+
| wsrep_apply_oooe             | 0.217105                                              |
| wsrep_apply_oool             | 0.001107                                              |
| wsrep_apply_window           | 1.802554                                              |
| wsrep_causal_reads           | 0                                                     |
| wsrep_cert_deps_distance     | 6.515574                                              |
| wsrep_cert_index_size        | 12                                                    |
| wsrep_cert_interval          | 38.732134                                             |
| wsrep_cluster_conf_id        | 9                                                     |
| wsrep_cluster_size           | 3                                                     |
| wsrep_cluster_state_uuid     | 2d0892a8-4736-11e7-83bb-5ba2bc65d3db                  |
| wsrep_cluster_status         | Primary                                               |
| wsrep_commit_oooe            | 0.000000                                              |
| wsrep_commit_oool            | 0.000000                                              |
| wsrep_commit_window          | 1.531008                                              |
| wsrep_connected              | ON                                                    |
| wsrep_desync_count           | 0                                                     |
| wsrep_evs_delayed            |                                                       |
| wsrep_evs_evict_list         |                                                       |
| wsrep_evs_repl_latency       | 0/0/0/0/0                                             |
| wsrep_evs_state              | OPERATIONAL                                           |
| wsrep_flow_control_paused    | 0.001394                                              |
| wsrep_flow_control_paused_ns | 17838049207                                           |
| wsrep_flow_control_recv      | 1542                                                  |
| wsrep_flow_control_sent      | 104                                                   |
| wsrep_gcomm_uuid             | c09c6be8-4737-11e7-a44d-76e430fdd50c                  |
| wsrep_incoming_addresses     | 172.17.8.103:3306,172.17.8.101:3306,172.17.8.102:3306 |
| wsrep_last_committed         | 64144                                                 |
| wsrep_local_bf_aborts        | 0                                                     |
| wsrep_local_cached_downto    | 1                                                     |
| wsrep_local_cert_failures    | 0                                                     |
| wsrep_local_commits          | 56661                                                 |
| wsrep_local_index            | 1                                                     |
| wsrep_local_recv_queue       | 0                                                     |
| wsrep_local_recv_queue_avg   | 21.964768                                             |
| wsrep_local_recv_queue_max   | 114                                                   |
| wsrep_local_recv_queue_min   | 0                                                     |
| wsrep_local_replays          | 0                                                     |
| wsrep_local_send_queue       | 0                                                     |
| wsrep_local_send_queue_avg   | 17.394481                                             |
| wsrep_local_send_queue_max   | 100                                                   |
| wsrep_local_send_queue_min   | 0                                                     |
| wsrep_local_state            | 4                                                     |
| wsrep_local_state_comment    | Synced                                                |
| wsrep_local_state_uuid       | 2d0892a8-4736-11e7-83bb-5ba2bc65d3db                  |
| wsrep_protocol_version       | 7                                                     |
| wsrep_provider_name          | Galera                                                |
| wsrep_provider_vendor        | Codership Oy <info@codership.com>                     |
| wsrep_provider_version       | 25.3.20(r3703)                                        |
| wsrep_ready                  | ON                                                    |
| wsrep_received               | 8515                                                  |
| wsrep_received_bytes         | 2536344                                               |
| wsrep_repl_data_bytes        | 13328670                                              |
| wsrep_repl_keys              | 170221                                                |
| wsrep_repl_keys_bytes        | 2668858                                               |
| wsrep_repl_other_bytes       | 0                                                     |
| wsrep_replicated             | 56830                                                 |
| wsrep_replicated_bytes       | 19634648                                              |
| wsrep_thread_count           | 2                                                     |
+------------------------------+-------------------------------------------------------+
58 rows in set (0.04 sec)

Как видно из вывода, все узлы внутри кластера и в рабочем состоянии.

Тестирование

Проведем тестирование с помощью встроенного в MySQL и MariaDB инструмента: «mysqlslap»[6]. Эта утилита позволяет провести нагрузочное тестирование. Посмотрим, как изменится нагрузка на узел при внезапном отключении одного из трёх узлов.

Нагрузка в режиме write-only

В этом тесте проверяется изменение нагрузки только для запросов вида INSERT. UPDATE, CREATE.

  1. Создадим тестовую базу данных mysqlslap :
    CREATE DATABASE mysqlslap;
    
  2. Запустим тестирование на одном из узлов (mariadb-node-02): mysqlslap --delimiter=";" -u root -p -P 2200 --auto-generate-sql --auto-generate-sql-load-type=write --concurrency=100 -vv --iterations=100
  3. Посмотрим на нагрузку:
    1. Это можно сделать следующей командой: top -p `pgrep mysqld | tr "\\n" "," | sed 's/,$//'`

Листинг 11: Пример вывода нагрузки узлов кластера до отключения одного из узлов (тест write-only)

mariadb-node-01

Tasks:   1 total,   0 running,   1 sleeping,   0 stopped,   0 zombie
%Cpu(s):  1.7 us, 20.4 sy,  0.0 ni, 67.1 id,  3.5 wa,  4.2 hi,  3.1 si,  0.0 st
KiB Mem:   1020800 total,   950368 used,    70432 free,    70692 buffers
KiB Swap:        0 total,        0 used,        0 free.   282808 cached Mem

  PID USER      PR  NI    VIRT    RES    SHR S %CPU %MEM     TIME+ COMMAND
 7006 999       20   0 1285640 360064  41976 S 21.3 35.3   0:59.45 mysqld


mariadb-node-02


top - 05:48:42 up  4:08,  1 user,  load average: 0.20, 0.08, 0.05
Tasks:   1 total,   0 running,   1 sleeping,   0 stopped,   0 zombie
%Cpu(s):  3.4 us, 35.8 sy,  0.0 ni, 47.0 id,  1.0 wa,  6.1 hi,  6.8 si,  0.0 st
KiB Mem:   1020800 total,   943944 used,    76856 free,    78028 buffers
KiB Swap:        0 total,        0 used,        0 free.   352396 cached Mem

  PID USER      PR  NI    VIRT    RES    SHR S %CPU %MEM     TIME+ COMMAND
 2637 999       20   0 1263976 389216  50324 S 37.6 38.1   0:52.51 mysqld


mariadb-node-03


Tasks:   1 total,   0 running,   1 sleeping,   0 stopped,   0 zombie
%Cpu(s):  1.4 us, 19.8 sy,  0.0 ni, 68.4 id,  3.8 wa,  3.8 hi,  2.8 si,  0.0 st
KiB Mem:   1020800 total,   890132 used,   130668 free,    79316 buffers
KiB Swap:        0 total,        0 used,        0 free.   454044 cached Mem

  PID USER      PR  NI    VIRT    RES    SHR S %CPU %MEM     TIME+ COMMAND
 2449 999       20   0 1091904 244732  55236 S 20.0 24.0   0:58.22 mysqld
  1. Отключим узел mariadb-node-03 и посмотрим на разницу: docker kill mariadb-1


Листинг 12: Пример вывода нагрузки узлов кластера после отключения одного из узлов (тест write-only)

mariadb-node-02


Tasks:   1 total,   0 running,   1 sleeping,   0 stopped,   0 zombie
%Cpu(s):  3.7 us, 37.6 sy,  0.0 ni, 43.6 id,  2.0 wa,  6.0 hi,  7.0 si,  0.0 st
KiB Mem:   1020800 total,   955880 used,    64920 free,    64288 buffers
KiB Swap:        0 total,        0 used,        0 free.   303516 cached Mem

  PID USER      PR  NI    VIRT    RES    SHR S %CPU %MEM     TIME+ COMMAND
 2637 999       20   0 1281544 414920  69840 S 39.0 40.6   1:41.00 mysqld


mariadb-node-01


Tasks:   1 total,   0 running,   1 sleeping,   0 stopped,   0 zombie
%Cpu(s):  0.7 us, 21.8 sy,  0.0 ni, 65.3 id,  5.3 wa,  4.2 hi,  2.8 si,  0.0 st
KiB Mem:   1020800 total,   940684 used,    80116 free,    62224 buffers
KiB Swap:        0 total,        0 used,        0 free.   259748 cached Mem

  PID USER      PR  NI    VIRT    RES    SHR S %CPU %MEM     TIME+ COMMAND
 7006 999       20   0 1285640 396172  55700 S 23.0 38.8   1:44.39 mysqld

Как видно, нагрузка увеличилась слабо: большая часть времени уходила на согласование транзакций.

Нагрузка в режиме read-only

Проделаем те же самые действия, однако запустим тест с другими параметрами: mysqlslap --delimiter=";" -u root -p -P 2200 --auto-generate-sql --auto-generate-sql-load-type=read --concurrency=100 -vv --iterations=100

До отключения:

Листинг 13: Пример вывода нагрузки узлов кластера до отключения одного из узлов (тест read-only)

mariadb-node-02

Tasks:   1 total,   0 running,   1 sleeping,   0 stopped,   0 zombie
%Cpu(s):  6.0 us,  7.4 sy,  0.0 ni, 76.8 id,  2.3 wa,  2.7 hi,  4.7 si,  0.0 st
KiB Mem:   1020800 total,   955620 used,    65180 free,    55372 buffers
KiB Swap:        0 total,        0 used,        0 free.   287124 cached Mem

  PID USER      PR  NI    VIRT    RES    SHR S %CPU %MEM     TIME+ COMMAND
 2637 999       20   0 1281544 429628  75640 S 11.3 42.1   2:05.36 mysqld


mariadb-node-01


Tasks:   1 total,   0 running,   1 sleeping,   0 stopped,   0 zombie
%Cpu(s):  1.0 us,  3.7 sy,  0.0 ni, 89.9 id,  2.7 wa,  1.3 hi,  1.3 si,  0.0 st
KiB Mem:   1020800 total,   953144 used,    67656 free,    79324 buffers
KiB Swap:        0 total,        0 used,        0 free.   540544 cached Mem

  PID USER      PR  NI    VIRT    RES    SHR S %CPU %MEM     TIME+ COMMAND
 2736 999       20   0 1091904 184176  20364 S  3.0 18.0   0:01.68 mysqld


mariadb-node-03

Tasks:   1 total,   0 running,   1 sleeping,   0 stopped,   0 zombie
%Cpu(s):  1.0 us,  6.1 sy,  0.0 ni, 86.5 id,  3.0 wa,  1.7 hi,  1.7 si,  0.0 st
KiB Mem:   1020800 total,   948128 used,    72672 free,    39184 buffers
KiB Swap:        0 total,        0 used,        0 free.   277868 cached Mem

  PID USER      PR  NI    VIRT    RES    SHR S %CPU %MEM     TIME+ COMMAND
 7006 999       20   0 1293836 383708  33900 S  5.3 37.6   1:58.18 mysqld

После:

Листинг 14: Пример вывода нагрузки узлов кластера после отключения одного из узлов (тест read-only)

mariadb-node-02


%Cpu(s):  6.0 us, 10.7 sy,  0.0 ni, 73.6 id,  1.7 wa,  3.0 hi,  5.0 si,  0.0 st
KiB Mem:   1020800 total,   957264 used,    63536 free,    53304 buffers
KiB Swap:        0 total,        0 used,        0 free.   277680 cached Mem

  PID USER      PR  NI    VIRT    RES    SHR S %CPU %MEM     TIME+ COMMAND
 2637 999       20   0 1281544 435280  73660 S 13.6 42.6   2:16.27 mysqld


mariadb-node-01


Tasks:   1 total,   0 running,   1 sleeping,   0 stopped,   0 zombie
%Cpu(s):  1.0 us,  3.7 sy,  0.0 ni, 89.9 id,  2.7 wa,  1.3 hi,  1.3 si,  0.0 st
KiB Mem:   1020800 total,   953144 used,    67656 free,    79324 buffers
KiB Swap:        0 total,        0 used,        0 free.   540544 cached Mem

  PID USER      PR  NI    VIRT    RES    SHR S %CPU %MEM     TIME+ COMMAND
 2736 999       20   0 1091904 184176  20364 S  3.0 18.0   0:01.68 mysqld


Колебания нагрзуки в пределах погрешности: два остальных узла были загружены только тем, что подтверждали приходящий read-set с транзакциями. Однако это не говорит о том, что метод синхронизации в чём-то плох, это означает, что необходимы сторонние средства балансировки трафика.

Ссылки

  1. "Weighted Quorum" http://galeracluster.com/documentation-webpages/weightedquorum.html (последняя дата обращения: 01.06.17)
  2. "Database Replication" http://galeracluster.com/documentation-webpages/introduction.html (последняя дата обращения: 31.06.17)
  3. "Official Repository: mariadb" https://hub.docker.com/_/mariadb/ (последняя дата обращения: 29.05.17)
  4. "Galera cluster, MariaDB, CoreOS and Docker (Part 1)" http://withblue.ink/2016/03/09/galera-cluster-mariadb-coreos-and-docker-part-1.html (последняя дата обращения: 02.06.17)
  5. "Running CoreOS Container Linux on Vagrant" https://coreos.com/os/docs/latest/booting-on-vagrant.html (последняя дата обращения: 29.05.17)
  6. "mysqlslap — Load Emulation Client" https://dev.mysql.com/doc/refman/5.7/en/mysqlslap.html#option_mysqlslap_auto-generate-sql-load-type (последняя дата обращения: 01.06.17)