总结自己在项目中实现分布式事务的设计思路

序言

中国移动的BOSS项目(业务运营支撑系统)需要做异地机房改造,即要把原本部署在单个机房的系统集群再部署一套到新的机房(原有机房称为南基房,新增加的机房称为北机房)。这么做并不是为了实现异地容灾,而是因为随着业务量不断增大(目前6亿用户,预计未来半年将增长至8.5亿),现有机房已经无法分配新的服务器以供扩容使用,需要将一部分数据库中的数据从南基房迁移至北机房。并且由于机房两地使用的网络不是专网而是内部公网,可能存在网络不稳定的情况,需要两套系统独立部署,当新机房网络不通时不会影响原机房的业务,加上网络策略和安全策略的要求不得不分拆为两套独立的微服务系统,最终部署方案如下图所示:
由图示和实际业务分析,有以下几点需要考虑:

  1. 数据按照省份拆分,分别存储在不同机房的数据库,存在一些公共数据,当某业务处理时,两个机房都需要同步更新处理结果。
  2. 机房间的网络是隔离的,跨机房访问时只能通过物理负载均衡F5路由,所以机房需要单独部署注册中心和配置中心,无法共用一套。
  3. 实际业务比较复杂,BOSS系统需要处理异地补卡或开销户的业务涉及更新南北机房的数据。比如用户A在广东办理北京手机卡的补办或者销户,按照当前的部署方案,这个请求将会发送到南机房处理,但是A用户的数据存储在北机房,南基房通过调用北机房的DB公共服务来更新北机房数据。


    image

    显然,异地机房改造是涉及分布式事务的,于是查阅了不少这方面的资料,打算引入阿里的分布式事务框架seata,但是seata要求涉及分布式事务的应用都要以微服务的形式使用相同的注册中心和配置中心,这与上述第二点相违背。引入seata失败后,决定自己参考seata的一些实现原理自己实现一套简单可用的分布式事务,不强制依赖微服务体系。

实现思路

关于seata的官方介绍
官方文档:https://seata.io/zh-cn/docs/overview/what-is-seata.html
Github地址:https://github.com/seata/seata
因为我的实现思路大体上是参考了seata的AT 模式,所以有必要先了解seata是如何实现的。Seata的整体机制是两阶段提交协议的演变:
一阶段:业务数据和回滚日志记录在同一个本地事务中提交,释放本地锁和连接资源。
二阶段:

  • 提交异步化,非常快速地完成。
  • 回滚通过一阶段的回滚日志进行反向补偿。
    那么如果按照这个思路,关键是要实现生成回滚记录。

生成回滚记录

有了每一步操作对应的回滚记录就能做回滚。根据seata实现思路,需要先解析正向sql,生成对应的select查询语句,执行select语句获取beforeImage(变更前的数据)。执行完正向sql后再执行一遍select语句获得afterImage(变更后的数据),将这两个数据存储到一张名为undo_Log的表中,有了beforeImage和afterImage,自然就能对数据做回滚了。

TableRecords beforeImage = beforeImage();
T result = statementCallback.execute(statementProxy.getTargetStatement(), args);
TableRecords afterImage = afterImage(beforeImage);
prepareUndoLog(beforeImage, afterImage);

而我的思路是直接存储用于回滚执行的sql,而不是存储beforeImage和afterImage
,并且存储在缓存中而不是直接存到undo_log表,只有执行回滚失败后才存到undo_log表。这样将大大提升执行效率。


image

BOSS服务使用统一封装的RemoteDBCaller(跨机房数据库调用器),将sqlid和参数对象封装成请求,调用异地机房DB公共服务,由DB公共服务完成sql操作并返回执行结果。因此在执行sql操作前生成对应的回退sql是完全可行的。可以使用sqping的AOP对RemoteDBCaller做一个切面,在真正发起调用前,取出sqlid和参数对象,根据sqlid和对象生成具体将要执行的sql,再生成对应的回退sql。

具体如何根据正向sql生成反向的回滚sql呢?接着这里面其实有不少需要注意的地方。这里只对update进行详细说明,insert和delete是一样的原理。正向sql分为三类:

  • 正向是update,回滚是update。
  • 正向是insert,回滚是delete。
  • 正向是delete,回滚是insert。

生成update回滚sql

update语句形如:

update table set fieldA={afterValue},fieldB={afterValue} where FieldC='xx' and FieldA='xx';

先解析获取where条件和表名,拼接查询语:select * from table where FieldC='xx' and FieldA='xx';执行该语句可获取beforeImage。结合该表的主键生成用于回滚的update语句:

update table set fieldA={beforeValue},fieldB={beforeValue} where Primary_key={Id};

image

update的回滚是有局限性的,只适用于有主键的表。因为primary_key作为主键是不会变更的,非主键的字段值都可能在执行完update后改变,只有根据primary_key可以定位到涉及变更的数据。

事务流程控制

seata的事务控制过程如下图所示:


image

1)TM:事务的发起者。用来告诉 TC,全局事务的开始,提交,回滚。
2)RM:具体的事务资源,每一个 RM 都会作为一个分支事务注册在 TC。
3)TC 事务的协调者。也可以看做是 Fescar-server,用于接收我们的事务的注册,提交和回滚。

在seata中,seata-service作为TC管理全局事务,所有RM都需要和它通信,注册分支事务,上报事务状态。而PBOSS(因为机房间网络隔离)为了避免需要与所有RM通信,由TM兼任TC,事务控制过程如下图:


image

TC并不控制全局的事务,只控制它下级的事务。比如有ABC三个服务A调用B,B又调用C,那么A会记录B的undo_log,B记录C的undo_log。当C异常时,会触发B调用DB公共服务执行C的回滚sql,同时触发A调用DB公共服务执行B的回滚sql,从而完成全局事务的回滚。

完整程序流程图

结合自定义注解、spring aop实现非侵入式的全局事务控制,与seata类似,通过在方法中使用注解@GlobalTransactional开启全局事务即可。


©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,402评论 6 499
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,377评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,483评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,165评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,176评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,146评论 1 297
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,032评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,896评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,311评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,536评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,696评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,413评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,008评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,659评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,815评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,698评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,592评论 2 353

推荐阅读更多精彩内容