前言: 本文是《持久化:Java帝国反击战》续集,开始讲解分布式事务。
1
JDBC大臣
自从和东海之滨的数据库联合酋长国缔结了合作协议以后, IO大臣就退居二线了。
他本来也想把JDBC也划归自己管理, 奈何国王头脑发热、竟然任命了新的JDBC大臣, 专门负责这一摊事儿。
JDBC大臣经常在早朝上给国王吹风: “ 陛下, 我们的JDBC设计的非常好, 别看什么Hibernate, Mybatis 是现在的事实标准, 他们底层都在用我们的JDBC接口。 ”
国王赞许地频频点头,似乎忘记了这是躲在角落中IO大臣的功绩。
IO大臣咬牙切齿又无可奈何。
这天JDBC又在给国王安利关系数据库的好处: “陛下,这关系数据库相比于简单的文件系统有个巨大的好处,就是支持事务。”
听到JDBC大臣又在贬低自己负责的部门, IO大臣怒火中烧。
国王问道:“什么是事务,要事务干嘛? ”
“我举个通俗的例子你就明白了, 假设IO大臣要给我转账100块钱, 他的数据库账户要扣掉100块, 我的账户要增加100块, 这就涉及到两个操作, 这两个操作要么全部完成,要么一个都不做,只有这样才能保证数据的一致性, 这就是一个事务。数据库联合酋长国有个对事务总结了4个特性: 原子性(Atomicity) ,一致性(Consistency), 隔离性(Isolation) , 持久性(Durability) , 简称ACID, 要不我再给详细的解释下?”
国王连忙摆手:“不不不, 别拿这些细节烦我, 你就告诉我们的臣民怎么去使用就行了”
JDBC大臣说: “这个很简单, 默认情况下我们的JDBC都会把对数据库的操作认为是一个事务, 当然臣民们也可以设置成手工的方式, 手工地提交和回滚事务。不管哪种方式,都是非常简单的 。”
国王说: “那就好, 爱卿辛苦了, 还有事吗? 有事启奏,无事退朝。”
2
密谋
IO大臣回到家中,依然感觉火气难平, 招来幕僚商谈。
InputStream说: “大人, 这JDBC大臣虽然猖狂, 我们却暂时拿他没办法, 现在都是Web时代, 哪个应用不用数据库啊? ”
“难道就让他这么猖獗下去? ”
InputReader足智多谋: “我倒是有一计, 只是得等待时机。 ”
“什么时机?”
“你看今天JDBC那厮提到了事务, 但是这个事务只是在一个数据库中有用啊, 如果需要跨数据库怎么办? 比如我的账号存在数据库A, 你的账号在数据库B, 那转账的时候怎么办? 怎么实现什么ACID ? ”
InputStream表示不同意: “谁会这么傻, 把我们的账号信息放到两个数据库当中? ”
“这就是时候未到, 现在大部分的应用数据量都不大, 放到一个数据库中绰绰有余,等到数据量大到一定程度,势必要拆分数据库,就会出现跨数据库的事务, 到那个时候我们的机会就来了, 我们准备好解决方案, 参那厮一本, 不信扳不倒他!”
IO大臣拍板: “好! 就这么办, 这事离不开数联酋(数据库联合酋长国)的支持,我和他们还有交情, 这就派人去,许以重金, 让他们继续和我们合作。”
在IO大臣密谋的同时, JDBC大臣的家中却是觥筹交错、莺歌燕舞。
有识之士如Connection 曾经向JDBC大臣提醒过要和数联酋搞好关系, 以便将来有什么不时之需。 可是处于巅峰的JDBC大臣哪能听得进去?
3
两阶段提交
InputReader 果然很有远见, 随着时间的流逝, Web越来越发达, 帝国出现了很多巨型网站, 他们的各种数据果然是没法放到一个数据库中了,把大的业务系统查分成多个数据库势在必行, 当一个业务同时操作多个数据库的时候, 没有分布式事务是做不了的。
正在此时,一个秘密奏章被送到了国王的案头, 状告JDBC大臣因循守旧,面对大好的形式不与时俱进,对分布式事务漠不关心,毫无作为。
国王召集朝会,讨论分布式事务的问题, 他向JDBC大臣率先发难: “爱卿, 你听说过臣民们要求支持分布式事务吗?”
JDBC大臣慌了: “这。。。 这好像是一撮刁民提的要求吧, 陛下不用理会。”
IO大臣冷笑一声:“刁民? 我看是良民吧 ! 启奏陛下, 据臣所知,帝国有不下百个系统要求支持分布式事务,JDBC大臣竟然连最基本的情况都不知道, 真是毫无作为。”
IO大臣觉得稳操胜券,直接撕破了脸。
国王心里明白了几分, 他直接对IO大臣说: “爱卿,你说说该怎么办?”
“陛下,当年臣和数据库联合酋长国谈判的时候, 和他们建立了良好的交情。 前几天我宴请他们的时候,特别提及了这件事情。 Oracle 告诉臣,这很好办, 人家别的王国正在讨论实施两阶段提交的协议, 我们也可以参与进来。”
虽然IO大臣已经和数据库联合酋长国讨价还价了很久, 不知道花费了多少金钱,但还是不显山不漏水、很随意地说了出来。
JDBC大臣一看IO大臣进入了自己的一亩三分地, 急忙问道: 什么是两阶段提交?
IO大臣不屑地瞥了他一眼, 从袖子中拿出早就准备好的提议,双手向国王奉上。
国王哪里看得懂,扫了一眼就赐给望眼欲穿的JDBC大臣, 只见上面赫然写着:
两阶段提交协议
由于涉及到多个分布式的数据库, 我们特设一个全局的事务管理器,它来负责协调各个数据库的事务提交, 为了实现分布式事务,特设两个阶段:
阶段1: 全局的事务管理器向各个数据库发出准备消息。 各个数据库需要在本地把一切都准备好,执行操作,锁住资源, 记录redo/undo 日志, 但是并不提交, 总而言之,要进入一个时刻准备提交或回滚的状态, 然后向全局事务管理器报告是否准备好了。
阶段2: 如果所有的数据库都报告说准备好了, 那全局的事务管理器就下命令: 提交, 这时候各个数据库才真正提交 , 由于之前已经万事具备,只欠东风,只需要快速完成本地提交即可;
如果有任何一个数据库报告说没准备好, 事务管理器就下命令: 放弃, 这时候各个数据库要执行回滚操作, 并且释放各种在阶段1锁住的资源。
JDBC大臣也是行家,一看就明白了是怎么回事。阶段1就是让大家都准备好,阶段2就是迅速提交。
这是一个看起来很美的理想方案,但是他意识到其中有漏洞,自己的幕僚曾经告诫过:一旦涉及到分布式,事情就不会那么简单,任何地方都有失败的可能。
比如在第二阶段,那个事务管理器要是出了问题怎么办? 人家各个数据库还在等着你发命令呢? 你迟迟不发命令,大家都阻塞在那里,不知所措,到底是提交呢?还是不提交呢, 我这里还锁着资源呢, 迟迟不能释放,多耽误事啊 !
还是第二阶段,事务管理器发出的提交命令由于网络问题,数据库1收到了,数据库2没收到,这两个数据库就处于不一致状态了, 该怎么处理?
JDBC大臣决心给IO大臣挖个坑:让你逞能 ! 让你给老子穿小鞋!
他说:“ 陛下,IO大臣不愧为设计过JDBC协议的股肱之臣, 臣才学疏浅,深为拜服,特奏请陛下恩准IO大臣再次出山和数据库联合酋长国设计出新协议, 来支持分布式事务。”
国王准奏。
4
JTA
IO大臣满心狐疑, 不知道JDBC老头儿在给自己下什么药,回到府中和大家商量。
InputReader 眼看自己多年前的计策就要成功,颇为兴奋: “管它呢, 只要咱们把这个分布式事务的协议给制定好,JDBC老儿就得下台了。”
“对,到时候我们就掌管文件, 网络,数据库,Java 帝国就是我们IO独大了” InputStream 开始畅想美好的未来,到时候自己估计至少从5品升为4品。
IO大臣马上安排和数据库联合酋长国的谈判,由于之前良好的交情。 这一次协议很容易就达成了, IO大臣给他起了一个很响亮的名字: Java Transaction API (简称JTA)。
这个JTA规范用起来也比较简单, 只要获得一个UserTransaction 就可以操作了,帝国的臣民们根本不用关系底层的协议细节:
经过国王的批准, JTA正式推广。
可是令IO大臣万万没有想到的是, 国王在JTA发布的前夕, 亲切地召见了自己和另外一个不知名的官员, 国王关心地说:“爱卿,朕知道你很忙,掌管着网络和文件操作,为了给你减轻负担,朕决定任命一个新的JTA大臣来协助你!”
IO大臣如同五雷轰顶,自己辛辛苦苦的工作完全被无视, 这到底是为什么?
他失魂落魄地回到府中, 好几天茶饭不思。
还是InputReader 出来安慰了他: “这是陛下的帝王之术, 害怕我们一家坐大, 平衡了一下朝中力量。大人可以放宽心, 你看JDBC大臣也受到了打压,风光不再了。”
5
塞翁失马,焉知非福
JTA并没有取得像JDBC那样的广泛应用, JDBC大臣挖的那个坑现在终于露出了狰狞的面目。
只不过这个坑并没有让IO大臣掉进去, 新任的JTA大臣背了黑锅。
臣民的抗议声越来越多: 分布式事务伴随着大量节点的通信交换, 协调者要确定其他节点是否完成, 加上网络带来的超时,导致JTA性能低下, 在高并发和高性能的场景下举步维艰。
拜IO大臣的工作所赐, 现在数据库联合酋长国的各个部落都支持两阶段提交,很多应用服务器Websphere , Weblogic 等都支持JTA, 可是使用者确是寥寥无几, 都快成摆设了。
JTA大臣每次上朝都战战兢兢, 他是个平庸之辈,虽然四处救火,但是无力解决根本的问题。
现在那些高并发的系统反而极力避免两阶段提交, 他们绕开JTA大臣, 直接找到了IO大臣诉苦:“大人,你带领着制定了JTA, 但是这个标准太理想化,完全不符合实情啊! ”
IO大臣说: “不会吧,这不是你们要求的吗, 用户A和B的账号分别在两个数据库, 当A给B转账100块的时候, 肯定得保证A扣掉100, 然后B增加100啊。”
“这就是官府的想法, 总是想着让两个数据库保证实时的一致性(强一致性), 为了达到这个目标,JTA付出的代价太高了。 我们现在不想这么干了。 我们可以忍受一段时间的不一致,只有最终一致就行。 比方说A给B转100元, A 中的钱已经扣除, 但是B中不会实时地增加,过段时间能保证增加就行了”
“最终一致性? 有点意思!” ,想到Java 帝国的官方标准总是被臣民们所建立的事实标准所打败,敏锐的IO大臣立刻看到了背后的机遇, 他决定这一次要联合民间力量,再次反攻, 一举搞掉JDBC大臣和JTA大臣。
想到这里, IO大臣得意地笑了......
(未完待续)
你看到的只是冰山一角, 更多精彩文章,请移步《码农翻身2016文章精华》或者《码农翻身2017上半年文章精华》
有心得想和大家分享? 欢迎投稿 ! 我的联系方式:微信:liuxinlehan QQ: 3340792577
码农翻身
用故事讲述技术