XID是什么
MySQL Binlog 文件由 event 组成,event 有不同的类型,而XID_EVENT 表示一个事务的提交操作。
当事务提交时,在 binlog 依赖的内部 XA 中,额外添加了 Xid 结构,binlog 有多种数据类型:
- statement 格式,记录为基本语句,包含 Commit
- row 格式,记录为基于行
- mixed 格式,日志记录使用混合格式
不论是 statement 还是 row 格式,binlog 都会添加一个 XID_EVENT 作为事务的结束,该事件记录了事务的 ID 也就是 Xid,在 MySQL 进行崩溃恢复时根据 binlog 中提交的情况来决定如何恢复。
XID如何生成
MySQL 内部维护了一个全局变量 global_query_id,每次执行语句的时候将它赋值给 Query_id,然后给这个变量加 1。如果当前语句是这个事务执行的第一条语句,那么 MySQL 还会同时把 Query_id 赋值给这个事务的 Xid。
XID是唯一的吗
而 global_query_id 是一个纯内存变量,重启之后就清零了。所以你就知道了,在同一个数据库实例中,不同事务的 Xid 也是有可能相同的。
但是 MySQL 重启之后会重新生成新的 binlog 文件,这就保证了,同一个 binlog 文件里,Xid 一定是惟一的。
虽然 MySQL 重启不会导致同一个 binlog 里面出现两个相同的 Xid,但是如果 global_query_id 达到上限后,就会继续从 0 开始计数。从理论上讲,还是就会出现同一个 binlog 里面出现相同 Xid 的场景。
因为 global_query_id 定义的长度是 8 个字节,这个自增值的上限是 2^64-1。要出现这种情况,必须是下面这样的过程:
执行一个事务,假设 Xid 是 A;
接下来执行 2^64次查询语句,让 global_query_id 回到 A;
再启动一个事务,这个事务的 Xid 也是 A。
不过,2^64这个值太大了,大到你可以认为这个可能性只会存在于理论上。
XID的作用
二阶段提交
步骤如下
InnoDB 进入 Prepare 阶段,并且 write/sync redo log,写 redo log,将事务的 xid 写入到 redo 日志中,binlog 不作任何操作
进行 write/sync binlog,写 binlog 日志,也会把 xid 写入到 binlog
调用 InnoDB 引擎的 commit 完成事务的提交,将 commit 信息写入到 redo 日志中
MySQL崩溃恢复会有什么操作
如果 redo log 里面的事务是完整的,也就是已经有了 commit 标识,则直接提交;
如果 redo log 里面的事务只有完整的 prepare,则判断对应的事务 binlog 是否存在并完整:
a. 如果是,则提交事务;
b. 否则,回滚事务。
MySQL 怎么知道 binlog 是完整的
一个事务的 binlog 是有完整格式的:statement 格式的 binlog,最后会有 COMMIT;
row 格式的 binlog,最后会有一个 XID event。
崩溃恢复的时候,会按顺序扫描 redo log:
如果碰到既有 prepare、又有 commit 的 redo log,就直接提交;
如果碰到只有 parepare、而没有 commit 的 redo log,就拿着 XID 去 binlog 找对应的事务。
MySQL解释如下
扫描最后一个binlog获取XID生成一个hash table,再扫描redo将checkpoint之后所有XID,如果XID在hash table里面则提交,不在则回滚。