凤凰架构记02-事务
事务的基本特性
A原子性:一系列操作为一个原子,都成功或都撤销
I隔离性:不同操作之间读写数据相互独立,不会彼此影响
D持久性:成功提交的数据都会被持久化,不会丢失
C一致性:一致性为最终目的,所有事物处理最终的数据状态保持一致
内部一致性:单数据源有确定并发事务的读写顺序
外部一致性:多个不同数据源,甚至多服务并发执行多个事务
强一致性:外部一致性通常无法保证完全一致,而是不同强度的一致性保证
本地事务-依赖DB实现原子性和持久性
undoLog回滚日志:用于事务回滚或崩溃恢复时,对已写入的数据“擦除”
redoLog重做日志:用于崩溃恢复时,未完成写入磁盘的日志内容的”重做“
db崩溃恢复时经历的三个阶段
分析阶段:从最后一次检查点扫描日志,找出没有End Record的事务,组成待恢复事务集合。包含Transaction Table和Dirty Page Table两部分
重做阶段:将待恢复事务集合中,所有提交成功(包含Commit Record)的日志,将数据写入磁盘,增加End Record
回滚阶段:将待恢复事务集合中,剩下的需要被回滚的Loser(未commit成功的),根据undolog将提前写入磁盘的数据改啊协会去,实现回滚
本地事务-依赖DB实现隔离性
写锁:排他锁,只允许拥有写锁的事务进行读写操作,不可加其他锁
读锁:共享锁,同一数据允许加多个读锁,拥有读锁的事务可以读操作,不可加写锁。当前事务独享读锁,则可升级为写锁
范围锁:对某个范围加排他锁,范围内其他事务无法增删改查数据 select * from table where id > 10;
串行化级别:最高强度隔离性
可重复读级别:有读锁写锁,无范围锁。会导致幻读,范围查询条件下,两个事务查出结果集不同
读提交级别:有读锁写锁,但是读锁在一次查询后释放,无范围锁。会造成不可重复读,同一数据在当前事务两次读操作中,被其他事务中途修改,会两次读到不同结果
读未提交:只有写锁,由于读不需要加读锁,事务会读到其他事务加了写锁正在写的数据,该数据可能未commit,造成脏读
不同隔离级别造成脏读,幻读,不可重复读都是表面现象,本质都是以锁的机制来实现不同隔离性
多版本并发控制-MVCC
MVCC是加在读提交和可重复读隔离级别上的一种机制1,只针对“读+写”的场景
基本思路:数据库的增删改操作以版本控制的方式实现,任何和修改不会直接覆盖旧数据,而是产生一条新数据
数据库每行记录存在两个字段create_version,delete_version,存储的值为事务id
当事务插入数据时:create_version记录该事务id,delete_version为空
当事务删除数据时:delete_version记录该事务id,create_version为空
当事务修改数据时:删除旧数据,插入新数据。原数据复制一份,原数据delete_version记录该事务id,create_version为空。复制后新数据create_version记录该事务id,delete_version为空
当有其他事务读取该数据时:根据隔离级别来读取。可重复读,则读取create_version <= 当前事务id的版本的最新一条。读提交,则总是读create_version最新的一条
全局事务-2PC
全局事务指单个服务使用多个数据源,将事务提交分为两阶段
准备阶段:协调者询问所有事物参与者是否prepared(在redolog中提交操作内容,但不写入最后一条Commit Record,仍然持有锁,不释放隔离性)
提交阶段:准备阶段完成后,协调者自己先commit,然后让所有事物参与者commit。如果准备阶段有参与者non-prepared或超时未回复,则发送给所有参与者abord指令,根据undolog进行回滚
(其中协调者参与者都是数据库DB自己,不需要程序介入,协调者由参与者中选举)
2PC的问题: 1.不允许协调者宕机,如果协调者无法发送指令,所有参与者都将等待 2.性能较差,期间经过两次远程服务调用,三次持久化,均需要等所有参与者完成
3PC: 吧2PC的准备阶段拆分为CanCommit和PreCommit,提交阶段为DoCommit
CanCommit是询问阶段,询问每个参与者自身数据库状态是否可以完成事务。目的是提高事务成功概率,尽量保证不会在明治事务无法完成的情况白白占用资源
分布式事务
多个服务访问多个数据源
CAP:分布式系统三特性 ,一致性,可用性,分区容错性
其中分区容错性未分布式系统天然属性,可用性为分布式系统的根本价值,所以大多数NoSQL和分布式缓存框架都是AP系统,不保证强一致性,而保证最终一致性
可靠事件队列:
1.将分布式事务中优先级最高的,最容易出错的事务放在第一步,当作本地事务来执行事务1
2.事务1操作完后,事务1向本地数据库消息表插入一条分布式消息 ,该消息处于执行中状态。事务1commit,发送消息到MQ
3.由MQ循环通知下一个服务执行事务2,直到事务2执行成功,更新消息表,事务2完成
4.由MQ循环通知下一个服务执行事务3,直到事务3执行成功,更新消息表,整体事务完成
遵循最大努力交付原则(例如TCP协议中,未收到ACK自动重新发包的可靠性保障)
没有失败回滚的概念,在事务1提交成功后续事务尽最大努力成功,除非人工介入
由于重复通知的机制,需要保证事务操作的幂等性
TCC事务:
1.Try,尝试阶段,完成过可执行性的检查,预留业务资源进行冻结,保障隔离性(比如库存仅剩10件,事务A通过Try预留6件,事务B同时执行Try预留5件会失败)
2.Confirm,确认阶段,对预留的资源执行操作。(可能重复执行,需具备幂等性)
3.Cancel,取消阶段,释放Try锁定的资源。(可能重复执行,需具备幂等性)
TCC类似于两阶段提交,
三个事务先同时执行Try阶段冻结资源,若任一事务检查或冻结失败,则所有事物执行Cancel。
三个事务再同时执行Confirm阶段业务操作,若有任一事务失败,则重复执行Confirm进行最大努力交付。
如有事务进行Cancel阶段失败,则重复执行Cancel进行最大努力交付。
TCC通过代码层面而非基础设施层面实现,具有更高的开发成本和业务侵入性,较高的灵活性设置冻结力度。
(阿里中间件Seata支持TCC事务)
SAGA事务:
将大事务分解为一系列顺序执行的子事务T1,T2,T3,T4... 每个子事务为原子行为
每个子事务都有对应的补偿动作C1,C2,C3,C4...
如果T1至Tn顺利提交,则事务成功,
否则,
1.正向恢复:Ti提交失败,则对Ti不断重试,最大努力交付,后续子事务等待Ti成功后执行。 (适用于扣款等强制要求成功场景)
2.反向恢复:Ti提交失败,则按顺序执行Ci,Ci-1,...C1的补偿操作,补偿操作要求最大努力交付,必须成功
SAGA必须要求所有事物都提交或者都补偿,
SAGA还具有日志机制,保证SAGA系统崩溃恢复后可以追踪到事务执行情况,继续执行。
(阿里中间件Seata支持SAGA事务)