在上一篇博客里,<a href="http://www.jianshu.com/p/e9004c0db5cc">分布式事务系统设计</a>,主要是从中间件的角度去考虑如何实现一个分布式事务的中间件,文章只写了一个大概的设计,详细并没有写出来;
根据业务场景不同,系统定义了多种事务模式,主要包括标准模式、自定义模式。每种事务模式对应了自己的RM实现。当有新的事务模式需求出现,我们可以开发一个新的满足新的分布式事务通信协议的RM。
一个分布式事务中间件大概应该存在两种模式:
标准模式
读未提交(read uncommitted)和读已提交(read committed),其中读未提交是缺省设置
Read uncommitted
标准模式在读未提交的隔离级别下,存在脏写,脏读的风险。
脏写出现的概率很低,只有以下几个条件同时满足才会出现:
A) 事务1修改了T表的一行记录;
B) 事务2接着也修改了T表的同一行记录;
C) 事务1因为某种原因需要回滚
上面3个条件同时满足时,事务1回滚失败,出现脏写情况,这时事务系统告警模块会通知到业务人员,需要根据系统的Undo日志进行恢复。为什么说发生概率很低?一个分布式事务完成时间通常是毫秒级,两个分布式事务在这么短时间内并发修改同一行记录概率很低,假设万分之一;事务失败的概率假设也是万分之一,则出现脏写的概率是亿分之一,可以说概率非常低了。
脏写出现概率极低,即使出现,由于在业务库上记录了详细的undo/redo log,手工恢复代价也通常较小;所以脏写问题对绝大多数应用场景的负面影响可以忽略,建议采用这种读未提交的隔离级别,比读已提交级别有更好的性能。
脏读指用户读到了分布式事务的中间状态。对于成功的分布式事务,这种中间状态基本可以认为是没有负面影响的;对于失败的分布式事务,也只有很少的场景下会有负面影响,因为这种中间状态持续时间很短(通常是毫秒级),用户很难感知。即使用户看到了中间状态,通常也是能够理解的,因为他知道自己事务失败了。
标准模式的“读未提交”隔离级别是的主流应用,它兼顾了易用性与较高的性能,适用于多数分布式事务场景Read committed
标准模式在读已提交的隔离级别下,将对所管理的所有读已提交的分布式事务,进行写入记录的行级锁检查,当发现锁冲突,采用重试策略(最长550ms),如果经过重试后还是拿不到锁,将拒绝后进的事务,并将异常抛出给上层业务系统。
需要说明的是:仅能保证被管理的读已提交的事务间的Read Committed的隔离性。
隔离级别可以通过的RM配置项进行设置。
读已提交隔离级别比读未提交增大了开销(10%~20%),如果select语句也采用读已提交则会有更大开销。如4.1.1所分析,实际业务场景下,读未提交是足够安全的,不建议使用读已提交隔离级别。
自定义模式
自定义模式就是业界比较流行的Tcc模式
TCC分别对应Try/Prepare、Confirm/Commit和Cancel/Rollback三种操作,这三种操作的业务含义如下
- Try/Prepare:预留业务资源
- Confirm/Commit:确认执行业务操作
- Cancel/Rollback:取消执行业务操作
这种模式的话,不依赖于事务系统去推进各资源管理器去推进各分支更新事务状态
优点:
- 解决了跨应用业务操作的原子性问题,在诸如组合支付、账务拆分场景非常实用。
- TCC实际上把数据库层的二阶段提交上提到了应用层来实现,对于数据库来说是一阶段提交,规避了数据库层的2PC性能低下问题。
缺点: - TCC的Try、Confirm和Cancel操作功能需业务提供,开发成本很高