要知道,Mysql 的主从使用的是 binlog 那样简单的 日志传输方式,来完成从库对主库的复制,虽然提高了效率,但是主库和从库之间并没有 raft 那样的协议来保证 主从一致。
有时候主库宕机,但是 binlog 还没有发出去,如果直接将从库切换为主库,那么将会主备不一致。
并且从库是单纯告诉主库,需要从主库的 binlog 的哪个偏移量开始 ,让主库发送 binlog 过来,所以很有可能因为偏移量不正确,导致从库得到的 binlog 是以前获得过的,导致重复的插入或者删除,使得从库因为错误终止运行。
Mysql 5.6 引入了 GTID ,启动这个模式:启动参数 + gtid_mode=on和enforce_gtid_consistency=on
Global Transaction ID , 可以唯一表示 一个集群中的事务(前提是这些集群中的机器的id 要相互不同),格式是 server_id : gno (数据库实例 id : 事务 id)
前者用来标识机器,后者用来在一台机器中唯一标识事务。
数据库会维护 一个 GTID 的集合,标识所有该数据库实例执行过的事务
于是有一种新的模式,从库把自己的 GTID 集合发送给 主库,主库检查 从库的 GTID 集合 和 自己 GTID 集合的差集,这个差集就是 主库需要给从库的 数据。
倘若从库要求的 GTID 集合在 主库上没有,那么可能是 主库删除了对应的 binlog,主库会报错(但是如果是从库自己在 成为从库前 执行了一些事务,这些事务的 GTID 也会被发给主库吗?如果发的话不就必然报错?)(其实可以 在从库上使用命令 : set global gtid_purged = '24024e52-bd95-11e4-9c6d-926853670d0b:1 - N'; 来告诉从库,哪些binlog 已经被 purge 了,不要再发给主库了,不要再向主库索要了 ,注意上面的 gno 是 1 - N 这种格式 ,只有事务提交了,才能使 gno + 1)
一般的 gtid 是递增的,gtid_next=automatic 可以设置递增。
也可以自己设置 set gtid_next='aaaaaaaa-cccc-dddd-eeee-ffffffffffff:10';
这样的话,被设置的机器 若执行下一个事务,这个事务 使用的 gtid 就是上面手工设置的 gtid。
假如有 互为 主从的 两个库 A B ,现在的写请求都是打到 A 上的,假如要加索引,又不想影响A 上的效率
1.先 停掉 B 向 A 发送 binlog
2.在 从库 B 上,加索引,假设这条 加索引的 DDL 的 GID 是 B:X
3.在 主库 A 上,设置 gid_next = A:X , 并且执行一个空事务(在设置 gid_next = A:X 到执行空事务期间,可不可能有其他的事务抢占,哦,好像就算被抢了也无所谓,反正我们要做的只是让这个gid 对应的 DDL 不被执行)
4. A 重新认 B 为 主库,会发送 A 的 GID 集合(这期间没有停止 A 向 B 发送 binlog,所以 B 的 gid 集合应该和 A 一致,所以不会报错),A 从 B 那里获取 B 的 gid 集合,因为之前已经执行过那条 DDL 语句对应事务的 gtid 代表的假事务,所以不会执行 这条 DDL。
把写请求打到 库 B 上,并且再来一次刚才的过程,只不过这次反过来,B 是之前的 A ,A 是之前的 B。
5. 最后 A 和 B 上 都有了 索引。