IT技术博客大学习 共学习 共进步

MySQL数据库分布式事务XA的实现原理分析

MySQLOPS 数据库与运维自动化技术分享 2012-02-26 23:09:57 浏览 3,505 次

1       原理

关于MySQL数据库的分布式事务XA,分布式事务实现的原理,可见[3];关于MySQL XA的说明,可见[1][2]。

MySQL XA分为两类,内部XA与外部XA;内部XA用于同一实例下跨多个引擎的事务,由大家熟悉的Binlog作为协调者;外部XA用于跨多MySQL实例的分布式事务,需要应用层介入作为协调者(崩溃时的悬挂事务,全局提交还是回滚,需要由应用层决定,对应用层的实现要求较高)
本文,假设读者已经知道MySQL数据库外部分布式事务XA的使用,而将重点放在MySQL数据库,如何处理外部分布式事务XA的crash recover,以及面对不同的crash recover的情形,应用程序如何处理,才能够保证分布式事务的一致性。最后,本文简单分析一下目前MySQL数据库外部XA支持存在的问题,以及可选的解决方案。

源代码分析基于MySQL 5.1.49,MySQL 5.5.16。

2       MySQL处理流程

2.1     MySQL 外部XA - 正常处理流程

MySQL数据库外部XA的正常处理流程,这里不准备介绍,可以参考[1][2][3]。接下来我重点描述一下MySQL数据库外部分布式事务XA的崩溃恢复流程,毕竟此流程跟应用程序如何正确使用外部XA息息相关。

2.2     MySQL外部XA - 崩溃恢复流程

若一个运行外部XA分布式事务的MySQL数据库节点发生崩溃,那么其重启之后的崩溃恢复,涉及到外部XA处理的流程如下:

Crash recover:

// 1. 读取binlog文件,将文件中的xid存入commit_list hash表

//    顾名思义,所谓的commit_list,就是说此list中对应prepare状态的xid

//     在崩溃恢复过程中均可以被提交,而不在commit_list中的xid,均须回滚

//    binlog中的xid,都是属于内部xid,由MySQL产生,用于内部XA

Log.cc::TC_LOG_BINLOG::recover

// 2. 遍历底层所有的事务引擎,收集处于XA_PREPARED状态的所有xid

//     这些xid列表,既包括内部xid,也包括外部xid,存储引擎内部不做区分

Handler.cc::ha_recover(commit_list)

// 执行各引擎层面提供的recover方法,收集所有的处于prepared状态的xid

// 根据xid分类:

// 3. 若xid属于内部xid,那么在commit_list中查找此xid,

//     若存在,则提交此xid对应的事务;否则,回滚此事务

// 4. 若xid属于外部xid,那么则将xid插入xid_cache hash表

//     xid_cache中的所有xid,将会通过xa recover命令返回,等待外部程序决策

Handler.cc::xarecover_handlerton

// 5. 收集InnoDB存储引擎中,处于prepare状态的所有xid,并返回

got = hton->recover (innobase_xa_recover)

my_xid x = info->list[i].get_my_xid();

if (!x)

// 若当前为外部xid,那么将xid插入xid_cache hash表

xid_cache_insert(&xid_cache, x);

else

if (x in commit_list)

// 若当前为内部xid,同时此xid在binlog中存在,则提交

hton->commit_by_xid();

else

// 若当前为内部xid,同时此xid在binlog中不存在,则回滚

hton->rollback_by_xid();

通过以上的分析,可以总结出:

  • MySQL数据库内部,会对xid做区分。内部xid有MySQL数据库自己产生(MySQL内部xid格式,将在本文下面给出),用于多引擎间事务的一致性;外部xid由应用程序给出,用于跨多MySQL实例的分布式事务。但是存储引擎层不做区分(区分在MySQL上层)。
  • crash recover时,存储引擎负责将引擎内部,处于prepare状态的事务收集,并返回MySQL上层。
  • Binlog作为内部XA的协调者[5],在binlog中出现的内部xid,在crash recover时,由binlog负责提交;在binlog中未出现的xid,由binlog负责回滚。(这是因为,binlog不进行prepare,只进行commit,因此在binlog中出现的内部xid,一定能够保证其在底层各存储引擎中已经完成prepare)。
  • 外部XA事务的xid,在crash recover过程中仅仅是插入xid_cache中,而不做其他处理。等到用户发起xa recover命令时,将xid_cache中处于prepare状态的xid返回。
  • xa recover命令的流程处理如下。

xa recover命令处理流程:

sql_parse.cc::mysql_execute_command

case SQLCOM_XA_RECOVER:

mysql_xa_recover();

// 遍历xid_cache,找出其中的状态处于XA_PREPARED的事务,发送客户端

while (xs = hash_element(&xid_cache,))

if (xs->xa_state == XA_PREPARED)

protocol->write();

根据xa recover命令收集到的各MySQL数据库实例返回的xid列表,然后再对比应用程序端日志,决定这些xid,哪些全局commit,哪些rollback。

由于测试中只有一个MySQL数据库实例,因此此时可以直接选择commit处于prepare状态的xid。

  • MySQL内部xid格式:       MYSQL_XID_PREFIX + server_id + my_xid

MYSQL_XID_PREFIX:     MySQLXid  (源码写死)             8 bytes

server_id:                          MySQL实例的id,ulong,        4 bytes

my_xid:                            内部自增序列,ulonglong,       8 bytes

例如:”MySQLXid 0004”

server_id = ‘ ’;my_xid = 4

因此,使用时应该注意,不要在外部构造这种形式的xid,否则MySQL数据库就会将内部xid与外部xid混淆。

一般情况下,构造一个内部xid比较困难,由于server_id一般小于4 bytes,不足的部分以ASCI NIL(null)补齐,而应用程序一般都不会构造此类的外部xid。

建议继续学习

  1. 分布式缓存系统 Memcached 入门 (阅读 16,043)
  2. Zookeeper工作原理 (阅读 11,944)
  3. GFS, HDFS, Blob File System架构对比 (阅读 10,343)
  4. Zookeeper研究和应用 (阅读 9,342)
  5. 一致性哈希算法及其在分布式系统中的应用 (阅读 9,043)
  6. 分布式日志系统scribe使用手记 (阅读 8,843)
  7. 分布式哈希和一致性哈希 (阅读 8,665)
  8. HBase技术介绍 (阅读 7,943)
  9. 分布式系统的事务处理 (阅读 7,246)
  10. Memcache分布式部署方案 (阅读 6,666)