说说数据库事务和开发(下)—— 分布式事务
Posted 技术闲谈
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了说说数据库事务和开发(下)—— 分布式事务相关的知识,希望对你有一定的参考价值。
前言
本文分上下两篇,主要系统总结常用关系数据库ORACLESQL Servermysql的事务特点原理以及场景,并跟OceanBase以及GTS、DTX进行比较,分析其中原理和风险,方便传统应用开发评估使用分布式数据库和中间件产品的风险。
本篇首先分析分布式事务的“最终一致性”的解决方案的原理,以及跟“强一致性”解决方案的区别。然后分析两款分布式事务中间件产品(GTS
和DTX
)的使用区别。最后介绍两个分布式数据库(DRDS
和OceanBase
)原生的分布式事务的原理和使用场景。
分布式事务场景
下面两种场景可能会产生分布式事务
业务垂直拆分多个模块,每个模块使用不同的数据库(有可能数据库类型都不一致),而一个业务横跨多个业务模块。如电商业务里下单时付款、库存扣减和积分发放服务等,以及付款时涉及到的交易、支付和帐务多个数据库等。
数据库水平拆分后,业务事务涉及到多个数据分片。如A给B转账业务,A和B的帐务数据在不同的分库上。
分布式事务解决方案
全局事务 DTP
模型
X/Open DTP
(X/Open Distributed Transaction Processing Reference Model
) 是X/Open
这个组织定义的一套分布式事务的标准,也就是了定义了规范和API接口,由各个厂商进行具体的实现。
DTP它规定了要实现分布式事务,需要三种角色:
AP
:Application
应用。就是分布式事务的发起方。TM
:Transaction Manager
事务管理器。负责分布式事务实现,提供TX接口给AP
使用。同时管理所有的资源管理器。TM
可以通过2PC
、3PC
或者Paxos
等协议实现分布式事务。RM
:Resource Manager
资源管理器。资源管理器包含业务数据,如数据库、消息中间件、缓存等。资源管理器能提供单机事务,并通过XA
接口将单机事务的提交、回滚交给TM
管理。XA
接口是DTP
定义的规范,如果数据库不支持XA
,则不能使用这个方案。两阶段提交过程:
在一阶段,
TM
协调所有RM
执行XA事务(业务逻辑,包括xa_start
、业务SQL、xa_end
)并完成事务预提交(xa_prepare
)。也有的认为业务逻辑这一部分不算做第一阶段。在二阶段,收集所有
RM
预提交信息。只要有一个RM
返回失败或者超时,TM
就会协调所有RM
执行回滚操作(xa_rollback
);否则,协调所有RM
执行提交操作(xa_commit
)。
以上是正常流程。还有异常情况这里就不展开。 另外,2PC
还有个缺陷就是协调者单点。这个可以通过3PC
流程缓解。不是本文重点,这里也不展开。
补偿型事务 TCC
模型
补偿型事务仿照全局事务,也需要三种角色:AP
、TM
和RM
。TCC
全称为Try
、Confirm
和Cancel
。注意这里说的TCC
是框架模型,不是具体的产品。
如上图是TCC
模型的2PC
架构图。跟DTP
很相似。 TCC(RM)
指应用服务实现TCC
接口就可以作为RM
参与到分布式事务中。具体体现为应用服务要实现三个类型的方法:
(1)Try
方法:完成业务所有业务的一致性检查(满足业务前提条件),预留(即“锁定”)好执行所需要的全部业务资源。
(2)Confirm
方法:直接执行业务逻辑(不做任何前提条件检查)。执行过程中会消费(即“释放”)Try
阶段预留的资源。
(3)Cancel
方法:取消执行的业务逻辑。返还(即“释放”)Try
阶段预留的资源,并回滚Try
阶段执行的操作(前提是对应Try
操作执行成功过)。
备注: Try
阶段的业务一致性检查和资源预留,就是一个典型的“先读后写”的过程。为了规避上篇文章里提到的丢失更新
场景,这里的读是当前读(需要加锁),使用 SELECT...FOR UPDATE
。
转账示例
业务场景:甲转乙 100¥。甲和乙的数据分属于两个数据库(RM1
和RM2
)。 下面是sql伪代码。只是示例,真实场景可能有更多细节要考虑。
# `RM1`:
## try:
select 余额,冻结余额 from account where name='甲' for update;
判断 余额-(冻结金额+100) 大于0
update account set 冻结金额=冻结金额+100 where name='甲';
commit;
## confirm:
update account set 余额=余额-100, 冻结金额=冻结金额-100 where name='甲';
commit;
## cancel:
update account set 冻结金额=冻结金额-100 where name='甲';
commit;
# `RM2`:
## try:
update account set 冻结金2=冻结金2+100 where name='乙';
commit;
## confirm:
update account set 余额=余额+100, 冻结金2=冻结金2-100 where name='乙';
commit;
## cancel:
update account set 冻结金2=冻结金2-100 where name='乙';
commit;
模型DTP
和TCC
的比较
DTP
在第一阶段通过数据库(RM
)的锁去锁定业务资源;TCC
是在第一阶段通过应用设计预留(锁定)业务资源,同时数据库记录也有锁(一阶段结束时释放)。DTP
的每个RM
的两阶段都是在一个单机事务中;TCC
的RM
的两阶段是分属于两个独立的单机事务。DTP
的每个数据库(RM
)的锁在RM
完成第二阶段提交或者回滚后释放;TCC
的数据库的锁在RM
的第一阶段结束后就释放了,业务设计的“锁”在第二阶段结束后“释放”。所以TCC
事务的第二阶段可以跟其他并发TCC
事务的第一阶段并行运行(会有锁竞争),第一阶段的并发相对更高,这也是TCC
性能优于XA
的一个原因。DTP
之所以是强一致,是因为有锁全程保护排斥并发会话修改,满足ACID
特性。TCC
之所以是最终一致,是因为各个RM
节点第一阶段本地事务可以独立提交或者回滚,在全局事务第二阶段结束之前,应用有可能看到中间状态数据(可能是不符合业务一致性的中间状态数据),而在全局事务第二阶段结束之后,应用的数据又最终符合业务一致性了。TCC
模型适合于对性能要求高于一致性要求的业务。TCC
的二阶段的两个方法(Confirm
和Cancel
)要支持幂等调用,通过保存在RM
内部的事务状态信息判断。这个用在网络异常或者节点宕机恢复时。
TCC
的自动化用法(框架)
这个标题命名不是很好。
补偿型事务TCC
模型的特别在于让业务去锁定资源,并自己决定提交逻辑和回滚逻辑,这个也可能是个缺点,意味着现有的应用都要改造去实现这个方法。所以补偿型事务 TCC
模型还支持一种应用不用修改的实现方式(框架),对三个方法的内容会有所改变。
Try
方法:应用执行业务逻辑SQL,框架拦截用户SQL,对修改操作自动查询前镜像(也是历史快照)和后镜像数据,保存在RM
(数据库)内部,这个类似于数据库的Undo
日志。然后提交。Confirm
方法:删除快照数据。并提交。Cancel
方法:读取快照数据,生成Undo
SQL,并提交。
备注:
(1)这个框架的优势就是应用改造成本最小,影响就是数据库实际
TPS
会翻倍。在评估数据库性能时要把框架发起的sql算在里面。(2)这个特点依然是最终一致。
这里面隐藏着两个重要细节:
(1)在读取前镜像和后镜像数据时,框架发起的读是
当前读
(需要加锁),使用SELECT...FOR UPDATE
。(2)为了避免脏写,框架会在内部针对每笔修改的数据维护一个
锁
记录。这个锁
不是数据库里的锁。只要是通过框架发起的DML
,都要检查对相应的记录是否有写权限。也就是说框架是通过自己构建的锁
来实现事务的隔离性。而读是读未提交还是读已提交,则取决于框架是否针对select检查记录权限以及如果有锁
时是否阻塞这个读操作了。
还有一个重要注意事项,只要一个表使用了TCC
模型(无论是那种用法),那么业务里所有对这个表的访问都应当使用TCC
模型。否则,很有可能破坏TCC
事务试图维持的最终一致性。比如说当应用设计了冻结金额
这个有着业务锁
含义的字段时,任何时候判断可用余额的方法就要变为当前余额减去冻结金额的差值。
分布式事务中间件产品
DTX
最早用于蚂蚁拆分场景中,内部名称XTS
。后来产品化在蚂蚁金服科技官网上对外提供,名称DTX
,作为SOFA
的一部分。
DTX
的功能支持三种模式。
一个是
XA
,对应于传统DTP
模型,依赖数据库提供XA
接口,满足个别场景追求强一致的需求。一个是
TCC
,业务实现提交和回滚逻辑。这方面公开资料很多。一个是
FMT
,框架自动化实现提交和回滚逻辑。这方面公开资料也很多。
DTX
在事务隔离级别方面支持三种:读未提交(READ UNCOMMITTED
)、读已提交(无锁,读取快照)、串行化(读加锁,读写互斥)。
DTX
产品跟OceanBase结合时,可以享受OceanBase对两阶段提交的优化功能。这也是蚂蚁双十一时交易峰值能做到很高的一个原因。
GTS
GTS
支持多种数据库。对于某些不支持XA接口的数据库,或者XA接口有bug的数据库(如MySQL 5.5/5.6),GTS可以提供分布式事务支持。更多特性可以看阿里云官网介绍。
GTS
的原理也是补偿型事务TCC
模型,分为两种运行模式。
一个是
AT
自动化型,对应上面介绍的自动化框架用法。应用改造量很少,GTS自动拦截用户sql并保存快照数据。这方面公开资料很多。一个是
MT
模型,就需要业务去实现提交和回滚逻辑。这个目前还没有完全开源。MT 模式一方面是 AT 模式的补充。另外,更重要的价值在于,通过 MT 模式可以把众多非事务性资源纳入全局事务的管理中。
总结
分布式事务还有基于消息中间件的方案等等,跟本文主题关系不大就不提了。
分布式事务中间件的本质都是构建一个框架,拦截用户SQL,控制多个RM
节点的事务提交回滚逻辑。由此都有个共同的难点就是支持的SQL类型。单表SQL很容易支持,复杂一点的SQL就需要具体看看。如果要丰富SQL解析功能,可能离事务就越来越远。所以,个人觉得应用还是要尽可能的先行完成相关改造。如合理服务化、SQL尽可能的简单等
GTS和DTX都是在阿里和蚂蚁内部经过众多真实业务场景检验过的,并且都经历过一年一度的双11大促考验。
当然,为了避免“手里拿着锤子看什么都是钉子”问题,还是要补充几句。并不是所有的分布式事务场景都可以接受最终一致性
,也不是所有的事务场景就一定要是分布式事务。如过度服务化也可能会被诟病。
分布式数据库的分布式事务
DRDS
的分布式事务
DRDS
是一款分布式数据库中间件,支持两种模式的分布式事务。一个是强一致的,使用XA
两阶段提交流程;一个是最终一致的柔性事务(FLEXIBLE
)。其原理应该也类似GTS
的AT
模式。此外新增了两种模式 FREE
和2PC
。
DRDS
默认禁止在一个事务里修改多个数据库,分布式事务需要主动开启。
OceanBase
的事务
OceanBase是一款真正分布式关系型数据库,它只有一种类型的事务,无论是否跨节点。
OceanBase对分布式事务的判断标准是看是否修改了多个分区
。如果多个分区跨节点了,OceanBase内部会发起两阶段提交(2PC
)流程,是强一致的。OceanBase针对2PC
做了一些优化,极大的减少了业务提交等待时间。如果多个分区没有跨节点,会优化为单机事务。
不过OceanBase分布式事务的边界是租户
。租户的概念就是实例。当一个分布式事务跨越了租户的时候,还是要借助分布式事务中间件产品来实现。
此外,OceanBase 1.x版本不支持 XA
接口,OceanBase 2.x会支持。
后记
本文参考了阿里云和蚂蚁金服的公开的有关TCC
设计思想的分析和实际产品的特点。对于产品的使用方面网上资料很多,这里就都略过。所总结的都是工作中感觉疑惑最多的地方,也是客户在做分布式开发经常问到的问题。
个人总结,囿于实际开发经验不多,可能个别理解存在不当,欢迎指出。
参考
分布式事务入门指南 · 常用分布式事务解决方案 https://zhuanlan.zhihu.com/p/33639067
分布式数据库系统原理(第3版)
笑一笑
以上是关于说说数据库事务和开发(下)—— 分布式事务的主要内容,如果未能解决你的问题,请参考以下文章