网上关于MySQL官方版本的GTID复制文章很多,我就不再赘述。我这里主要想写一点关于MariaDB的GTID复制特性。相比官方mysql的GTID复制,MariaDB更加的方便好用。
参考文档 https://mariadb.com/kb/en/mariadb/gtid/
MariaDB GTID组成
MariaDB的全局事物ID由下列三部分组成
”域识别符“-”服务器标识符“(server_id)-”事物识别符“
域识别符是MariaDB 10.0新引入的概念,可以将其理解为每个复制集群分配到唯一ID。域识别符主要是为了在引入多源复制后,把各事务在全局范围内区分开来。
服务器标识符就是复制中使用的server_id系统变量。最后的事物识别符是依次增1的8字节常数值。
MariaDB的GTID是以事务为单位进行分配的。当使用不支持事务的存储引擎时,GTID会以SQL语句为单位进行分配。
下面将详细说明一下GTID复制相关内容。
一、搭建GTID复制从库
从库导入主库的备份数据,目前mysql主要由以下两种方式进行备份:
1、mydumper逻辑备份(或者mysqldump逻辑备份)
可以到备份目录文件下获取备份完成时主库最后执行完事务的GTID.
more metadata
Started dump at: 2017-06-17 10:55:40
SHOW MASTER STATUS:
Log: mysql-bin.000056
Pos: 9637
GTID:0-64236-299476
GTID:0-64236-299476 就是后面从库导入备份数据后,从这个GTID开始进行复制的位置。
2、xtrabackup备份
more xtrabackup_binlog_info
mysql-bin.000104 95830984 0-691253306-60596652
0-691253306-60596652 是后面从库数据导入后进行复制的起始位置。
搭建步骤如下:
实验机器:10.24.64.236 主库,10.24.64.239 从库。
从库导入主库的备份数据(mydumper或者xtrabackup备份)后,执行以下命令。
a. set global gtid_slave_pos='0-64236-299476';
b. show variables like 'gtid%';确认gtid_slave_pos位置有效。
MariaDB [jira]> show variables like 'gtid%';
+------------------------+----------------+
| Variable_name | Value |
+------------------------+----------------+
| gtid_binlog_pos | |
| gtid_binlog_state | |
| gtid_current_pos | 0-64236-299476 |
| gtid_domain_id | 0 |
| gtid_ignore_duplicates | OFF |
| gtid_seq_no | 0 |
| gtid_slave_pos | 0-64236-299476 |
| gtid_strict_mode | OFF |
+------------------------+----------------+
c. change master to master_host='10.24.64.236',master_user='rep',master_password='xxx',master_use_gtid=slave_pos;
d.start slave; show slave status\G 确认复制搭建成功。
MariaDB [jira]> show slave status\G
*************************** 1. row ***************************
Slave_IO_State: Waiting for master to send event
Master_Host: 10.24.64.236
Master_User: rep
Master_Port: 3306
Connect_Retry: 60
Master_Log_File: mysql-bin.000056
Read_Master_Log_Pos: 9637
Relay_Log_File: relay-bin.000002
Relay_Log_Pos: 670
Relay_Master_Log_File: mysql-bin.000056
Slave_IO_Running: Yes
Slave_SQL_Running: Yes
...
Master_Server_Id: 64236
Master_SSL_Crl:
Master_SSL_Crlpath:
Using_Gtid: Slave_Pos
Gtid_IO_Pos: 0-64236-299476
二、GTID复制与传统复制之间的切换
1、GTID复制切换到传统复制
stop slave;
change master to master_use_gtid=no;
start slave;
shwo slave status\G
Using_Gtid: No 可以确认已经由GTID复制切换到传统复制。
MariaDB [test]> show slave status\G
*************************** 1. row ***************************
Slave_IO_State: Waiting for master to send event
Master_Host: 10.24.64.236
Master_User: rep
Master_Port: 3306
Connect_Retry: 60
Master_Log_File: mysql-bin.000056
Read_Master_Log_Pos: 9955
Relay_Log_File: relay-bin.000002
Relay_Log_Pos: 537
Relay_Master_Log_File: mysql-bin.000056
Slave_IO_Running: Yes
Slave_SQL_Running: Yes
..
Using_Gtid: No
Gtid_IO_Pos: 0-64236-299478
我们再在236主库任意插入一条数据,可以发现239从库的GTID也跟着在增长。并且和236主库的gtid_current_pos保持一致。
GTID由原来的 Gtid_IO_Pos: 0-64236-299478 增长到现在的 gtid_slave_pos | 0-64236-299479。
MariaDB [test]> show variables like 'gtid%';
+------------------------+----------------+
| Variable_name | Value |
+------------------------+----------------+
| gtid_binlog_pos | 0-64236-299479 |
| gtid_binlog_state | 0-64236-299479 |
| gtid_current_pos | 0-64236-299479 |
| gtid_domain_id | 0 |
| gtid_ignore_duplicates | OFF |
| gtid_seq_no | 0 |
| gtid_slave_pos | 0-64236-299479 |
| gtid_strict_mode | OFF |
+------------------------+----------------+
8 rows in set (0.00 sec)
2、传统复制切换到GTID复制
从库执行以下命令:
stop slave;
change master to master_use_gtid=slave_pos;或者执行 change master to master_use_gtid=current_pos
start slave;
show slave status\G 确认复制已经切换成功。
三、主从GTID复制结构中Master切换
A(M)-->B(S) 切换为 A(S)<--B(M) 结构
主从GTID复制结构中Master切换非常简单。
1、先确认从库B已经完全追上主库A
Master_Log_File: mysql-bin.000056 = Relay_Master_Log_File: mysql-bin.000056 && Read_Master_Log_Pos: 10390 = Exec_Master_Log_Pos: 10390
2、A 上执行
change master to master_host='B',master_port=,master_user='rep',master_password='xxx',master_use_gtid=current_pos;start slave;
这里把 master_use_gtid 配置成 current_pos。
因为该主库没有做过其他数据库的从库,所以slave_pos为空,需要用current_pos。二者区别可以看后面的定义。
3、B 上执行 stop slave;
一主多从结构的主库切换和一主一从切换类似,确认主从数据一致后把原来的从库直接change到新的主库上面就好。
四、在使用GTID的从服务器中跳过事务
场景:
在从库某表中删掉某条记录,然后在主库上执行同样的删除动作。
这时主库会报错如下:
Last_SQL_Error: Could not execute Delete_rows_v1 event on table test.tt; Can't find record in 'tt', Error_code: 1032;
handler error HA_ERR_KEY_NOT_FOUND; the event's master log mysql-bin.000056, end_log_pos 10508
从库找不到需要删除的记录,这时可以手动跳过这个错误。
方法1:
stop slave; set global gtid_slave_pos='0-64236-299482';start slave;
show slave status\G 确认主从复制恢复正常。
方法2:
stop slave;set global sql_slave_skip_counter=1;start slave;
show slave status\G 确认主从复制恢复正常。
五、GTID使用限制
1、slave的本地写入,需要注意,因为跟master不是同一个GTID范围,写入binlog的值,复制到后续从库,容易失败,需要使用
set sql_log_bin=0,来禁止binlog的写入。
生产环境所有从库已经设置为read_only(普通用户只有select权限,有all/super权限的都不在这个范围内)。如果因为特殊情况需要在从库写入数据,则先临时关闭binlog写入。
2、切换主库前,必须保证主库数据已经全部复制到将要作为新主库的从库。
六、常见复制错误处理
1、主库进行update,delete操作,从库发现没有相应记录,导致复制中断。
Last_SQL_Error: Could not execute Delete_rows_v1 event on table test.tt; Can't find record in 'tt', Error_code: 1032; handler error HA_ERR_KEY_NOT_FOUND; the event's master log mysql-bin.000056, end_log_pos 10508
处理方式:
根据复制报错的binlog位置,在主库上解析相应的binlog,确认从库具体因为哪些数据确失导致复制中断。
找出从库缺失的记录之后手动从主库导出,再导入从库。导入的时候,切记要先关闭binlog写入。
set sql_log_bin=0;导入数据;set sql_log_bin=1; 然后重启复制。
2、主库上执行 insert操作,从库已经有相应的记录,导致复制中断。
从库报错如下:
Last_SQL_Error: Could not execute Write_rows_v1 event on table test.tt; Duplicate entry '12' for key 'PRIMARY', Error_code: 1062; handler error HA_ERR_FOUND_DUPP_KEY; the event's master log mysql-bin.000056, end_log_pos 10907
处理方式:
根据报错信息可以知道从库已经存在ID=12的记录,因此复制再次插入同样主键ID记录时,报1062错误。
为了验证这一点,可以解析主库相应位置的binlog。
因此我们可以直接删除从库ID=12的记录,重启复制。
以上错误除了主库delete操作,从库因记录缺失报错可以跳过。其他错误都不能直接跳过,会导致主从数据不一致。
七、GTID有关的三个全局变量
select @@global.gtid_slave_pos, @@global.gtid_binlog_pos,@@global.gtid_current_pos;
gtid_slave_pos:
This variable is the GTID of the last event group replicated on a slave server, for each replication domain.
gtid_binlog_pos:
This variable is the GTID of the last event group written to the binary log, for each replication domain.
gtid_current_pos:
This variable is the GTID of the last change to the database for each replication domain. Such changes can either be master events (ie. local changes made by user or application), or replicated events originating from another master server.
总结
MariaDB10.0.2之后的GTID复制搭建与管理非常方便,整体比MySQL官方版本好用很多。主要有以下优势:
1、MariaDB GTID可以有间断,支持 set global sql_slave_skip_counter=1 跳过错误的语法。
MySQL的GTID是连续的,不支持直接跳过错误的语法,只能采取插入空事务来跳过相应的GTID.
2、MariaDB 复制结构中可以同时混杂传统复制从库和GTID复制从库。但是MySQL不行,必须所有数据库都开启GTID复制。
3、MariaDB GTID和传统复制间的切换是非常方便的无需其他配置。而MySQL就繁琐很多,无法做到平滑过渡,需要修改主库配置并重启。
MariaDB官方文档也推荐使用GTID复制。后续会琢磨一下GTID复制下的高可用工具,目标使MariaDB GTID复制在主库失败的情况下能自动切换,同时不丢失数据