Spec-Zone.ru › MariaDB

Параллельная репликация

В исторической репликации использовались термины мастер и слейв, но сейчас предпочтительнее термины первичный и реплика. Старые термины всё ещё используются в некоторых частях документации и командах MariaDB, хотя MariaDB 10.5 начала процесс переименования. Процесс документации продолжается. Следите за прогрессом на MDEV-18777.

Некоторые записи, реплицированные с первичного сервера, могут выполняться параллельно (одновременно) на реплике. Обратите внимание, что для работы параллельной репликации, как первичный, так и реплицируемый сервер должны быть версии MariaDB 10.0.5 или более поздней.

Обзор параллельной репликации

Репликация MariaDB, в целом, происходит в три этапа:

  • События репликации считываются с первичного сервера потоком ввода-вывода и помещаются в очередь в журнал репликации.
  • События репликации извлекаются по одному потоком SQL из журнала репликации.
  • Каждое событие применяется на реплике для репликации всех изменений, произведённых на первичном сервере.

До MariaDB 10, третий шаг также выполнялся потоком SQL; это означало, что только одно событие могло выполняться за раз, и репликация была по сути однопоточной. С MariaDB 10, третий шаг может быть необязательно выполнен пулом отдельных потоков репликации, тем самым потенциально повышая производительность репликации путём одновременного применения нескольких событий.

Как включить параллельную реплику

Для включения укажите slave-parallel-threads=# в вашем файле my.cnf как аргумент для mysql. Параллельная репликация дополнительно может быть отключена на подключении к нескольким источникам, установив @@connection_name.slave-parallel-mode в "none".

Значение (#) slave_parallel_threads определяет количество потоков, которые будут созданы в пуле рабочих потоков, используемых для одновременного применения событий для *всех* ваших реплик (включая репликацию с несколькими источниками). Если значение равно нулю, то рабочие потоки не создаются, и используется репликация старого стиля, где события применяются внутри потока SQL. Обычно значение, если оно не равно нулю, должно быть по меньшей мере вдвое больше количества подключений к первичным серверам с несколькими источниками. Использовать только один рабочий поток для одного подключения не имеет смысла; это создаст некоторую нагрузку при межпоточной коммуникации между потоком SQL и рабочим потоком, но с одним рабочим потоком события всё равно не могут применяться параллельно.

slave-parallel-threads=# — это динамическая переменная, которую можно изменить без перезапуска mysqld. Однако при изменении значения необходимо остановить все подключения реплик.

Настройка режима параллельной репликации

Параллельная репликация может быть по порядку или вне порядка:

  • По порядку выполняет транзакции параллельно, но упорядочивает этап фиксации транзакций таким образом, чтобы происходить в том же порядке, что и на первичном сервере. Транзакции выполняются параллельно только в той мере, в которой это может быть автоматически проверено, чтобы это было возможно без конфликтов. Это означает, что использование параллелизма полностью прозрачно для приложения.
  • Вне порядка может выполнять и фиксировать транзакции в разном порядке на реплике, чем первоначально на первичном сервере. Это означает, что приложение должно быть устойчиво к появлению обновлений в разном порядке. Приложение также несёт ответственность за обеспечение отсутствия конфликтов между транзакциями, которые реплицируются вне порядка. Режим вне порядка используется только в режиме GTID и только при явном включении приложением, используя домен репликации, который является частью GTID.

Параллельная репликация по порядку

Оптимистичный режим параллельной репликации по порядку

Оптимистичный режим параллельной репликации по порядку предоставляет множество возможностей для параллельного применения на реплике, сохраняя при этом точную семантику транзакций с точки зрения приложений. Это режим по умолчанию начиная с MariaDB 10.5.1.

Оптимистичный режим параллельной репликации по порядку может быть настроен путём установки переменной системы slave_parallel_mode на реплике в optimistic.

Любые транзакционные DML (INSERT/UPDATE/DELETE) разрешены для параллельного выполнения до предела @@slave_domain_parallel_threads. Это может вызвать конфликты на реплике, например, если две транзакции пытаются изменить одну и ту же строку. Любой такой конфликт обнаруживается, и последняя из двух транзакций откатывается, позволяя первой продолжить работу. Последняя транзакция затем повторно выполняется после завершения первой.

Термин «оптимистичный» используется для этого режима, поскольку сервер оптимистично предполагает, что конфликтов произойдёт мало, и что дополнительная работа по откату и повторному выполнению конфликтующих транзакций оправдана выигрышем от одновременного выполнения большинства транзакций.

Существуют несколько эвристик для попытки избежать ненужных конфликтов. Если транзакция выполнила ожидание блокировки строки на первичном сервере, она не будет выполняться параллельно на реплике. Транзакции также могут быть явным образом помечены как потенциально конфликтующие на первичном сервере, установив переменную @@skip_parallel_replication. В будущих версиях MariaDB могут быть добавлены ещё такие эвристики. Существует ещё один режим --slave-parallel-mode под названием «агрессивный», где эти эвристики отключены, что позволяет применять ещё больше транзакций параллельно.

Нетранзакционные DML и DDL небезопасны для оптимистичного параллельного применения, так как их нельзя откатить в случае конфликтов. Таким образом, в оптимистичном режиме не транзакционные (например, MyISAM) обновления не применяются параллельно с предыдущими событиями (однако возможно применение обновления MyISAM параллельно с последующим обновлением InnoDB). DDL-операторы не применяются параллельно с другими транзакциями, предшествующими или последующими.

Разные типы транзакций можно идентифицировать по результатам работы mariadb-binlog. Например:

#150324 13:06:26 server id 1  end_log_pos 6881 	GTID 0-1-42 ddl
...
#150324 13:06:26 server id 1  end_log_pos 7816 	GTID 0-1-47
...
#150324 13:06:26 server id 1  end_log_pos 8177  GTID 0-1-49 trans
/*!100101 SET @@session.skip_parallel_replication=1*//*!*/;
...
#150324 13:06:26 server id 1  end_log_pos 9836 	GTID 0-1-59 trans waited

GTID 0-1-42 помечен как DDL. GTID 0-1-47 помечен как не транзакционный DML, в то время как GTID 0-1-49 является транзакционным DML (видно по ключевому слову "trans"). GTID 0-1-49 был дополнительно запущен с @@skip_parallel_replication, установленным на первичном сервере. GTID 0-1-59 — это транзакционный DML, который ожидал блокировку строки при выполнении на первичном сервере (ключевое слово "waited").

Агрессивный режим параллельной репликации по порядку

Агрессивный режим параллельной репликации по порядку очень похож на оптимистичный режим. Основное отличие заключается в том, что реплика не учитывает, конфликтовали ли транзакции на первичном сервере при принятии решения о том, следует ли применять транзакции параллельно.

Агрессивный режим параллельной репликации по порядку может быть настроен путём установки переменной системы slave_parallel_mode на реплике в aggressive.

Консервативный режим параллельной репликации по порядку

Консервативный режим параллельной репликации по порядку использует групповую фиксацию на первичном сервере для определения возможностей параллельного применения событий на реплике. Если две транзакции фиксируются вместе в групповой фиксации на первичном сервере, они записываются в журнал бинарных логов с одним идентификатором фиксации. Такие события гарантированно не конфликтуют друг с другом, и параллельная репликация может распределить их по различным рабочим потокам.

Консервативный режим параллельной репликации по порядку является режимом по умолчанию до MariaDB 10.5.0, но его также можно настроить, установив переменную системы slave_parallel_mode на реплике в conservative.

Две транзакции, которые были зафиксированы раздельно на первичном сервере, могут потенциально конфликтовать (например, изменять одну и ту же строку таблицы). Таким образом, рабочий поток, применяющий вторую транзакцию, не начнётся немедленно, а дождётся, пока первый поток начнёт этап фиксации; в этот момент можно безопасно начать вторую транзакцию, так как она уже не может нарушить выполнение первой.

Вот пример вывода mariadb-binlog, который показывает, как события GTID помечаются идентификатором фиксации. GTID 0-1-47 не имеет идентификатора фиксации и не может выполняться параллельно. GTID 0-1-48 и 0-1-49 имеют один и тот же идентификатор фиксации 630 и, следовательно, могут быть реплицированы параллельно на реплике:

#150324 12:54:24 server id 1  end_log_pos 20052 	GTID 0-1-47 trans
...
#150324 12:54:24 server id 1  end_log_pos 20212 	GTID 0-1-48 cid=630 trans
...
#150324 12:54:24 server id 1  end_log_pos 20372 	GTID 0-1-49 cid=630 trans

В любом случае, когда две транзакции достигают момента, когда происходит низкоуровневая фиксация и определяется порядок фиксации, две фиксации упорядочиваются таким образом, чтобы происходили в том же порядке, что и на первичном сервере, таким образом, эта операция прозрачна для приложений.

Возможности параллельной репликации на репликах могут быть значительно увеличены, если больше транзакций фиксируются в групповой фиксации на первичном сервере. Это можно настроить, используя переменные binlog_commit_wait_count и binlog_commit_wait_usec. Если, например, приложение может допустить задержку транзакций на первичном сервере до 50 миллисекунд, можно установить binlog_commit_wait_usec=50000 и binlog_commit_wait_count=20 для получения до 20 транзакций одновременно, доступных для параллельной репликации. Однако необходимо быть осторожным, чтобы не устанавливать binlog_commit_wait_usec слишком высоким, так как это может привести к значительному замедлению для приложений, которые выполняют много небольших транзакций последовательно одна за другой.

Обратите внимание, что даже если параллелизм недоступен на основном сервере (с помощью группового подтверждения), всё ещё существует возможность ускорения благодаря параллельной репликации в порядке следования, так как фактические шаги подтверждения разных транзакций могут выполняться параллельно. Это особенно эффективно на реплике с включённым бинарным журналом (log_slave_updates=1), и ещё больше, если реплика настроена для устойчивости к сбоям (sync_binlog=1 и innodb_flush_log_at_trx_commit=1), так как это делает возможным групповое подтверждение на реплике.

Минимальный режим параллельной репликации в порядке следования

Минимальный режим параллельной репликации в порядке следования только позволяет выполнять шаг подтверждения транзакций параллельно; все остальные шаги выполняются последовательно.

Минимальный режим параллельной репликации в порядке следования можно настроить, установив системную переменную slave_parallel_mode на minimal на реплике.

Параллельная репликация вне порядка следования

Параллельная репликация вне порядка следования происходит (только) при использовании режима GTID, когда используются GTID с разными доменами репликации. Домен репликации устанавливается администратором базы данных/приложением с использованием переменной gtid_domain_id.

Две транзакции с GTID, имеющими разные значения domain_id, планируются в разные рабочие потоки параллельной репликацией и могут выполняться полностью независимо друг от друга. Приложение должно устанавливать разные значения domain_id только для транзакций, которые действительно независимы и гарантированно не конфликтуют друг с другом. Приложение также должно правильно работать, даже если транзакции с разными значениями domain_id отображаются как подтверждаемые в разном порядке между репликой и основным сервером, а также между различными репликами.

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

Одно простое, но эффективное использование — запуск длительных операторов, таких как ALTER TABLE, в отдельном домене репликации. Это позволяет продолжать репликацию других транзакций без прерывания:

SET SESSION gtid_domain_id=1
ALTER TABLE t ADD INDEX myidx(b)
SET SESSION gtid_domain_id=0

Обычно длительный запрос ALTER TABLE или другой запрос приостановит все последующие транзакции, что приведёт к тому, что реплика отстанет от основного сервера как минимум на время, необходимое для выполнения длительного запроса. Используя параллельную репликацию вне порядка следования, установив идентификатор домена репликации, этого можно избежать. Администратор базы данных/приложение должно гарантировать, что во время выполнения ALTER TABLE не будет реплицироваться никаких конфликтующих транзакций.

Ещё один распространённый случай для параллельной репликации вне порядка следования связан с репликацией с нескольких источников. Предположим, у нас есть два разных основных сервера M1 и M2, и мы используем репликацию с нескольких источников для того, чтобы S1 был репликой как M1, так и M2. S1 будет применять события, полученные от M1, параллельно с событиями, полученными от M2. Если теперь у нас есть реплика третьего уровня S2, которая реплицирует S1 как основной сервер, мы хотим, чтобы S2 также мог применять события, исходящие от M1, параллельно с событиями, исходящими от M2. Этого можно достичь с помощью параллельной репликации вне порядка следования, установив gtid_domain_id разные для M1 и M2.

Обратите внимание, что нет никаких специальных ограничений на то, какие операции могут быть реплицированы параллельно с помощью параллельной репликации вне порядка следования; такие операции могут выполняться в одной и той же базе данных/схеме или даже в одной и той же таблице. Единственное ограничение состоит в том, что операции не должны конфликтовать, то есть они должны быть применимы в любом порядке и при этом давать тот же результат.

При использовании параллельной репликации вне порядка следования текущая позиция реплики в бинарном журнале основного сервера становится многомерной — каждый домен репликации может достичь разных точек в бинарном журнале основного сервера в любое время. Текущую позицию можно увидеть в переменной gtid_slave_pos. Когда реплика остановлена, перезапущена или переключена на репликацию с другого основного сервера с помощью CHANGE MASTER, MariaDB автоматически обрабатывает перезапуск каждого домена репликации в соответствующей точке бинарного журнала.

Параллельная репликация вне порядка следования отключена, когда --slave-parallel-mode=minimal (или не задано).

Проверка статуса рабочих потоков в SHOW PROCESSLIST

Рабочие потоки будут отображаться как "система" в SHOW PROCESSLIST. Их состояние покажет запрос, над которым они в настоящее время работают, или одно из следующих:

  • "Ожидание работы от основных SQL-потоков". Это означает, что рабочий поток бездействует, в данный момент для него нет доступной работы.
  • "Ожидание начала подтверждения предыдущей транзакции перед началом следующей транзакции". Это означает, что предыдущая группа транзакций, которые были подтверждены вместе на основном сервере, должна быть завершена первой. Этот рабочий поток ждёт этого, прежде чем сможет начать работу над следующей группой.
  • "Ожидание подтверждения предыдущей транзакции". Это означает, что транзакция была выполнена рабочим потоком. Для обеспечения подтверждения в порядке следования рабочий поток ждёт подтверждения, пока предыдущая транзакция не будет готова к подтверждению.

Ожидаемое повышение производительности

В этой статье показано увеличение производительности до десяти раз при использовании параллельной репликации: http://kristiannielsen.livejournal.com/18435.html.

Настройка максимального размера очереди параллельной реплики

Системная переменная slave_parallel_max_queued может быть использована для настройки максимального размера очереди параллельной реплики. Эта системная переменная имеет смысл только при настройке параллельной репликации (то есть, когда slave_parallel_threads > 0).

Когда используется параллельная репликация, SQL-поток будет читать вперёд в журналах ретрансляции, помещая события в очередь в памяти, одновременно ища возможности для параллельного выполнения событий. Системная переменная slave_parallel_max_queued задаёт предел, сколько памяти будет использоваться для этого.

Настроенное значение переменной slave_parallel_max_queued фактически выделяется для каждого рабочего потока, поэтому общее выделение фактически эквивалентно следующему:

slave_parallel_max_queued * slave_parallel_threads

Если это значение слишком высокое, а реплика сильно отстаёт (например, гигабайты бинарного журнала) от основного сервера, то SQL-поток может быстро прочитать всё это и заполнить память огромным количеством событий бинарного журнала быстрее, чем рабочие потоки смогут их обработать.

С другой стороны, если значение слишком низкое, SQL-поток может не иметь достаточно места для постановки в очередь достаточного количества событий, чтобы занять рабочие потоки, что может снизить производительность. В этом случае у SQL-потока будет состояние потока, которое указывает Waiting for room in worker thread event queue. Например:

+----+-------------+-----------+------+---------+--------+-----------------------------------------------+------------------+----------+
| Id | User        | Host      | db   | Command | Time   | State                                         | Info             | Progress |
+----+-------------+-----------+------+---------+--------+-----------------------------------------------+------------------+----------+
|  3 | system user |           | NULL | Connect |    139 | closing tables                                | NULL             |    0.000 |
|  4 | system user |           | NULL | Connect |    139 | Waiting for work from SQL thread              | NULL             |    0.000 |
|  6 | system user |           | NULL | Connect | 264274 | Waiting for master to send event              | NULL             |    0.000 |
| 10 | root        | localhost | NULL | Sleep   |     43 |                                               | NULL             |    0.000 |
| 21 | system user |           | NULL | Connect |     45 | Waiting for room in worker thread event queue | NULL             |    0.000 |
| 54 | root        | localhost | NULL | Query   |      0 | init                                          | SHOW PROCESSLIST |    0.000 |
+----+-------------+-----------+------+---------+--------+-----------------------------------------------+------------------+----------+

Переменная slave_parallel_max_queued не определяет жёсткий лимит, так как события бинарного журнала, которые в настоящее время выполняются, всегда должны храниться в памяти. Это означает, что как минимум два события на рабочий поток всегда могут быть помещены в очередь в памяти, независимо от значения slave_parallel_threads.

Обычно системная переменная slave_parallel_threads должна быть достаточно большой для того, чтобы SQL-поток мог прочитать достаточно вперёд в бинарном журнале, чтобы использовать весь возможный параллелизм. В нормальной работе реплика, надеюсь, не сильно отстаёт, поэтому не будет необходимости помещать много данных в очередь в памяти. Переменная slave_parallel_max_queued может быть установлена достаточно высокой (например, несколько сотен килобайт), чтобы не ограничивать пропускную способность. Она должна быть достаточно низкой для того, чтобы общее выделение очереди параллельной реплики не привело к тому, что сервер не хватит оперативной памяти.

Переменная конфигурации slave_domain_parallel_threads

Пул рабочих потоков репликации используется совместно для всех подключений к основному серверу с несколькими источниками и для всех доменов репликации, которые могут реплицироваться параллельно с помощью параллельной репликации вне порядка следования.

Если одно подключение к основному серверу или домен репликации в настоящее время выполняют длительный запрос, возможно, что они выделят все рабочие потоки в пуле, только для того, чтобы они ожидали завершения длительного запроса, что остановит любые другие подключения к основному серверу или домены репликации, которые должны будут ждать освобождения рабочего потока.

Этому можно избежать, установив slave_domain_parallel_threads на значение меньшее, чем slave_parallel_threads. Когда задано отличное от нуля значение, каждый домен репликации в одном подключении к основному серверу может зарезервировать не более указанного количества рабочих потоков в любое время, оставив остальные (до значения slave_parallel_threads) свободными для использования другими подключениями к основному серверу или доменами репликации параллельно.

Переменная slave_domain_parallel_threads динамическая и может быть изменена без перезапуска сервера; однако, во время изменения её необходимо остановить все реплики.

Подробности реализации

Реализация описана в MDEV-4506.

См. также

  • Улучшенная параллельная репликация для MariaDB и MySQL (блог MariaDB.com)
  • Оценка параллельной репликации MariaDB и MySQL, часть 2: групповое подтверждение на сервере-следующем (блог MariaDB.com)
Содержимое, воспроизведенное на этом сайте, является собственностью соответствующих владельцев, и это содержимое не проверяется заранее компанией MariaDB. Мнения, информация и мнения, выраженные в этом содержании, не обязательно отражают точку зрения MariaDB или любой другой стороны.

© 2023 MariaDB
Licensed under the Creative Commons Attribution 3.0 Unported License and the GNU Free Documentation License.
https://mariadb.com/kb/en/parallel-replication/

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API