分布式事务入门学习
一、本地事务
什么是事务?先从本地事务了解。本地事务,是指传统的单机数据库事务,必须具备ACID原则。
1、ACID
那什么是ACID呢?
A(atomicity)-- 原子性:在事务的执行过程中,事务包含了多个操作,要求所有操作,要么全部成功,要么全部失败,即要么全都执行,要么全都不执行。
C(consistency)-- 一致性:事务执行的过程中要保证数据执行的一致,即多个事务之间执行前和执行后的一致。
I(isolation)-- 隔离性:事务的执行是相互独立的,它们不会相互干扰,一个事务不会看到另一个正在运行过程中的事务的数据。
D(durability)-- 持久性:事务一旦提交,数据库中的数据得保证不丢失,即使服务器宕机或者停电等故障发生。
其实,原子性、隔离性和持久性都是数据库内部机制可以保证的,而一致性是需要我们去实现的,也是事务的最终目标。
2、 如何保证原子性和持久性
数据库事务具备原子性(Atomicity),如果事务执行失败,需要把数据回滚。
事务同时还具备持久性(Durability),事务对数据所做的变更就完全保存在了数据库,不能因为故障而丢失。
原子性利用undo日志来实现。
2.1 Undo Log

- 安全和性能问题
- 如何保证持久性?
事务提交前,会把修改数据到磁盘前,也就是说只要事务提交了,数据肯定持久化了。
- 如何保证原子性?
每次对数据库修改,都会把修改前数据记录在undo log,那么需要回滚时,可以读取undo log,恢复数据。
- 若系统在G和H之间崩溃
此时事务并未提交,需要回滚。而undo log已经被持久化,可以根据undo log来恢复数据
- 若系统在G之前崩溃
此时数据并未持久化到硬盘,依然保持在事务之前的状态
缺陷:每个事务提交前将数据和Undo Log写入磁盘,这样会导致大量的磁盘IO,因此性能很低(影响数据库性能最主要的因素: 磁盘和cpu)。
磁盘IO示例图:

磁盘读写数据前会不断的转动寻址,寻址操作是影响磁盘效率的关键。
如果能够将数据缓存一段时间,就能减少IO提高性能。但是这样就会丧失事务的持久性。因此引入了另外一种机制来实现持久化,即Redo Log
2.2 Redo Log

- 安全和性能问题
- 如何保证原子性?
如果在事务提交前故障,通过undo log日志恢复数据。如果undo log都还没写入,那么数据就尚未持久化,无需回滚
- 如何保证持久化?
大家会发现,这里并没有出现数据的持久化。因为数据已经写入redo log,而redo log持久化到了硬盘,因此只要到了步骤`I`以后,事务是可以提交的。
- 内存中的数据库数据何时持久化到磁盘?
因为redo log已经持久化,因此数据库数据写入磁盘与否影响不大,不过为了避免出现脏数据(内存中与磁盘不一致),事务提交后也会异步地将内存数据刷入磁盘(也可以按照固设定的频率刷新内存数据到磁盘中)。
- redo log何时写入磁盘
redo log会在事务提交之前,或者redo log buffer满了的时候写入磁盘
这里大家可能会想两个问题:
问题1:之前是写undo和数据库数据到硬盘,现在是写undo和redo到磁盘,似乎没有减少IO次数
- 数据库数据写入是随机IO,性能很差。
- redo log在初始化时会开辟一段连续的空间,写入是顺序IO,性能很好。
- 实际上因为undo log不是顺序IO,故存在磁盘IO问题,真实情况下并不是直接写入磁盘,而是先写入到redo log buffer中,当redo log持久化时,undo log就同时持久化到硬盘了。
因此事务提交前,只需要对redo log持久化即可。
另外,redo log并不是写入一次就持久化一次,redo log在内存中也有自己的缓冲池:`redo log buffer`。每次写redo log都是写入到buffer,在提交时一次性持久化到磁盘,减少IO次数。
问题2:redo log 数据是写入内存buffer中,当buffer满或者事务提交时,将buffer数据写入磁盘。
redo log中记录的数据,有可能包含尚未提交事务,如果此时数据库崩溃,那么如何完成数据恢复?
数据恢复有两种策略:
- 恢复时,只重做已经提交了的事务
- 恢复时,重做所有事务包括未提交的事务和回滚了的事务。然后通过Undo Log回滚那些未提交的事务
Inodb引擎采用的是第二种方案,因此undo log要在 redo log前持久化
拓展:
什么是随机IO?什么是顺序IO?
随机IO----先寻址再写入,寻址耗费更多的时间。
顺序IO----在磁盘上开辟连续的空间,无需重复寻址。
例:数据库表数据采取的是顺序写(索引b+树算法、数据量大)
总结一下:
- undo log 记录更新前数据,用于保证事务原子性
- redo log 记录更新后数据,用于保证事务的持久性
- redo log有自己的内存buffer,先写入到buffer,事务提交时写入磁盘
3、 如何保证隔离性
SQL 标准定义了四个隔离级别:
- READ-UNCOMMITTED(读取未提交): 最低的隔离级别,允许读取尚未提交的数据变更,**可能会导致脏读、幻读或不可重复读**。
- READ-COMMITTED(读取已提交):允许读取并发事务已经提交的数据,**可以阻止脏读,但是幻读或不可重复读仍有可能发生**。
- REPEATABLE-READ(可重复读):对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,**可以阻止脏读和不可重复读,但幻读仍有可能发生**。
- SERIALIZABLE(可串行化):最高的隔离级别,完全服从ACID的隔离级别。所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,**该级别可以防止脏读、不
可重复读以及幻读**。
- 
- 
不可重复读(Non-repeatable read):在一个事务的两次查询之中数据不一致,这可能是两次查询过程中间插入了一个事务更新的原有的数据。 
- 
注意:
- 这里需要注意的是:Mysql 默认采用的 REPEATABLE_READ隔离级别 Oracle 默认采用的 READ_COMMITTED隔离级别。
- 事务隔离机制的实现基于锁机制和并发调度。其中并发调度使用的是MVVC(多版本并发控制),通过保存修改的旧版本信息来支持并发一致性读和回滚等特性。
- 因为隔离级别越低,事务请求的锁越少,所以大部分数据库系统的隔离级别都是**READ-COMMITTED(读取提交内容):**,但是你要知道的是InnoDB 存储引擎默认使用
**REPEATABLE-READ(可重读)**并不会有任何性能损失。
- InnoDB 存储引擎在 **分布式事务** 的情况下一般会用到**SERIALIZABLE(可串行化)**隔离级别。
二、分布式事务
什么是分布式事务?主要有以下三种:
——跨服务的分布式事务
如上图所示,单个服务被拆分成三个单独的服务,共享一个数据源时,即为跨服务的分布式系统。
每个服务有自己的JVM、Spring以及事务管理器进行管理,但是服务与服务之间的事务无法实现。
应用场景:
当我们的系统采用了微服务架构后,一个电商系统往往被拆分成如下几个子系统:商品系统、订单系统、支付系统、积分系统等。整个下单的过程如下:
用户通过商品系统浏览商品,他看中了某一项商品,便点击下单
此时订单系统会生成一条订单
订单创建成功后,支付系统提供支付功能,同时扣减商品库存
当支付完成后,由积分系统为该用户增加积分
- 
什么是CAP定理呢?
![]()  - 1998年,加州大学的计算机科学家Eric Brewer提出,分布式系统有三个指标: 
C--Consistency 一致性。
A--Availability 可用性。
P--Partition tolerance 分区容错性。
 
为了让G2也能变为v1,就要在G1写操作的时候,让G1向G2发送一条消息,要求G2也变成v1。这样的话,用户向G2发起读操作,也能得到v1。
 
BASE理论:
BASE是三个单词的缩写:
- Basically Available(基本可用)
- Soft state(软状态)
- Eventually consistent(最终一致性)
基本可用:同步数据的短时间内不可用即为基本可用
最终一致:将数据同步时间缩短到可接受的范围内,比如1ms——弱一致,需要等一段时间再一致,但最终是一致的
由上面的两种思想,延伸出了很多的分布式事务解决方案:
  - XA
  - TCC
  - 可靠消息最终一致
  - AT
事务的入门学习就到这了,下一篇介绍分布式事务解决方案。
 
                    
                     
                    
                 
                    
                



 

 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号