26-27
26 | 备库为什么会延迟好几个小时?
主备复制的流程图在24 | MySQL是怎么保证主备一致的提过了。
备库通过sql_thread更新数据,5.6版本之前只支持单线程复制,所以主库并发高、TPS高会出现严重的主备延迟。
上图为改进的多线程复制模型,coordinator为原来的sql_thread,但不再直接更新数据,只负责中转日志和分发事务。worker数量由参数 slave_parallel_workers 决定(32核推荐配置8~16)。
coordinator分发需满足两个基本要求:
- 不能造成更新覆盖。这就要求更新同一行的两个事务,必须被分发到同一个 worker 中。
- 同一个同一个事务不能被拆开,必须放到同一个worker 中。
并行复制策略
- 按库分发,hash库名到一个worker 中,MySQL 5.6 版本的并行复制策略。
- 按表分发,需将相同表hash到一个worker 中。
- 按行分发,按“库名 + 表名 + 唯一索引 a 的名字 +a 的值”hash到一个worker 中。
MariaDB 利用了redo log 组提交 (group commit)特性,因为能在一组中提交,一定不会修改同一行。
MySQL 5.7 并行复制策略由参数 slave-parallel-type 来控制,配置成DATABASE使用5.6版本的策略,LOGICAL_CLOCK使用MariaDB 的策略,但进行了优化(针对两阶段提交)。
MySQL 5.7.22 新增了一个并行复制策略,基于 WRITESET 的并行复制。
27 | 主库出问题了,从库怎么办?
虚线箭头表示的是主备关系,也就是 A 和 A’互为主备, 从库 B、C、D 指向的是主库 A。
相比于一主一备,一主多从结构在切换完成后,A’会成为新的主库,从库 B、C、D 也要改接到 A’。
基于位点的主备切换
当我们把节点 B 设置成节点 A’的从库的时候,需要执行一条change master 命令:
CHANGE MASTER TO
//主库A’的信息
MASTER_HOST=$host_name
MASTER_PORT=$port
MASTER_USER=$user_name
MASTER_PASSWORD=$password
//同步位点
MASTER_LOG_FILE=$master_log_name
MASTER_LOG_POS=$master_log_pos
最后两个参数表示要从主库的 master_log_name 文件的 master_log_name 文件的 master_log_pos 这个位置的日志继续同步。而这个位置就是我们所说的同步位点,也就是主库对应的文件名和日志偏移量。 同步位点很难取到精确位置,因为不能丢数据,需要需要找一个“稍微靠前”的位点,然后判断跳过已经执行过的事务。
- 等待新主库 A’把中转日志(relay log)全部同步完成;
- 在 A’上执行 show master status 命令,得到当前 A’上最新的 File 和 Position;
- 取原主库 A 故障的时刻 T;
- 用 mysqlbinlog 工具解析 A’的 File,得到 T 时刻的位点。
mysqlbinlog File --stop-datetime=T --start-datetime=T
假设在 T 这个时刻,主库 A 已经执行完成了一个 insert 语句插入了一行数据 R,并且已经将 binlog 传给了 A’和 B,然后在传完的瞬间主库 A 的主机就掉电了。这种情况下,再从这个位点同步时,会把已经存在在B的数据再次同步过来,出现主键冲突。
主动跳过事务:
set global sql_slave_skip_counter=1;
start slave;
跳过设置 slave_skip_errors 参数,直接设置跳过指定错误。“1032”删除找不到行,“1062”主键冲突。同步完成后,稳定一段时间,去掉该设置。
GTID
MySQL 5.6 版本引入了 GTID。 GTID启动加上参数 gtid_mode=on 和 enforce_gtid_consistency=on。 GTID 的全称是 Global Transaction Identifier,也就是全局事务 ID,是一个事务在提交的时候生成的,是这个事务的唯一标识。它由两部分组成,格式是:
//server_uuid实例第一次启动时生成,全局唯一
//gno一个整数,初始值是 1,每次提交事务的时候分配给这个事务,并+1
//mysql文档中叫GTID=source_id:transaction_id
GTID=server_uuid:gno
如果从库中已经存在了某事务,使用以下方式跳过。前三句执行了一个空事务,并把GTID加到了从库的集合中。
set gtid_next='aaaaaaaa-cccc-dddd-eeee-ffffffffffff:10';
begin;
commit;
set gtid_next=automatic;
start slave;
基于 GTID 的主备切换
CHANGE MASTER TO
MASTER_HOST=$host_name
MASTER_PORT=$port
MASTER_USER=$user_name
MASTER_PASSWORD=$password
//使用GTID协议
master_auto_position=1
从库会把自己的GTID集合传给切换的主库,主库会计算差集,然后把不同的同步给从库。