目录
1.事务基本概念
1.1 什么是事务
事务是恢复和并发控制的基本单位,事务有四个特性(ACID),原子性(Atomicity),一 致性(Consistency),隔离性(Isolation),持久性(Durability)。本文主要围绕分布式环境下的事务控制进行分析。
1.2 事务经典场景
假设这样一个场景:
A 给 B 转账 100,流程步骤如下:
1、A 减 100
2、B 多 100。
如果第一步骤执行后,系统崩溃掉了。会怎么样呢?
问题:A 被减掉了 100,但 B 的钱未能加 100. 此时,A + B 的金钱总额凭空少了 100。数据不一致了。
解决思路呢?我们希望步骤 1 和步骤 2 能够绑定在一起执行,不可分;并且在步骤 1 和步骤 2执行的过程中,尽量规避中间状态。即谓事务。
事务在解决上述问题中,提出了原子性、一致性、隔离性、持久性四种特性。
1.2.1 原子性
原子性就是不可拆分的特性,要么全部成功然后提交(commit),要么全部失败然后回滚(rollback)。若开启事务,在上述场景就不会出现 A 少 100 成功,B 多 100 失败 这种情况。MySQL通过Redo Log重做日志实现了原子性,在将执行SQL语句时,会先写入redo log buffer,再执行 SQL 语句,若 SQL 语句执行出错就会根据 redo log buffer 中的记录来执行回滚操作,由此拥有原子性。
1.2.2 一致性
一致性指事务将数据库从一种状态转变为下一种一致的状态。比如有一个字段 name 有唯一索引约束,那么在事务前后都不能有重复的 name 出现违反唯一索引约束,否则回滚。在上述场景中即金钱总数总是 200,不能凭空增加减少。MySQL 通过 undo Log 实现一致性,执行 SQL 语句时,会先写入 undo log 再写入 redo log buffer。undo 是逻辑日志,会根据之前的 SQL 语句进行相应回滚,比如之前是 insert 那么回滚时会执行一个 delete,一个 update会执行 一个相反的 update。并且除了回滚,undo log 还有一个作用是 MVCC,当用户读取一行记录时,若该记录已经被其他事务占用,当前事务可通过 undo 读取之前的行版本信息,实现非锁定读取。并且 undo log 也会产生 redo log,因为 undo log 也需要持久性的保护。
1.2.3 隔离性
首先介绍如果没有隔离性会发生的 4 种情况
丢失更新
A 事务撤销时,把已经提交的 B 事务的更新数据覆盖了。这种错误可能造成很严重的问题,通过下面的账户取款转账就可以看出来,MySQL 通过三级封锁协议的第一级解决了丢失更新,事务 T 要修改数据 A 时必须加 X 锁,直到 T 结束才释放锁。
脏读
脏读主要是读取到了其他事务的数据,而其他事务随后发生回滚。MySQL 通过三级封锁协议的第二级解决了脏读,在一级的基础上,要求读取数据 A 时必须加 S 锁,读取完马上释放 S 锁。
不可重复读
不可重复读是读取到数据后,随后其他事务对数据发生了修改,无法再次读取。MySQL通过三级封锁协议的第三级解决了不可重复读。在二级的基础上,要求读取数据 A 时必须加 S 锁,直到事务结束了才能释放 S 锁。
幻读
幻读是读取到数据后,随后其他事务对数据发生了新增,无法再次读取。在 InnoDB 引擎Repeatable Read 的隔离级别下,MySQL 通过 Next-Key Lock 以及 MVCC 解决了幻读,事务中分为当前读以及快照读。
1.2.4 持久性
一旦事务提交,则其所做的修改就会永久保存到数据库中。此时即使系统崩溃,修改的数据也不会丢失。具体实现原理就是在事务 commit 之前会将,redo log buffer 中的数据持久化到硬盘中的 redo log file,这样在 commit 的时候,硬盘中已经有了我们修改或新增的数据,由此做到持久化。
1.3 事务原理与锁
1.3.1 锁的问题场景
多对一问题,多个操作者同时操作一个资源,而资源的状态变化是非原子的(有中间态),哄抢会导致资源状态混乱;
1.3.2 事务的问题场景
一对多的问题,一个操作者需要绑定操作一系列资源(比如多条 sql),若任何一条操作失败,都会导致整个操作失去意义;