分布式事务产生背景
一般情况下,讲分布式事务都是说数据库的事务(database transaction)。后来一些非数据库的事务也借鉴了此概念。
事务:运行在数据库上的逻辑单元,一切命令SQL具有原子性的操作特点(要么完全成功的执行,要么完全撤销不执行)。
数据库事务要满足的要求ACID
- 原子性Atomic
- 事务必须是原子的工作单元
- 隔离性Isolation
- 并发事务所做的修改必须和其它事务所做的修改时隔离的
- 一致性Consistent
- 事务完成时,必须使所有数据都保持一致状态
- 持久性Duration
- 事务完成之后,对系统的影响是永久性的
隔离性通过锁实现,其他特性通过数据库的事务日志来实现。涉及到IO操作。这就是事务操作的性能瓶颈所在。
Mysql里的事务处理过程
- 记录
redo
和undo
的log文件,确保日志在磁盘上的持久化 - 更新数据记录
- 提交事务,redo写入commit记录
分布式事务
分库分表
- 数据库拆分
- 读库
- 写库
- SOA化
- 单机可以支撑的系统做拆分
- 用户中心
- 库存中心
- 订单中心
- 商品中心
- …
- 单机可以支撑的系统做拆分
经典的X/OpenDTP事务模型
X/Open Distributed Transaction Processing Reference Model分布式事务处理模型。
X/Open是一个组织机构,定义出的一套分布式事务标准,定义了规范的API接口,由具体厂商负责实现。
J2EE 遵循了X/open DTP规范,设计并实现了java里面的分布式事务编程接口规范-JTA。
XA是X/Open DTP定义的中间件与数据库之间的接口规范。 XA接口函数由数据库厂商提供。
X/OpenDTP角色
- AP
- application
- RM
- resouces manager
- 资源管理器
- 一般情况下是数据库或者是文件系统
- XA定义的接口
- TM
- transaction manager
- 事务管理器,事务协调者
- 负责接收来自AP的XA事务指令,并协调RM执行XA指令
二阶段提交(2PC)
2PC(two -phase-commit), 用来保证分布式事务的完整性。
阶段一:提交事务请求(投票)
- TM向所有的AP发送prepare请求(事务内容,事务的预提交,相当于把耗时的操作先执行),询问是否可以执行事务的提交操作,并等待各个AP的响应
- 执行事务
- 各个AP节点执行事务操作,将undo和redo信息记录到事务日志中,尽量把提交过程中所消耗时间的操作和准备都提前完成后确保后续事务提交的成功率
- 各个AP向TM反馈事务询问的响应
- 各个AP成功执行了事务操作,那么反馈给TM
yes
的response - 如果AP没有成功执行事务,就反馈TM
no
的response
- 各个AP成功执行了事务操作,那么反馈给TM
阶段二:执行事务提交
- TM向AP发送commit请求(提交事务请求)
- 执行提交事务
- 全部返回
yes
- 全部返回
- 执行提交事务
- TM向AP发送abort请求(中断事务提交)
- 只要任何一个返回返回了
no
,协调者就认为这个事务需要回滚 - 按照undo日志回滚,回滚后释放事务资源(包括锁),返回
ack
给TM
- 只要任何一个返回返回了
假设一个事务的提交过程总共需要30s,其中prepare操作需要28s(事务日志落地磁盘及各种IO操作),而真正commit只需要2s。那么,commit阶段发生错误的概率和prepare相比,2/28(<10%).只要第一个阶段成功,那么commit阶段出现失败的概率就非常小,大大增加了分布式事务的成功概率。
2PC存在的问题
- 数据一致性问题
- 在二阶段,参与者和协调者都挂了,挂了的参与者AP在挂之前已经执行了一个操作,但是由于挂了,其他机器不知道执行了什么操作。
- TM可以通过leader选举或其它方式来产生,新TM需要保证与AP保证数据一致性, 如果三个AP中两个执行了commit,另外一个在挂掉之前执行了rollback,这时候就会产生数据一致性的问题。
- 在二阶段,参与者和协调者都挂了,挂了的参与者AP在挂之前已经执行了一个操作,但是由于挂了,其他机器不知道执行了什么操作。
- 同步阻塞
- TM发送请求后,需等待AP返回响应,这个过程中,TM处于等待状态,AP返回yes,TM发送commit后挂了,AP处于阻塞状态,会导致性能问题。
三阶段提交3PC(three phase commit)
- canCommit
- 有没有时间去钓鱼
- preCommit
- 暂定某天去钓鱼并告知所有人
- doCommit
- 到了预定的那天,一起去钓鱼
引入超时机制,preCommit没有响应,则认为该事务失败。
增加询问的操作,确保挂掉后能正常恢复。
但其实依旧存在一致性问题,Zookeeper那采用多阶段提交,可以查看源码一探究竟。
分布式事务的常见实现
- JOTM(java open transaction manager)
- Object Web开源的基于J2EE的JTA规范的实现
- Atomikos
- 本身是一个商业项目,开源
- 常用
互联网行业分布式事务解决方案
柔性事务:相对于强一致性的ACID刚性事务
- 最终一致性
- eBay模式
- BASE理论
- 无需保证数据在某一点的强一致性,保证最终一致性
- BASE理论
- eBay模式
- TCC事务
- 补偿机制
消息重复消费解决方案
- 幂等校验
- 数据库mq_reciver,key为消息的唯一值
- 发送消息在数据库里插入
- 数据库mq_reciver,key为消息的唯一值
- 日志表、状态判断
- 记录消费表
- msgId、status
- 处理后更改status
- 记录消费表
保证最终一致性的模式
- 查询模式
- 提供接口,返回当前操作是否成功的状态
- 衰减查询
- 补偿模式
- 即在事务链中的任何一个正向事务操作,都必须存在一个完全符合回滚规则的可逆事务。
- 自动恢复
- A调B,失败了。自动重试。
- 衰减查询后的结果一直是失败的,自动对原有操作做回滚。
- 通知运营
- 自动恢复不了,成本较大,通过运营系统人工补偿
- 通知技术
- 监控、预警
- 修复数据
- 自动恢复
- 即在事务链中的任何一个正向事务操作,都必须存在一个完全符合回滚规则的可逆事务。
- TCC事务模型(支付宝的DTS事务架构)
- 放宽了数据库强一致性约束,保证数据最终一致性的方案
- 三个阶段
- TRYING—lock(尝试执行业务)
- 完成所有业务检查(一致性)
- 预留必须业务资源(准隔离性)
- CONFIRMING—commit(确认执行任务)
- 真正执行业务
- 不做任何业务检查
- 只使用Try阶段预留的业务资源
- Confirm操作满足幂等性
- CANCELING—rollback(取消执行业务)
- 释放Try阶段预留的业务资源
- Cancel操作满足幂等性
- TRYING—lock(尝试执行业务)
- 开源框架(可以整合dubbo)
- 最大努力通知型
- 同步
- 异步
- 查询掉单