Nodejs 分布式事务

事务是恢复和并发控制的基本单位,保证ACID:原子性、一致性、隔离性、持久性。

对于全是异步的 Nodejs 而言, 并不适合做事务操作:

代码书写上:

try ... catch ...是写给人看的,但是属于同步方法,局限性很大。

callback简直是噩梦。

Promise.then(...).catch(...)相对而言好一点。

ES7 的async ... await ...比较清爽,使用 Babel 编译,这是笔者目前找到的最人类的方式,但是和原来的Promise混合使用的时候有时候会出问题,因为编译之后的代码没法看,最后还得重构回Promise。

异步:

异步导致有可能有意料之外的uncaughtExceptionError。

对于 JAVA/C++ 这样语言,出错能直接转到catch中,但是 Node 不是,uncaughtExceptionError将直接导致处理链断掉,你只能通过其他方式保证数据一致性。

虽然 Node 做事务相当非人类,但是考虑开发效率 / 成本,使用 Node 进行开发并不比换语言开差,毕竟事务只有核心业务需要用到。

单点

单机的事务相当容易保证,特别在依赖 MySQL 或者其他关系数据库时。

Nodejs 有 ORM (如Sequelize) 支持事务,也可以直接使用PROCEDURE/FUNCTION。

两者各有优势:

ORM 适合复杂逻辑的事务;

存储过程可以有效减少 IO 次数,防止使用 ORM 时回滚失败。

实际开发过程中可以将两者结合起来一起使用,使用 ORM 完成逻辑,使用存储过程减少 IO 次数。

分布式

对于分布式系统,相信很多人都知道CAP理论,即任何一个分布式系统无法同时满足:

Consistency (一致性)

Availability (可用性)

Partition tolerance (分区容错性)

但是实际上 Consistency 是任何一个系统都不可能放弃的,分布式事务亦是为了保证数据一致性,有时候为了妥协另外两个特性,会放弃强一致性,保证最终一致性

http://www.developcls.com

http://www.developcls.com/qa/85d4b9a5d70a4a7d8709a95ae56d33b7.html

解决方式

目前业界有很多解决分布式事务的方案,根据对数据一致性的强弱要求,可以选择不同的方案,但是解决思路大致如下:

两阶段提交

如 XA 协议(TM(事务管理器)和RM(资源管理器)之间的接口)。

假设有 A、B、C 三个操作,第一阶段,等待 A B C 均就绪,第二阶段,提交 A B C;如果第一阶段 A 失败了,则第二阶段回滚 B C。

本地事务

使用本地消息表,将远程事务拆分成一个个本地事务,写入本地表中,然后 定时 / 使用 MQ 通知事务方。

两者各有利弊,定时扫描可能大部分时候都在做无用功,而只使用 MQ 可能会有失败 / 多次消费的问题。

使用回滚接口

如 A B 两个接口,串行处理,B 失败了回滚 A ,但是回滚也可能失败,所以也需要使用本地事务表 / MQ。

使用 Node 开发,1 比较重型,不适合;2 和 3 是比较好的选择:

选择一款可靠的 MQ 服务(单次消费 / 失败重试);

拆分本地事务;

不能拆分的事务,保证回滚。

举个栗子

做一个抢购系统,用户使用虚拟币进行抢购,虚拟币是另外一套系统。为了考虑到公平,每个用户还可能要限制购买上限。

这样用户一次抢购的完整流程如下:

检查购买上限

检查总数

扣除虚拟币

写入数据库

需要事务保证的地方就是 3 和 4,3 是远程事务,4 是本地事务,此栗子中必然是串行操作,3 在前,4 在后。

0x0001

这个时候流量很少,并发不高,将 3 和 4 作为一个事务,保证一起成功,而失败一起回滚。

事务 4 即使使用 ORM 完成,也能完成功能,这个时候系统能很好的工作。

0x0010

流量上升中,抢购的商品变多,并发也变大,这个时候,考虑使用 Redis 来提高性能了(牺牲强一致性):

将 购买上限 与 总数 写入 Redis,在压力转嫁到数据库之前就挡掉,由于 Redis 的强大性能,可以假设 Redis 等同于内存操作,做好回滚就可以了。

同时可以将事务 4 重构成PROCEDURE防止 ORM 可能回滚失败。

0x0011

流量大到数据库扛不住了,加入 MQ 服务:

使用 Redis 抗住流量,使用 MQ 抗住压力,使用PROCEDURE降低 IO 。

0x0011看起来像一个可靠的系统了,但是还有一个隐患:uncaughtExceptionError或者 程序宕掉了,这个会影响最终一致性,导致 Redis 数据与 Database 中的数据在抢购临界结束的时候不一致。

0x0100

增加最终一致性保证。

抢购的栗子有个很特别的地方,就是total limit,达到总数上限之后,就只有 MQ 中的部分需要处理了,因此可以很巧妙的利用时间差,即考虑在达到上限之后,取一次数据库快照,延迟一段时间之后,再对比一次数据库,判断是数据不一致还是正常逻辑。

这是一个投机取巧的处理方式,一定程度上可以保证最终一致性。当然,还是人最靠谱了,程序搞不定,人工修复嘛,ORZ~。

终语

分布式事务是一个很大的话题,依据业务量大小可以给出很多实现。

Nodejs 做分布式事务勉勉强强,异步里面的雷很多,不过依赖良好的设计和逻辑一样可以实现。

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

推荐阅读更多精彩内容