阿里分布式事务解决方案fescar简析

一、概要

阿里最近开源了分布式事务的解决方案fescar。

Fescar 是 阿里巴巴 开源的 分布式事务中间件,以 高效 并且对业务 0 侵入 的方式,解决 微服务 场景下面临的分布式事务问题。

具体介绍可以详见wiki地址
分布式事务一直是微服务的一个痛点。性能问题和业务的接入成本一直是困扰开发者的难题。阿里能够实现业务0入侵并且保证性能,不得不觉得很惊讶。

二、解决的概要思路

解决方案
  • Transaction Coordinator(TC): 全局和分支事务的状态的保持,驱动这个全局的提交和回滚.
  • Transaction Manager(TM): 明确全局事务的范围:开始一个全局事务,提交或者回滚一个全局事务.
  • Resource Manager(RM): 管理分支事务工作资源,告诉TC,注册这个分支事务和上报分支事务的状态,驱动分支事务的的提交和回滚。

三、回滚方案

接下来我们重点看下fescar是如何做到对代码无入侵的。

Phase1:
Fescar 的 JDBC 数据源代理通过对业务 SQL 的解析,把业务数据在更新前后的数据镜像组织成回滚日志,利用 本地事务 的 ACID 特性,将业务数据的更新和回滚日志的写入在同一个 本地事务 中提交。
这样,可以保证:任何提交的业务数据的更新一定有相应的回滚日志存在。

Phase2:
如果决议是全局提交,此时分支事务此时已经完成提交,不需要同步协调处理(只需要异步清理回滚日志),Phase2 可以非常快速地完成。

如果决议是全局回滚,RM 收到协调器发来的回滚请求,通过 XID 和 Branch ID 找到相应的回滚日志记录,通过回滚记录生成反向的更新 SQL 并执行,以完成分支的回滚。

简单来说,是通过生成自动生成回滚sql来实现自动回滚的。

三、疑问

解决方案好像很美好,但是事实上会不会有问题呢?

1.回滚sql生成的正确性如何保证

fescar的做法是类似mysql binlog的做法,把更新前后的每个字段都保存下来,用于生成回滚的sql。

2.即便回滚的sql能正确生成,如何保证一定能回滚成功?
3.并发的场景数据的一致性如何保证

前面提到的高性能的前提是不会对资源做锁,也就是在phase1的时候就已经释放锁了。那么在并发场景下会不会有数据不一致的情况?

四、验证

MySQLUndoUpdateExecutor.java

protected String buildUndoSQL() {
        TableRecords beforeImage = sqlUndoLog.getBeforeImage();
        List<Row> beforeImageRows = beforeImage.getRows();
        if (beforeImageRows == null || beforeImageRows.size() == 0) {
            throw new ShouldNeverHappenException("Invalid UNDO LOG"); // TODO
        }
        Row row = beforeImageRows.get(0);
        StringBuffer mainSQL = new StringBuffer("UPDATE " + sqlUndoLog.getTableName() + " SET ");
        StringBuffer where = new StringBuffer(" WHERE ");
        boolean first = true;
        for (Field field : row.getFields()) {
            if (field.getKeyType() == KeyType.PrimaryKey) {
                where.append(field.getName() + " = ?");
            } else {
                if (first) {
                    first = false;
                } else {
                    mainSQL.append(", ");
                }
                mainSQL.append(field.getName() + " = ?");
            }

        }
        return mainSQL.append(where).toString();
    }

从代码可以看得出来,这里采用的是类似binlog的row方式,把更新前后的值都保存下来。然后在更新的时候尝试还原回去。
下面是我本地拿下来的一条undo_log的记录

mysql> select * from undo_log\G
*************************** 1. row ***************************
           id: 165
    branch_id: 201285990
          xid: 172.19.5.94:8091:201285989
rollback_info: {"branchId":201285990,"sqlUndoLogs":[{
"afterImage":{"rows":[{"fields":[{"keyType":"PrimaryKey","name":"ID","type":4,"value":3},{"keyType":"NULL","name":"COUNT","type":4,"value":98}]}],"tableName":"storage_tbl"},
"beforeImage":{"rows":[{"fields":[{"keyType":"PrimaryKey","name":"ID","type":4,"value":3},{"keyType":"NULL","name":"COUNT","type":4,"value":100}]}],
"tableName":"storage_tbl"},
"sqlType":"UPDATE",
"tableName":"storage_tbl"}],
"xid":"172.19.5.94:8091:201285989"}
   log_status: 0
  log_created: 2019-01-15 21:55:37
 log_modified: 2019-01-15 21:55:37
          ext: NULL

最终回滚的sql会生成
update storage_tbl set count=100 where count=98 and id=3;
回到我们的第二个问题:

2.即便回滚的sql能正确生成,如何保证一定能回滚成功?

如果回滚的时候count字段已经变化了,能保证回滚成功吗?

我们来考虑一个场景:对于一个减库存的业务,如果事务回滚,就需要把库存+1来进行回滚。

  • 但是由于我们希望能够做到阿里所说的高性能,在phase1提交成功后就可以马上开启第二个事务。当第二个事务成功完成phase2的提交后,第一个事务被通知需要回滚了。这时候必然会失败。
  • 如果我们对于整个仓库在整个分布式事务完全提交之前加锁,性能又会大大降低。
    由于这个模式是0入侵的,开发人员可能甚至在使用的过程中根本不会思考并发场景会导致的问题,直接理解成按本地事务的方式去实现,很有可能到线上才发现大问题。对于扣减的业务,回滚失败可能问题还不大,对于增加的业务,回滚失败很有可能就意味着公司资产的损失。

五、补充

在测试回滚的过程中发现一个比较诡异的地方,在测试demo的时候起了4个业务进程AccountServiceImpl,OrderServiceImpl,StorageServiceImpl,BusinessServiceImpl。
我在执行回滚日志的地方增加了一条日志。
AbstractUndoExecutor

                undoPrepare(undoPST, undoValues, pkValue);

                int affectRow = undoPST.executeUpdate();
                if(affectRow>0) {
                    LOGGER.info("执行回滚语句成功...undoSQL:{}",undoSQL);
                }else{
                    LOGGER.error("执行回滚语句失败...undoSQL:{}",undoSQL);
                }

结果发现所有的回滚都在全在AccountServiceImpl里面回滚了。难道还要求分布式事务涉及的所有进程的DB权限都必须共享>-<?

六、总结

总体我认为fescar的想法还是很美好,但是在实际落地过程中能否真正地解决问题我认为还是要再三经过考证才行。本人水平有限,可能对fescar理解有误的地方,欢迎大家指正。

七、问题

最后提几个问题,方便我们后面带着问题往下一步步深入了解

  1. undo log是如何生成的?如何知道修改前后的值?
  2. tc和rm的通讯方式是怎样,txid如何生成?
  3. rm强依赖tc,tc如何实现高可用?
  4. undo log这种实现方式如何解决ABA的问题?
  5. fescar能够解决哪些业务场景的一致性问题?
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,980评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,178评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,868评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,498评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,492评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,521评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,910评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,569评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,793评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,559评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,639评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,342评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,931评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,904评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,144评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,833评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,350评论 2 342

推荐阅读更多精彩内容