大多数读多写少,解决读性能,一主多从切换正确性
虚线主备A 和 A’, 从库 B、C、D 。读写分离,主库:写入、部分读,从库:其他读
A’新的主库,B、C、D 改接 A’(多了重指,复杂增加)
一、基于位点的主备切换
B 设置 A’从库change master 命令:
主库 A’的 IP、端口、用户名和密码
MASTER_LOG_FILE 和 MASTER_LOG_POS 从主库master_log_name 文件的 master_log_pos 位置日志继续同步(同步位点,主库对应文件名和日志偏移量)
相同日志,A 和 A’的位点不同。
1.1 B “找同步位点”(只能取大概)
不能丢数据,找“稍微往前”,跳过 B 上已执行:
1. A’中转日志(relay log)同步完成;执行 show master status ,得A’ File 和 Position
2. 取A 故障时刻 T
3. 解析 A’的 File得T 位点(用 mysqlbinlog 工具)
mysqlbinlog File --stop-datetime=T --start-datetime=T
end_log_pos 后“123”,A’ T 时刻写入新 binlog 的位置。$master_log_pos =123 ,用节点 B change master 命令里
1.2同步位点不精确
T 时,A insert 数据 R,将 binlog 传给 A’和 B,传完 A 掉电状态:
1. B 同步binlog, R 存在;
2. A’上R 存在,123后
3. B 上 change master 命令,指向 A’的 File 文件的 123 位置,就会把插入 R 这一行数据的 binlog 又同步到从库 B 去执行。
B Duplicate entry ‘id_of_R’ for key ‘PRIMARY’ 错误,主键冲突,停止同步。
1.3切换任务,主动跳错误方法
(1)跳过命令:set global sql_slave_skip_counter=1; start slave;
B 接 A’时,碰到错误就停,执行命令,直到没有
(2)设置slave_skip_errors 跳过指定错误
1062 唯一键冲突;1032 删除找不到行。
slave_skip_errors 设置为 “1032,1062”(无损),直接跳过。
稳定后,设置为空,以免不一致,也跳过
二、GTID( 找同步位点 )
全称Global Transaction Identifier 全局事务 ID(MySQL 5.6 版本引入 ),提交时生成唯一标识。两部分组成,格式:GTID=server_uuid : gno
server_uuid 第一次启动自动生成,全局唯一;
gno 整数,初始值1,每次提交分配加 1
MySQL 官方文档:GTID=source_id:transaction_id;用transaction_id 容易造成误解:transaction_id 就是指事务 id,执行中分配,gno 提交时分配。
GTID 启动模简单,加 gtid_mode=on和 enforce_gtid_consistency=on 每个事务跟GTID 一一对应。
2.1两种生成方式
用哪取决于 session 变量 gtid_next值。
1. gtid_next=automatic,默认值。把server_uuid:gno 分配给这个事务。
a.记录 binlog 时先记录SET@@SESSION.GTID_NEXT=‘server_uuid:gno’;
b. GTID 加入本实例GTID 集合。
2. gtid_next 指定 GTID 值, setgtid_next='current_gtid’两种可能:
a.如果 current_gtid 已存在 GTID 集合,系统忽略;
b. 没在,current_gtid分配要执行事务,不需给事务生成新GTID,gno 不加 1。
2.2冲突解决
事务BEGIN 前有SET@@SESSION.GTID_NEXT 。如实例 X 有从库,将 CREATE TABLE和 insert 语句的 binlog 同步执行,之前就会先执行这两个 SET 命令, 加入从库的 GTID 集合的,图中的这两个 GTID。
X 是 Y 从库, Y 上执行:insert into t values(1,1); Y 上GTID “aaaaaaaa-cccc-dddd-eeee-ffffffffffff:10”。
同步执行,主键冲突,执行:
set gtid_next='aaaaaaaa-cccc-dddd-eeee-ffffffffffff:10';
begin;
commit;//前三条语句,提交空事务,把GTID 加到X GTID 集合中
set gtid_next=automatic; //新事按照原来分配方式gno=3
start slave;
已加入 GTID。再执行 start slave 同步线程, X 上执行Y 传来事务,“aaaaaaaa-cccc-dddd-eeee-ffffffffffff:10”已经存在 X,直接跳过,不冲突
三、基于 GTID 主备切换
master_auto_position=1用是 GTID 协议。MASTER_LOG_FILE 和 MASTER_LOG_POS 不需要指定。
A’的 GTID 集合为 set_a, B 为 set_b。主备切换逻辑(B 上 start slave 取 binlog ):
1. B 指定主库 A’,基于主备协议建立连接。
2. B 把 set_b 发给主库 A’
3. A’算出 set_a 与 set_b 的差集(在于 set_a,不在 set_b GTID 集合),判断 A’是否包含差集需 binlog
a.不包含,A’把 B 要binlog 删了,返回错误(不发);
b.包含,从 binlog 里发给 B(后面一个个发);
基于位点主备协议不同:基于位点协议,备库决定,指定位点,主库发,不做日志完整性判断。
一主多从实现
不需找位点(A’内部完成),B、C、D 分别执行 change master 指向 A’。
A’自己生成 binlog 中 GTID 集合:server_uuid_of_A’:1-M
B GTID 集合 server_uuid_of_A:1-N,
GTID 集合:server_uuid_of_A:1-N, server_uuid_of_A’:1-M。
A’是 A 备库, A’和 B 的 GTID 一样达到预期。
四、GTID 和在线DDL
22 篇文章《MySQL 有哪些“饮鸩止渴”提高性能的方法?》中,高峰期慢查询性能,分析到在线加索引来。先在备库加索引,再切换。
双 M 结构下,备库 DDL 传给主库,避免影响,set sql_log_bin=off 关掉 binlog
加了索引,binlog没记录,导致数据和日志不一致?
主库是 X,X,Y都开GTID 模式。主备切换:
X stop slave。Y DDL 语句(不关 binlog)。
查DDL 对应GTID,并记erver_uuid_of_Y:gno。
X :set GTID_NEXT="server_uuid_of_Y:gno";
begin;
commit;
set gtid_next=automatic;
start slave;
Y 有 binlog 记录,确保 X 上不执行更新。执行完主备切换,再执行一遍
小结
从库找新主库位点是痛点。GTID 一主多从切换方便(建议)
思考题
GTID 从库 start slave,主库发现要的 binlog被删,主备创建不成功。怎么处理呢?
1. 业务允许不一致,主库show global variables like ‘gtid_purged’,得到主库已经删除GTID 集合,假设是 gtid_purged1;从库 reset master,再 set global gtid_purged =‘gtid_purged1’;最后start slave,从主库 binlog 开始同步。binlog 缺失那部分,从库可能丢失,主从不一致。
2. 主从一致,重新搭建。
3. 其他从库保留全量 binlog ,接从库,追上接回主库。
4. binlog 备份,从库应用缺失binlog,再start slave。
评论1
级联复制A->B->C结构下, 从库C的Seconds_Behind_Master时间计算问题.
主库A DDL,耗时1分钟.从库C的SBM最大多少?
3分钟. 算的是:当前执行时间,跟*日志时间*(A上执行出来)的差距
评论2
快速定位 binlog 里 GTID 位置?
binlog文件开头,Previous_gtids, 记录 “生成这个binlog时,Executed_gtid_set”, 启动时候只解析最后一个文件;
评论3
sql_slave_skip_counter=1跳过1个event,除非事务是有单个event组成,才会跳过一个事务。
评论4
主从备份问题太多:
1. binlog传输前,主库宕机,数据丢失。
2. 一主多从,半同步,只能保证binlog至少在两台机器上,没能选最完整作为新主库。
semi-sync(性能影响,现默认)还是留异步复制(容易数据丢失,丢一两行,可以应用层日志补)