Статьи

Репликация на основе строк, обновления MySQL 5.6 и временные типы данных

Эта статья была первоначально написана  Джеем Янссеном для блога MySQL Performance .

Куда твой план отката?

Обновления MySQL 5.6 идут полным ходом, и важно знать, как безопасно перейти с MySQL 5.5 на 5.6. При обновлении среды репликации важно создать план миграции, который позволяет безопасно выполнить обновление с минимальным риском — откат часто является очень важным компонентом для этого.

Для многих людей это означает модернизацию рабов, а затем и хозяина. Стратегия репликации старого мастера на новый ведомый хорошо известна и очень долго  поддерживается в репликации MySQL  . Если быть точным: у вас может быть MySQL 5.6 slave-сервера master 5.5, и это должно работать нормально, пока вы не обновите ваш master и / или не повысите статус одного из slave до master.

Тем не менее, есть те из нас, кто любит жить на грани и делать неподдерживаемые вещи. Предположим, что при переходе на этот мастер MySQL 5.6 ваше приложение полностью ломается. Каким будет ваш план отката? В таком случае было бы полезно оставить подчиненный 5.5 нового мастера 5.6 (или, возможно, настройку двойного мастера с 5.5 и 5.6), чтобы можно было выполнить откат, но все еще иметь данные, записанные на мастер 5.6.

Что может сломаться?

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

Теперь одна приятная новая особенность MySQL 5.6 — это улучшение требований к хранилищу для полей DATETIME,  а также добавление поддержки доли секунды для TIME, DATETIME и TIMESTAMP. Это замечательно, но, к сожалению, это новый формат столбцов, который 5.5 явно не поймет. Это ставит под угрозу нашу репликацию от 5,6 до 5,5? Ответ: если мы будем осторожны, НЕТ.

Проще говоря, MySQL 5.6 поддерживает как старые, так и новые типы, а mysql_upgrade не выполняет такое преобразование в существующих таблицах. Только НОВЫЕ таблицы или таблицы REBUILT в 5.6 будут использовать новый формат. Любые таблицы от 5.5 с простым mysql_upgrade до 5.6 будут по-прежнему использовать старые типы. Для получения дополнительной информации о том, как найти столбцы в 5.6, которые используют старый формат, см . Отличный пост в блоге Айка Уокера на эту тему . (Спасибо Айк!)

Несовершенный тест

Чтобы проверить это, я создал простой эксперимент. У меня есть ведущий и ведомый, использующие RBR, оба на 5.5, и я устанавливаю pt-heartbeat для обновления мастера. Я понял, что pt-heartbeat на самом деле использует varchar для поля метки времени — я подозреваю, что это облегчает поддержку нескольких баз данных. Однако, поскольку обновление pt-heartbeat использует NOW () для заполнения этого поля, я могу преобразовать его в DATETIME:

[root@master ~]# pt-heartbeat --update  --database percona --create-table
CREATE TABLE `heartbeat` (
  `ts` varchar(26) NOT NULL,
  `server_id` int(10) unsigned NOT NULL,
  `file` varchar(255) DEFAULT NULL,
  `position` bigint(20) unsigned DEFAULT NULL,
  `relay_master_log_file` varchar(255) DEFAULT NULL,
  `exec_master_log_pos` bigint(20) unsigned DEFAULT NULL,
  PRIMARY KEY (`server_id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1
 
master mysql> alter table heartbeat drop column ts, add column ts DATETIME;
 
slave mysql> select * from heartbeat\G
*************************** 1. row ***************************
             server_id: 1
                  file: master-bin.000002
              position: 5107583
relay_master_log_file: NULL
   exec_master_log_pos: NULL
                    ts: 2014-05-02 17:03:59
1 row in set (0.00 sec)
 
CREATE TABLE `heartbeat` (
   `server_id` int(10) unsigned NOT NULL,
   `file` varchar(255) DEFAULT NULL,
   `position` bigint(20) unsigned DEFAULT NULL,
   `relay_master_log_file` varchar(255) DEFAULT NULL,
   `exec_master_log_pos` bigint(20) unsigned DEFAULT NULL,
   `ts` datetime DEFAULT NULL,
   PRIMARY KEY (`server_id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 

Таким образом, моя таблица сердцебиения теперь имеет 5.5 DATETIME, pt-heartbeat работает правильно, и сердцебиение воспроизводится на подчиненном устройстве. Теперь я обновлю свой мастер до MySQL 5.6:

[root@master ~]# rpm -e Percona-Server-devel-55-5.5.36-rel34.2.el6.x86_64 Percona-Server-shared-55-5.5.36-rel34.2.el6.x86_64 Percona-Server-client-55-5.5.36-rel34.2.el6.x86_64 Percona-Server-server-55-5.5.36-rel34.2.el6.x86_64 --nodeps
[root@master ~]# yum install Percona-Server-server-56.x86_64
==============================================================================================================
Package                             Arch              Version                       Repository          Size
==============================================================================================================
Installing:
Percona-Server-server-56            x86_64            5.6.16-rel64.2.el6            Percona             19 M
Installing for dependencies:
Percona-Server-client-56            x86_64            5.6.16-rel64.2.el6            Percona            6.8 M
Percona-Server-shared-56            x86_64            5.6.16-rel64.2.el6            Percona            712 k
 
Transaction Summary
==============================================================================================================
Install       3 Package(s)
...
 
[root@master ~]# service mysql start
Starting MySQL (Percona Server)....... SUCCESS!
[root@master ~]# mysql
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 1
Server version: 5.6.16-64.2-56-log Percona Server (GPL), Release 64.2, Revision 569
 
[root@master ~]# mysql_upgrade
Looking for 'mysql' as: mysql
Looking for 'mysqlcheck' as: mysqlcheck
Running 'mysqlcheck with default connection arguments
Running 'mysqlcheck with default connection arguments
mysql.columns_priv                                 OK
mysql.db                                           OK
mysql.event                                        OK
mysql.func                                         OK
mysql.general_log                                  OK
mysql.help_category                                OK
mysql.help_keyword                                 OK
mysql.help_relation                                OK
mysql.help_topic                                   OK
mysql.host                                         OK
mysql.ndb_binlog_index                             OK
mysql.plugin                                       OK
mysql.proc                                         OK
mysql.procs_priv                                   OK
mysql.proxies_priv                                 OK
mysql.servers                                      OK
mysql.slow_log                                     OK
mysql.tables_priv                                  OK
mysql.time_zone                                    OK
mysql.time_zone_leap_second                        OK
mysql.time_zone_name                               OK
mysql.time_zone_transition                         OK
mysql.time_zone_transition_type                    OK
mysql.user                                         OK
Running 'mysql_fix_privilege_tables'...
Running 'mysqlcheck with default connection arguments
Running 'mysqlcheck with default connection arguments
percona.heartbeat                                  OK
OK

Теперь я могу убедиться, что запросы IKE INFORMATION_SCHEMA правильно определяют столбец «heartbeat.ts» как старый формат:

 master mysql> select t.table_schema,t.engine,t.table_name,c.column_name,c.column_type
from information_schema.tables t
  inner join information_schema.columns c on c.table_schema = t.table_schema and c.table_name = t.table_name
  left outer join information_schema.innodb_sys_tables ist on ist.name = concat(t.table_schema,'/',t.table_name)
  left outer join information_schema.innodb_sys_columns isc on isc.table_id = ist.table_id and isc.name = c.column_name
where c.column_type in ('time','timestamp','datetime')
  and t.table_schema not in ('mysql','information_schema','performance_schema')
  and t.table_type = 'base table'
  and (t.engine != 'innodb' or (t.engine = 'innodb' and isc.mtype = 6))
order by t.table_schema,t.table_name,c.column_name;
 
+--------------+--------+------------+-------------+-------------+
| table_schema | engine | table_name | column_name | column_type |
+--------------+--------+------------+-------------+-------------+
| percona      | InnoDB | heartbeat  | ts          | datetime    |
+--------------+--------+------------+-------------+-------------+
1 row in set (0.04 sec)

Чтобы репликация работала с MySQL 5.6 до 5.5, мне также пришлось добавить несколько опций обратной совместимости на мастер:

log_bin_use_v1_row_events = ON
binlog_checksum = NONE

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

slave mysql> show slave status\G
*************************** 1. row ***************************
               Slave_IO_State: Waiting for master to send event
                  Master_Host: 192.168.70.2
                  Master_User: repl
                  Master_Port: 3306
                Connect_Retry: 60
              Master_Log_File: master-bin.000005
          Read_Master_Log_Pos: 120
               Relay_Log_File: slave-relay-bin.000002
                Relay_Log_Pos: 267
        Relay_Master_Log_File: master-bin.000005
             Slave_IO_Running: Yes
            Slave_SQL_Running: Yes
 
master mysql> select * from heartbeat;
+-----------+-------------------+----------+-----------------------+---------------------+---------------------+
| server_id | file              | position | relay_master_log_file | exec_master_log_pos | ts                  |
+-----------+-------------------+----------+-----------------------+---------------------+---------------------+
|         1 | master-bin.000002 |  5115935 | NULL                  |                NULL | 2014-05-02 17:04:23 |
+-----------+-------------------+----------+-----------------------+---------------------+---------------------+
1 row in set (0.01 sec)
 
slave mysql> select * from heartbeat;
+-----------+-------------------+----------+-----------------------+---------------------+---------------------+
| server_id | file              | position | relay_master_log_file | exec_master_log_pos | ts                  |
+-----------+-------------------+----------+-----------------------+---------------------+---------------------+
|         1 | master-bin.000002 |  5115935 | NULL                  |                NULL | 2014-05-02 17:04:23 |
+-----------+-------------------+----------+-----------------------+---------------------+---------------------+
1 row in set (0.00 sec)

Но, если я не буду осторожен с MySQL 5.6 и пересоберу таблицу, новый формат явно укусит меня:

master mysql> set sql_log_bin=0;
Query OK, 0 rows affected (0.00 sec)
 
master mysql> alter table percona.heartbeat force;
Query OK, 1 row affected, 1 warning (0.18 sec)
Records: 1  Duplicates: 0  Warnings: 1
 
master mysql> show warnings;
+-------+------+-------------------------------------------------------------------------------------+
| Level | Code | Message                                                                             |
+-------+------+-------------------------------------------------------------------------------------+
| Note  | 1880 | TIME/TIMESTAMP/DATETIME columns of old format have been upgraded to the new format. |
+-------+------+-------------------------------------------------------------------------------------+
1 row in set (0.00 sec)
 
slave mysql> show slave status\G
*************************** 1. row ***************************
...
             Slave_IO_Running: Yes
            Slave_SQL_Running: No
...
                   Last_Errno: 1677
                   Last_Error: Column 5 of table 'percona.heartbeat' cannot be converted from type '' to type 'datetime'
...
               Last_SQL_Errno: 1677
               Last_SQL_Error: Column 5 of table 'percona.heartbeat' cannot be converted from type '' to type 'datetime'
  Replicate_Ignore_Server_Ids:
             Master_Server_Id: 1
1 row in set (0.00 sec)

TL; DR

Чему все это нас учит?

Хотя версия MySQL важна, для RBR важнее всего актуальный текущий формат каждого столбца. Ваш ведущий и ведомый (ие) ДОЛЖНЫ иметь одинаковые форматы столбцов для правильной работы RBR

Таким образом, новые временные форматы не обязательно нарушают репликацию RBR до 5.5 при условии:

  • Все базовые улучшения репликации MySQL 5.6 отключены (GTID, контрольные суммы binlog и формат RBR v2)
  • Таблицы с временными форматами сохраняются в своих форматах 5.5, пока все узлы 5.5 не будут удалены.
  • Вы можете избежать создания каких-либо новых таблиц на мастере MySQL 5.6 с временными форматами

Однако я хочу прояснить, что репликация MySQL 5.6 — 5.5 технически не поддерживается. Я не исчерпал все возможности для проблем с репликацией RBR от 5,6 до 5,5, только этот конкретный. Если вы решите создать стратегию обновления, основанную на обратной репликации, будьте готовы к тому, что она не будет работать, и тщательно протестируйте ее заранее. Цель этого поста — просто указать, что форматы типов данных сами по себе не обязательно нарушают обратную совместимость RBR.