Статьи

Замена асинхронного подчиненного устройства кластера PXC на новый мастер

Этот пост принадлежит Джей Янссену из MySQL Performance Blog.

Async и PXC

Обычный вопрос, который я получаю о кластере Percona XtraDB, заключается в том, можете ли вы смешать его с асинхронной репликацией, и ответ — да! Вы можете выбрать любой узел в вашем кластере, и он может быть либо ведомым, либо ведущим, как любой другой обычный автономный сервер MySQL (просто убедитесь, что в обоих случаях на данном узле используются обновления log-slave!). Рассмотрим эту архитектуру:

Холст 1

Однако есть некоторые предостережения, о которых следует помнить. Если вы подчинены с узла кластера, не существует встроенного механизма для автоматического переключения этого ведомого на другой главный узел в вашем кластере. Вы не можете предполагать, что позиции двоичного журнала одинаковы на всех узлах кластера (даже если они начинают двоичное ведение журнала в одно и то же время), поэтому вы не можете выполнить команду CHANGE MASTER, не зная правильную позицию двоичного журнала для начала.

Холст 2

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

Galera Seqnos и бинарный журнал Xids

Учитывая вышеописанный сценарий, мы имеем кластер из 2 узлов с асинхронной репликацией узла 3 с узла 1. Пишет собирается на node2. Если я кратко остановлю запись и посмотрю двоичный журнал узла 1, я увижу кое-что интересное:

[root@node1 mysql]# mysqlbinlog node1.000004  | tail
MzkzODgyMjg3NDctMjQwNzE1NDU1MjEtNTkxOTQwMzk0MTYtMjU3MTQxNDkzMzI=
'/*!*/;
# at 442923
#130620 11:07:00 server id 2  end_log_pos 442950 	Xid = 86277
COMMIT/*!*/;
DELIMITER ;
# End of log file
ROLLBACK /* added by mysqlbinlog */;
/*!50003 SET COMPLETION_TYPE=@OLD_COMPLETION_TYPE*/;
/*!50530 SET @@SESSION.PSEUDO_SLAVE_MODE=0*/;

Обратите внимание на поле Xid. Galera модифицирует это поле, которое существует в стандартном бинлоге, с последним подтвержденным seqno:

[root@node1 ~]# mysql -e "show global status like 'wsrep_last_committed'";
+----------------------+-------+
| Variable_name        | Value |
+----------------------+-------+
| wsrep_last_committed | 86277 |
+----------------------+-------+

Если я проверю node2, то наверняка его последний binlog имеет тот же Xid, даже если он находится в другом файле в другой позиции:

[root@node2 mysql]# mysqlbinlog node2.000002  | tail
MzkzODgyMjg3NDctMjQwNzE1NDU1MjEtNTkxOTQwMzk0MTYtMjU3MTQxNDkzMzI=
'/*!*/;
# at 162077
#130620 11:07:00 server id 2  end_log_pos 162104 	Xid = 86277
COMMIT/*!*/;
DELIMITER ;
# End of log file
ROLLBACK /* added by mysqlbinlog */;
/*!50003 SET COMPLETION_TYPE=@OLD_COMPLETION_TYPE*/;
/*!50530 SET @@SESSION.PSEUDO_SLAVE_MODE=0*/;

Таким образом, поскольку оба узла выполняют log-bin (и оба с log-slave-updates), даже если они находятся в разных файлах журнала, они все равно имеют один и тот же Xid, который очень легко найти.

Но знает ли наш раб (узел 3) об этих Xids? Если мы проверим последний релейный журнал:

[root@node3 mysql]# mysqlbinlog node3-relay-bin.000002  | tail
MzkzODgyMjg3NDctMjQwNzE1NDU1MjEtNTkxOTQwMzk0MTYtMjU3MTQxNDkzMzI=
'/*!*/;
# at 442942
#130620 11:07:00 server id 2  end_log_pos 442950 	Xid = 86277
COMMIT/*!*/;
DELIMITER ;
# End of log file
ROLLBACK /* added by mysqlbinlog */;
/*!50003 SET COMPLETION_TYPE=@OLD_COMPLETION_TYPE*/;
/*!50530 SET @@SESSION.PSEUDO_SLAVE_MODE=0*/;

Да, в самом деле! Мы предполагаем, что поток SQL на этом ведомом устройстве обработал все в журнале ретрансляции, и, если это так, то мы знаем, что транзакция Galera seqno 86277 была последней записанной на ведомом устройстве. Если это не так, вам нужно будет найти последнюю позицию, примененную потоком SQL, используя SHOW SLAVE STATUS, и найти связанный Xid с этой позицией.

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

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

root@node2 mysql]# myq_status wsrep
mycluster / node2 / Galera 2.5(r150)
Wsrep    Cluster  Node     Queue   Ops     Bytes     Flow    Conflct PApply        Commit
    time P cnf  #  cmt sta  Up  Dn  Up  Dn   Up   Dn pau snt lcf bfa dst oooe oool wind
11:13:33 P  24  2 Sync T/T   0   0  2k  11 2.8M  258 0.0   0   0   0   9    0    0    1
11:13:34 P  24  2 Sync T/T   0   0  10   0  16K    0 0.0   0   0   0   9    0    0    1
11:13:35 P  24  2 Sync T/T   0   0  14   0  22K    0 0.0   0   0   0   8    0    0    1
11:13:36 P  24  2 Sync T/T   0   0  10   0  16K    0 0.0   0   0   0   8    0    0    1
11:13:37 P  24  2 Sync T/T   0   0   7   0  11K    0 0.0   0   0   0   8    0    0    1
11:13:38 P  24  2 Sync T/T   0   0   5   0 7.5K    0 0.0   0   0   0   8    0    0    1
11:13:39 P  24  2 Sync T/T   0   0   6   0 9.3K    0 0.0   0   0   0   8    0    0    1
11:13:40 P  24  2 Sync T/T   0   0  10   0  15K    0 0.0   0   0   0   8    0    0    1
11:13:41 P  24  2 Sync T/T   0   0   8   0  12K    0 0.0   0   0   0   8    0    0    1
11:13:42 P  24  2 Sync T/T   0   0   0   0    0    0 0.0   0   0   0   8    0    0    0
11:13:43 P  24  2 Sync T/T   0   0   0   0    0    0 0.0   0   0   0   8    0    0    0
11:13:44 P  24  2 Sync T/T   0   0   0   0    0    0 0.0   0   0   0   8    0    0    0
11:13:45 P  24  2 Sync T/T   0   0   0   0    0    0 0.0   0   0   0   8    0    0    0
11:13:47 P  24  2 Sync T/T   0   0   0   0    0    0 0.0   0   0   0   8    0    0    0
11:13:48 P  24  2 Sync T/T   0   0   0   0    0    0 0.0   0   0   0   8    0    0    0
mycluster / node2 / Galera 2.5(r150)
Wsrep    Cluster  Node     Queue   Ops     Bytes     Flow    Conflct PApply        Commit
    time P cnf  #  cmt sta  Up  Dn  Up  Dn   Up   Dn pau snt lcf bfa dst oooe oool wind
11:13:49 P  24  2 Sync T/T   0   0   0   0    0    0 0.0   0   0   0   8    0    0    0
11:13:50 P  24  2 Sync T/T   0   0   0   0    0    0 0.0   0   0   0   8    0    0    0
11:13:51 P  24  2 Sync T/T   0   0   0   0    0    0 0.0   0   0   0   8    0    0    0
11:13:52 P  24  2 Sync T/T   0   0   0   0    0    0 0.0   0   0   0   8    0    0    0
11:13:53 P  24  2 Sync T/T   0   0   0   0    0    0 0.0   0   0   0   8    0    0    0
11:13:54 P  24  2 Sync T/T   0   0   0   0    0    0 0.0   0   0   0   8    0    0    0
11:13:55 P  24  2 Sync T/T   0   0   0   0    0    0 0.0   0   0   0   8    0    0    0
11:13:56 P  24  2 Sync T/T   0   0   0   0    0    0 0.0   0   0   0   8    0    0    0
11:13:57 P  24  2 Sync T/T   0   0   0   0    0    0 0.0   0   0   0   8    0    0    0
11:13:58 N 615  1 Init F/T   0   0   0   2    0  234 0.0   0   0   0   8    0    0    0
11:13:59 N 615  1 Init F/T   0   0   0   0    0    0 0.0   0   0   0   8    0    0    0
11:14:00 N 615  1 Init F/T   0   0   0   0    0    0 0.0   0   0   0   8    0    0    0

0 0

Отказ узла занимает немного времени. Это потому, что это был кластер из 2 узлов, и мы просто потеряли кворум. Обратите внимание, что узел переходит в состояние Init и также не является основным. Это также случилось, чтобы убить нашего тестового клиента. Если мы проверим нашего ведомого, мы увидим, что он действительно отключен, так как узел 1 ушел:

node3 mysql> show slave status\G
*************************** 1. row ***************************
               Slave_IO_State: Reconnecting after a failed master event read
                  Master_Host: node1
                  Master_User: slave
                  Master_Port: 3306
                Connect_Retry: 60
              Master_Log_File: node1.000004
          Read_Master_Log_Pos: 3247513
               Relay_Log_File: node3-relay-bin.000002
                Relay_Log_Pos: 3247532
        Relay_Master_Log_File: node1.000004
             Slave_IO_Running: Connecting
            Slave_SQL_Running: Yes
...

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

[root@node2 mysql]# mysql -e 'set global wsrep_provider_options="pc.bootstrap=true"'
[root@node2 mysql]# myq_status wsrep
mycluster / node2 / Galera 2.5(r150)
Wsrep    Cluster  Node     Queue   Ops     Bytes     Flow    Conflct PApply        Commit
    time P cnf  #  cmt sta  Up  Dn  Up  Dn   Up   Dn pau snt lcf bfa dst oooe oool wind
11:16:46 P  25  1 Sync T/T   0   0  2k  17 3.0M 1021 0.0   0   0   0   5    0    0    1
11:16:47 P  25  1 Sync T/T   0   0  10   0  16K    0 0.0   0   0   0   5    0    0    1
11:16:48 P  25  1 Sync T/T   0   0  13   0  20K    0 0.0   0   0   0   5    0    0    1
11:16:49 P  25  1 Sync T/T   0   0  10   0  15K    0 0.0   0   0   0   5    0    0    1
11:16:50 P  25  1 Sync T/T   0   0   8   0  11K    0 0.0   0   0   0   5    0    0    1
11:16:51 P  25  1 Sync T/T   0   0  15   0  23K    0 0.0   0   0   0   5    0    0    1
11:16:52 P  25  1 Sync T/T   0   0  11   0  17K    0 0.0   0   0   0   6    0    0    1
11:16:53 P  25  1 Sync T/T   0   0  10   0  15K    0 0.0   0   0   0   6    0    0    1

Итак, наш кластер 1 восстановлен и принимает записи. Но как мы можем ИЗМЕНИТЬ МАСТЕР, чтобы узел 3 реплицировался с узла 1? Сначала нам нужно найти последний полученный Xid для узла 3:

[root@node3 mysql]# mysqlbinlog node3-relay-bin.000002 | tail
MDk5MjkxNDY3MTUtMDkwMzM3NTg2NjgtMTQ0NTI2MDQ5MDItNzgwOTA5MTcyNTc=
'/*!*/;
# at 3247505
#130620 11:13:41 server id 2  end_log_pos 3247513 	Xid = 88085
COMMIT/*!*/;
DELIMITER ;
# End of log file
ROLLBACK /* added by mysqlbinlog */;
/*!50003 SET COMPLETION_TYPE=@OLD_COMPLETION_TYPE*/;
/*!50530 SET @@SESSION.PSEUDO_SLAVE_MODE=0*/;

Из SHOW SLAVE STATUS мы можем сказать, что весь релейный журнал был обработан, поэтому мы знаем, что последний примененный Xid был 88085. Проверяя узел 2, мы действительно можем сказать, что кластер продвинулся дальше:

[root@node2 mysql]# mysql -e "show global status like 'wsrep_last_committed'"
+----------------------+-------+
| Variable_name        | Value |
+----------------------+-------+
| wsrep_last_committed | 88568 |
+----------------------+-------+

Легко найти последний Xid узла 3 в binlog узла 2 (если ведомое устройство какое-то время было отключено, вам, возможно, придется поискать немного больше в binlogs):

[root@node2 mysql]# mysqlbinlog node2.000002 | grep "Xid = 88085"
#130620 11:13:41 server id 2  end_log_pos 2973899 	Xid = 88085

Итак, на узле 2 Xid 88085 находится в двоичном лог-узле 2.000002, позиция 2973899. 2973899 — это «конечная» позиция, так что она должна быть там, где мы начинаем на ведомом устройстве. Вернувшись на узел 3, мы можем теперь выпустить правильный CHANGE MASTER:

node3 mysql> slave stop;
Query OK, 0 rows affected (0.01 sec)
node3 mysql> change master to master_host='node2', master_log_file='node2.000002', master_log_pos=2973899;
Query OK, 0 rows affected (0.02 sec)
node3 mysql> slave start;
Query OK, 0 rows affected (0.00 sec)
node3 mysql> show slave status\G
*************************** 1. row ***************************
               Slave_IO_State: Waiting for master to send event
                  Master_Host: node2
                  Master_User: slave
                  Master_Port: 3306
                Connect_Retry: 60
              Master_Log_File: node2.000002
          Read_Master_Log_Pos: 7072423
               Relay_Log_File: node3-relay-bin.000002
                Relay_Log_Pos: 4098705
        Relay_Master_Log_File: node2.000002
             Slave_IO_Running: Yes
            Slave_SQL_Running: Yes
....

Конечно, мы по-прежнему сканируем релейные и двоичные журналы, но сопоставить числа Xid намного проще, чем пытаться сопоставить полезные нагрузки RBR.

Резюме

  • Galera записывает seqno (часть его GTID) в двоичные журналы узла (если включено) в поле Xid
  • Xid одинаков для всех узлов кластера, независимо от позиции двоичного журнала
  • Это поле Xid распространяется на подчиненные и может быть сопоставлено в их журналах ретрансляции.
  • Те ведомые устройства, которые также ведут двоичную регистрацию с обновлениями log-slave, будут перезаписывать Xid, так что это не является истинным GTID для асинхронного в 5.5.