首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 数据库 > Mysql >

MySQL 5.6 全局事务 ID(GTID)兑现原理(二)

2013-01-23 
MySQL 5.6 全局事务 ID(GTID)实现原理(二)前文?MySQL 5.6 全局事务 ID(GTID)实现原理(一)? 介绍了 MySQL 5

MySQL 5.6 全局事务 ID(GTID)实现原理(二)
前文?MySQL 5.6 全局事务 ID(GTID)实现原理(一)? 介绍了 MySQL 5.6 全局事务 ID 的定义和相关的数据结构 Gtid_set 与 Sid_map。接下来,这一篇的主要目标是深入了解文章最后提到的全局事务状态 Gtid_state。并且,如果可能 —— 顺便介绍下这些 Gtid_state 在主备复制中的功能:?全局事务状态 Gtid_state?Gtid_state 是 MySQL 5.6 内的一个全局对象,它的数据结构如下:?(mysql-5.6.9-rc\sql\rpl_gtid.h,line 2043)?Gtid_state := (logged_gtids, lost_gtids, owned_gtids)?创建 Gtid_state 的代码可以在?mysqld.cc 的 gtid_server_init 方法里找到。(代码路径:mysql-5.6.9-rc\sql\mysqld.cc, 1719 line)?Gtid_state 的重要性是,它维护三个与全局事务 ID 有关的 MySQL global variables:?logged_gtids / `gtid_executed`?存储已经执行过,并且记录到 binlog 的全局事务 ID 集合。它对应的 MySQL variable 是?gtid_executed,可以用命令:?SHOW GLOBAL VARIABLES LIKE 'gtid_executed'?查看数据库上已经执行的全局事务 ID。?MySQL 5.6 在开启?--gtid_mode?=ON 后,每当执行完一个事务,就会调用 Gtid_state 的 update_on_flush() 方法,把事务对应的 GTID 写入 logged_gtids。?详细一点的过程是这样的:?为了防止写入 binlog 的是一组不完整的事务,MySQL 会缓存整个事务的 binlog 内容在 binlog_cache_data 中。如果事务提交,MySQL 会执行一个叫 ordered_commit() 的三阶段提交操作:(源代码:mysql-5.6.9-rc\sql\binlog.cc,line 6245)?第一步:调用?process_flush_stage_queue() 和 flush_io_cache() 将缓存在 binlog_cache_data 中的内容刷出到 binlog 文件。此时,Gtid_state 的 update_on_flush() 调用到,事务对应的 GTID 写入 logged_gtids。?第二步:调用 sync_binlog_file() 在 binlog 文件上执行 fsync() 保证内容更新到磁盘。??第三步:调用?process_commit_stage_queue() 执行所有事务提交,在存储引擎上调用 ha_commit_low()。?结束后,MySQL 调用 Gtid_state 的 update_on_commit(),从 owned_gtids 里删除完成 ?commit 的全局事务 ID。?与名字相同,logged_gtids 用来判断 MySQL 有没有执行某个事务。例如,在 Master 发送 binlog 时,MySQL 5.6 能够根据 Slave 提供的 logged_gtids 记录,自动过滤 binlog 中不需要执行的事务(请参考新增 COM_BINLOG_DUMP_GTID 协议)。??另外,MySQL 5.6 支持在 START SLAVE 时指定?UNTIL_SQL_BEFORE_GTIDS / UNTIL_SQL_AFTER_GTIDS 条件,让 Slave 执行完所需要的事务后就自动停止复制。这个功能也依赖于 Slave 的 logged_gtids 记录来检查作为条件的 GTIDs 是否满足。?lost_gtids / `gtid_purged`?记录从 binlog 删除的全局事务 ID 集合。它对应的 MySQL Global variable 是:gtid_purged? 。?每当 MySQL 5.6 调用?purge_logs 删除 binlog 时,会顺带更新 lost_gtids 的内容。这是通过读剩下的 binlog 文件实现的,我会在介绍 GTID 在 binlog 的存储方式时描述。(源代码:mysql-5.6.9-rc\sql\binlog.cc,line 3754)?它的作用是检查 Slave 请求的 Gtids 是否已经被 Master 删除。如果 Master 的 lost_gtids 记录已经不是 Slave 的 logged_gtids 记录的子集,请求的 Slave 会收到代码为?ER_MASTER_HAS_PURGED_REQUIRED_GTIDS 的错误。?owned_gtids / `gtid_owned`?正在由线程执行的全局事务 ID 集合。它对应的 MySQL variable 是:gtid_owned,而对应?的类型是 Owned_gtids,基本上可以看作一个 Gtid 到 owner_thread_id 的 hash_map?映射:?Owned_gtids := array(sidno => hash_map(Node))?Node := (gno, owner_thread_id)??其中 gno 是 Gtid 中的事务 ID。?在 MySQL 5.6 中,owned_gtids 提供了一个正在执行的事务纪录(以及执行它们的线程 ID)。这份记录是怎么维护的? —— 这与 MySQL 产生 GTID 的过程有关:?每个数据库更新都会产生一条 binlog,当一条 binlog 写入 binlog_cache_data 之前,MySQL Master 会调用?generate_automatic_gno() 产生一个 gno —— 事务 ID,详细过程是这样的:?首先,generate_automatic_gno ?会检查 Gtid_state 中的 logged_gtids 和 owned_gtids,找到一个当前最大的而且没有使用的 gno(Gtid_state 的 get_automatic_gno() 方法),创建出新的 Gtid。?然后,MySQL 调用 Gtid_state 的 acquire_ownership(),把新的 Gtid 写入全局的 owned_gtids,并记录到线程的 owned_gtid 变量(注意:NDB 集群的处理有不同,这里我不一一介绍了)。?当事务结束时,MySQL 会调用 Gtid_state 的 update_on_commit / update_on_rollback 方法,把线程执行的 owned_gtid 从全局 owned_gtids 中删除。?全局 owned_gtids 常常用来反向查找执行事务的线程。有个重点是,在?Gtid_state 的 acquire_ownership() 方法中,如果所给的 Gtid 在全局 owned_gtids 已经被标记成另一个线程执行,那么 MySQL 会尝试等待并检查这个线程是否被 kill。?Gtid_state 回顾?从上面 Gtid_state 的实现逻辑中,大家可以看到,在 MySQL 5.6 里一个全局事务 ID 的生命周期是这样的:?首先,执行数据库操作时,产生一个全局事务 ID,立即记录到全局和当前线程的?gtid_owned?(owned_gtids)状态。?接着,提交数据库事务时,新产生的全局事务 ID 被写入 binlog,接着记录到?gtid_executed(logged_gtids)状态,然后从全局与线程区域的?gtid_owned?(owned_gtids)状态中清除。?如果 DBA 执行了 purge 操作删除 binlog,被删除的全局事务 ID 会记录到?gtid_lost(lost_gtids)。但是,这些全局事务 ID 仍然包含在?gtid_executed(logged_gtids)全局状态里。?最后。?在上面的介绍中,我刻意没有提到 MySQL 是怎么保持这些全局事务状态的持久化的。因为这些和 5.6 新增的 binlog 事件 Gdit_log_event / Previous_gtid_log_event 有关。下一篇,我会先介绍一下 MySQL 5.6 对 binlog 格式的扩展,然后再介绍全局事务 ID 是如何作用于新的主备复制流程的。?(未完待续)

热点排行