分布式事务入门学习

一、本地事务

什么是事务?先从本地事务了解。本地事务,是指传统的单机数据库事务,必须具备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的隔离级别。所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,**该级别可以防止脏读、不

可重复读以及幻读**。

什么是脏读?幻读?不可重复读?

  • 脏读(Drity Read):某个事务已更新一份数据,另一个事务在此时读取了同一份数据,由于某些原因,前一个RollBack了操作,则后一个事务所读取的数据就会是不正确的。

  • 不可重复读(Non-repeatable read):在一个事务的两次查询之中数据不一致,这可能是两次查询过程中间插入了一个事务更新的原有的数据。

  • 幻读(Phantom Read):在一个事务的两次查询中数据笔数不一致,例如有一个事务查询了几列(Row)数据,而另一个事务却在此时插入了新的几列数据,先前的事务在接下来的查询中,就会发现有几列数据是它先前所没有的。

注意:

- 这里需要注意的是:Mysql 默认采用的 REPEATABLE_READ隔离级别 Oracle 默认采用的 READ_COMMITTED隔离级别。

- 事务隔离机制的实现基于锁机制和并发调度。其中并发调度使用的是MVVC(多版本并发控制),通过保存修改的旧版本信息来支持并发一致性读和回滚等特性。

- 因为隔离级别越低,事务请求的锁越少,所以大部分数据库系统的隔离级别都是**READ-COMMITTED(读取提交内容):**,但是你要知道的是InnoDB 存储引擎默认使用

**REPEATABLE-READ(可重读)**并不会有任何性能损失。

- InnoDB 存储引擎在 **分布式事务** 的情况下一般会用到**SERIALIZABLE(可串行化)**隔离级别。

 

二、分布式事务

什么是分布式事务?主要有以下三种:

——跨数据库的分布式事务

——跨服务的分布式事务

——综合情况

1、跨数据源的分布式事务

例如淘宝,每年几十亿上百亿的单子,一个数据库肯定承受不了,这个时候就需要对数据库进行分库分表。

 

2、跨服务的分布式事务

 

如上图所示,单个服务被拆分成三个单独的服务,共享一个数据源时,即为跨服务的分布式系统。

每个服务有自己的JVM、Spring以及事务管理器进行管理,但是服务与服务之间的事务无法实现。

3、综合情况

综合情况即既跨数据库也跨服务。

应用场景: 

  当我们的系统采用了微服务架构后,一个电商系统往往被拆分成如下几个子系统:商品系统、订单系统、支付系统、积分系统等。整个下单的过程如下:

  用户通过商品系统浏览商品,他看中了某一项商品,便点击下单

  此时订单系统会生成一条订单

  订单创建成功后,支付系统提供支付功能,同时扣减商品库存

  当支付完成后,由积分系统为该用户增加积分

如下图所示完成下单付款业务需要访问三个不同的微服务和三个不同的数据库。每个服务满足自己的ACID。

 

4、CAP定理

  • 什么是CAP定理呢?

  •  

    1998年,加州大学的计算机科学家Eric Brewer提出,分布式系统有三个指标: 

    C--Consistency 一致性。

    A--Availability 可用性。

    P--Partition tolerance 分区容错性。

      实际上一个分布式系统无法同时满足这三个指标。

4.1一致性

如下图所示,G1、G2为两个不同的服务器,client为客户端,v0为原始数据值,当客户端发起请求将v0改为v1时,请求到的是服务器G1,此时G1服务器上的数据为v1而G2服务器上的数据还是v0,接下来若用户进行读操作时请求到的服务器还是G1,得到数据v1。这就叫一致性。

 

问题是,若读数据时的请求被负载均衡到了G2服务器,由于G2的值没有发生变化,因此返回的是v0,G1和G2读操作的结果不一致,这就不满足一致性了。

 

为了让G2也能变为v1,就要在G1写操作的时候,让G1向G2发送一条消息,要求G2也变成v1。这样的话,用户向G2发起读操作,也能得到v1。

 

4.2可用性

什么是可用性?

只要服务器接收到请求,就必须给出回应,不管是正确的还是错误的。

4.3分区容错性

分区:一个一个的网络,在全球各地都有服务器,跨区的服务器因为网络的影响,数据传输会有延迟等影响,虽然现在网络非常发达,但是网络波动的影响始终无法避免。

总结:

要保证一致性,服务器之间同步时,G2不可用,故一致性与可用性无法同时满足。

 

拓展:

怎样才能同时满足CA?

----除非是单点架构

何时需要满足CP?

----对一致性要求高的场景。例如我们的Zookeeper就是这样的,它是一个集群,在服务节点间数据同步时,服务对外不可用。

何时需要满足AP?

----对可用性要求高的场景。例如Eureka,必须保证注册中心随时可用,不然拉取不到服务就可能出现问题。

 

4、Base理论

为了解决ca无法同时满足,提出了BAE理论。什么是BASE理论呢?

  BASE理论:

    BASE是三个单词的缩写:

    - Basically Available(基本可用)

    - Soft state(软状态)

    - Eventually consistent(最终一致性)

基本可用:同步数据的短时间内不可用即为基本可用

最终一致:将数据同步时间缩短到可接受的范围内,比如1ms——弱一致,需要等一段时间再一致,但最终是一致的

 

由上面的两种思想,延伸出了很多的分布式事务解决方案:

  - XA
  - TCC
  - 可靠消息最终一致
  - AT

事务的入门学习就到这了,下一篇介绍分布式事务解决方案。

posted @ 2021-10-22 11:11  ThisTFF  阅读(142)  评论(0)    收藏  举报