mysql中gtid关闭方法_mysql怎么关闭gtid模式

2016-08-21 回答

mysql 5.6 全局事务 id(gtid)实现原理(三)这是 mysql 5.6 全局事务 id(gtid) 系列的第三篇博客。

在之前的两篇博客中,第一篇? 介绍了全局事务 id 的定义与数据结构。第二篇? 介绍了 mysql 5.6 新增的全局事务状态(gtid_state)。

这里准备介绍的是全局事务 id 如何参与 mysql 的主备复制流程。

mysql 5.6 引入全局事务 id 的首要目的,是保证 slave 在复制的时候不会重复执行相同的事务操作;其次,是用全局事务 ids 代替由文件名和物理偏移量组成的复制位点,定位 slave 需要复制的 binlog 内容。

因此,mysql 必须在写 binlog 时记录每个事务的全局 gtid,保证 master / slave 可以根据这些 gtid 忽略或者执行相应的事务。在实现上,mysql 没有修改旧的 binlog 事件,而是新增了两类事件:

+----------------------------+----------------------------------------+| 名称 | 功能 |+----------------------------+----------------------------------------+| previous_gtids_log_event | 该事件之前的全局事务 id 集合。 |+----------------------------+----------------------------------------+| gtid_log_event | 标记之后的事务对应的全局事务 id。 |+----------------------------+----------------------------------------+gtid_log_event

在 mysql 5.6 的 binlog 文件中,每个事务的开始不是 "begin" ,而是 gtid_log_event 事件:

(图片来源:mysql_innovation_day_replication_ha.pdf?)它里面只包含一条 gtid,记录结构如下:

gtid_log_event := (commit_flag, sid, gno) // commit_flag 目前总是 true里面 sid 就是产生该事务的 server_uuid,gno 是顺序编号的 transaction_id。

把 gtid 记录在事务的开头是为了便于 mysql 过滤 binlog:检查到某个 gtid 不需要时,可以直接忽略后面的整段事务。

mysql 5.6 保证同时写入 gtid_log_event 和全局 logged_gtids 状态:

第一步,在向 binlog_cache_data 写入第一条 binlog 前,mysql 会在缓存的 buffer 中写入一个空的 gtid_log_event 占位。

第二步,当 binlog_cache_data 的内容刷到 binlog 文件时,mysql 会把位于缓存 buffer 的 gtid_log_event 内容替换成实际的 gtid,重新写入缓存。

最后,mysql 调用 gtid_state 的 update_on_flush() 把 gtid 写入 logged_gtids,再调用 sync_binlog_file() 保证内容更新到磁盘。

在主备复制中,slave 不像 master 那样自动产生 gtid,而是直接拷贝 gtid_log_event 中包含的 gtid。这个特性是这样实现的 —— mysql 5.6 维护了一个线程(session)级别的变量 gtid_next,类型为 gtid_specification:

gtid_specification := (enum_group_type, gtid)enum_group_type :=enum(automatic_group, gtid_group, anonymous_group, invalid_group, undefined_group)在 master 执行事务时,gtid_next 的类型默认是 automatic_group,表示应该调用 generate_automatic_gno() 自动产生全局事务 id。

而在 slave 执行事务时,先用 gtid_log_event 内的 gtid 覆盖 gtid_next,使它的类型为 gtid_group。这样的话,mysql 会使用 gtid_next 内设置的 gtid 值作为下一个全局事务 id。

previous_gtids_log_event

这个事件出现在 mysql 5.6 每个 binlog 文件的开始处。

mysql 创建一个新的 binlog 文件后,首先写入一个 format_description_log_event 描述,接着写入一个 previous_gtids_log_event,内容是在创建这个 binlog 文件之前执行的全局事务 gtids。

事件的格式很简单,就是字符串编码的 gdit_set:(编码格式参考本文 第一篇?)previous_gtids_log_event := buffer of gtid_set(例如:3e11fa47-71ca-11e1-9e33-c80aa9429562:1-5)这个事件只是作为记录。在主备复制时,slave 会忽略 binlog 里的 previous_gtids_log_event 事件。

binlog 与持久化全局事务状态

在 上一篇? 没有讲到 mysql 5.6 如何持久化全局事务状态的 —— logged_gtids 和 lost_gtids 状态里存储了这台数据库 有史以来 执行的所有 gtids(包括删除 binlog 中的 gtids)—— 如果数据库停机或崩溃前不做持久化,之后肯定丢失信息。

mysql 的解决方案很简单,在启动时扫描剩余的 binlog 文件,用文件存储的 previous_gtids_log_event 和 gtid_log_event 事件内容恢复全局 logged_gtids 和 lost_gtids 状态。

具体的代码如下:(源代码:mysql-5.6.9-rc\sql\binlog.cc,line 2558)第一步,找到最后一个 binlog 文件,读出 previous_gtids_log_event 记录;再遍历 binlog 文件中所有的 gtid_log_event, 把找到的 gtid 记录合并起来,作为这台数据库历史上执行的所有 gtids 放入全局 logged_gtids 记录;第二步,找到第一个 binlog 文件,用它的 previous_gtids_log_event 信息代替全局 lost_gtids 的内容。因为这是第一个未删除的 binlog 文件,这里记录的就是之前已经删除的 binlog 文件所包含的全部 gtids。

由于 mysql 在提交事务中是最后才写入真实 gtid_log_event 信息的,从 binlog 恢复信息,可以保证读到的 gtids 与成功执行的事务一致。

change master to ...

mysql 5.6 主备复制的一个改变,是新增了 com_binlog_dump_gtid 协议,支持在 slave 切换到新 master 时,用 master_auto_position = 1 (auto_position 方式)代替原来的 binlog 文件名和物理偏移量。

com_binlog_dump_gtid 协议并不复杂,请求格式如下:

request = { server_id, binlog_name, binlog_offset, gtids_executed }

如果采用 auto_position 方式连接 master,现在 slave 发送的 binlog_name 和 binlog_offset 都是空白,master 只使用 gtids_executed 定位 slave 上需要执行的 binlog。

实现逻辑是这样的:master 从第一个文件开始读取 binlog,逐个检查 gdit_log_event 事件的全局事务 id 是不是包含在 slave 发送的 gtids_executed 集合中。如果发现这个 gtid 已经包含在 gtids_executed 集合内,就忽略后面的整段事务,不向 slave 发送 binlog 内容。

其实这个过程还不是很优化,因为如果是正常情况,master 需要遍历若干 g 的 binlog 才能找到 slave 需要复制的 binlog 内容 —— 这应该是一个改进点。

全局事务 id 与并发复制

mysql 5.6 主备复制的另一个改变,是实现了多线程并行复制。这个功能必须有全局事务 id 的支持,原因是:

1) 在并行复制方式下,有些操作是不按照记录在 binlog 中的顺序执行的。这样的话,如果按照文件名 + 物理偏移量的方式记录复制位点,则停止 / 恢复主备复制时,可能会有一些操作被重复执行。

2) 我们知道,即使是 mixed / row 模式下记录的 binlog,仍有些 ddl 操作是用 statement 的方式编码的,这些 ddl 操作不能在 slave 重复执行(因为非幂等)。一旦操作在 slave 执行出错,结果就是复制中断。

因此,slave 必须依赖 binlog 中的全局事务 id,在停止 / 恢复主备复制时,精确的记录哪些事务在 slave 执行过,哪些没有。

现在,mysql 5.6 可以用 com_binlog_dump_gtid 来保证这一点:在恢复主备复制时,slave 向 master 发送自己所有执行过的 gtids(logged_gtids),在上次中断主备复制时,已经执行过的 binlog 被 master 直接滤掉,不向 slave 传送。

总结

在主备复制上,mysql 5.6 新增了三个特性:

1)使用 gtids 作为主备复制的位点,在写 binlog 时用 gtid_log_event 标记事务。

2)支持 auto_position 方式进行主备切换。在新增的协议中,使用 gtids 作为复制位点向主库请求 binlog 信息。

3)多线程并发复制,使用 gtids 防止事务重复执行。

全局事务 id(gtid)可以很好的支持这几个功能。而且,使用 gtids 避免了在传送 binlog 逻辑上依赖文件名和物理偏移量,能够更好的支持自动容灾切换。

但是个人感觉,全局事务 id 这里还有些待解决的问题:

1)gtid 是局部有序的,不能记录事务的全局顺序。因此在双写 / 快速主备切换场景下,不能根据 gtid 顺序来解决更新冲突的问题。

2)容灾切换时,master_auto_position 只能解决记录位点的问题。为了保证一致性,停写和等待主备 caught up 仍然是必须的,通常这是服务无法快速恢复的主要原因。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值