分布式事务(一) 本地事务回顾

/ 分布式数据库 / 0 条评论 / 172浏览

什么是分布式事务

要了解分布式事务,首先需要了解什么是本地事务。

本地事务

本地事务,是指传统的单机数据库事务,必须满足ACID原则。

传统项目中,项目部署基本都是单点式,这种情况下,数据库本身的事务机制就能保证ACID的原则,这样的事务叫做本地事务。 其中原子性和持久性需要依靠undo和redo日志来实现。

undo和redo日志

数据库中数据从来不是最重要的,日志才是。

在数据库系统中,既有存放数据的文件,也有存放日志的文件。日志在缓存中也有log buffer,也有磁盘log file。
MySQL的日志文件,有两种与事务有关,undo log和redo log

undo日志

介绍

数据库事务具备原子性(Atomicity),如果事务执行失败,需要把数据回滚。
事务同时还具备持久性(durability),事务对数据做的更改完全保存在数据库,不能因为故障而丢失。
原子性可以使用undo log来实现。

原理

undo log的原理很简单,为了满足事务的原子性,在操作任何数据之前都会将数据备份到undo log中然后再对数据进行更改。如果执行出错或者手动回滚后,系统可以利用undo log中备份的数据恢复到事务开始之前的状态。 数据库写入数据到磁盘之前,会把数据先缓存在内存中。事务提交才写入到磁盘。

实例

用undo log实现原子性和持久性的简化过程: 假设有A=1, B=2两条数据。

实例.png

事务提交前,会把修改数据记录到磁盘中,只要事务提交了,数据肯定持久化了。

每次对数据库修改,都会把修改前记录保存在undo log中,需要回滚时可以直接读取undo log恢复数据。

缺陷

每个事务提交前都需要将数据和undo log写入到磁盘,导致大量磁盘IO,性能差。

redo日志

和undo log相反,redo log是对新数据的备份,在事务提交之前,只要将redo log持久化即可,不需要将数据持久化,减少IO操作。

实例

用undo log + redo log 实现原子性和持久性的简化过程: 假设有A=1, B=2两条数据。

  1. 事务开始
  2. 记录A=1到undo log buffer
  3. 修改A=3
  4. 记录A=3到redo log buffer
  5. 记录B=2到undo log buffer
  6. 修改B=4
  7. 记录B=4到redo log buffer
  8. 将undo log写入redo log
  9. 将redo log写入磁盘
  10. 事务提交

整个过程数据并未持久化,因为数据已经写入到redo log中,而redo log已经写入到磁盘中,因此只要进行到(9)后,事务是可以提交的。

如果在事务提交前故障,通过undo log恢复数据,如果undo log还未写入(8),那么数据尚未持久化,无需回滚。

因为redo log已经持久化,因此是否写入硬盘已经不重要了。但是一般为了避免内存数据与数据库数据不一致,在事务提交后或者会固定频率刷新到数据库中。

问题

  1. 数据库数据写入是随机的,性能差
  2. redo log在初始化时会开辟一段连续空间,写入是顺序IO,性能高
  3. 实际上undo log并不是直接写入磁盘,而是写入到redo log buffer中,当redo log持久化时,undo log也顺便持久化了。 因此事务在提交前只需要将redo log持久化即可。
    另外redo log并不是写入一次就持久化一次,redo log在内存中有redo log buffer缓冲池,在最终数据库事务提交时一次性持久化,减少IO次数。
  1. 恢复时,只重做已经提交了的事务。
  2. 恢复时,重做所有事务包括未提交的和回滚的事务,然后通过undo log回滚哪些未提交的事务。 innodb采用了方案2,因此undo log要在redo log前持久化。

总结

undo log记录旧数据,redo log记录最新数据。

分布式事务

跨数据源

对数据库进行水平或垂直拆分,将原表数据拆分成数据库分片。于是就产生了跨数据库事务问题。 因为ACID是数据库内部的,不能解决多个数据库实例之间的问题。 multiDS

跨服务

对单体项目进行微服务拆分后,将原有的Spring Transaction Manager拆分到每个微服务之间都有自己的Spring事务管理器。导致出现分布式事务问题。

microService

分布式系统的数据一致性问题

当出现部分事务成功,部分事务失败时,业务数据就会不一致。
例如电商下单场景,包括以下几个行为:

  1. 创建订单
  2. 扣减库存
  3. 扣减余额 完成上面三个动作需要三个微服务和三个不同的数据库,一旦其中任何一个失败,其它的服务之间都无法感知,就会造成数据的不一致问题。
    这正是分布式事务要解决的问题。 microServiceSimple