摘自 数据库系统原理 | CS-Notes

目录

一、事务

事务满足 ACID 特性的一组操作,可以通过Commit提交一个事务,也可以通过Rollback进行回滚

ACID

1. Atomicity 原子性

事务被视为不可分割的最小单元,事务的所有操作要么全部提交成功,要么全部失败回滚

使用回滚日志Undo Log保证原子性

2. Consistency 一致性

数据库事务执行前后都保持一致性状态,所有事务对一个数据的读取结果都是相同的

3. Isolation 隔离性

一个事务所做的修改在最终提交以前,对其他事务不可见

4. Durability 持久性

一旦事务提交,则其所做的修改将会永远保存到数据库中,即使系统发生崩溃,事务的执行结果也不能丢失。

使用重做日志Redo Log保证持久性

ACID 之间的关系

  • 只有满足一致性,事务的执行结果才是正确的
  • 无并发的情况下,事务串行执行,隔离性一定满足。此时只要满足原子性,就可以满足一致性
  • 有并发的情况下,多个事务并行执行,事务不仅要满足原子性,还要满足隔离性,才能满足一致性
  • 事务满足持久性是为了应对数据库崩溃的情况

AUTOCOMMIT

MySQL 默认采用自动提交模式,如果不显式使用START TRANSACTION语句来开始一个事务,那么每个查询都会被当作一个事务自动提交

二、并发一致性

并发环境下,事务的隔离性很难保证,有可能出现很多并发一致性问题。

丢失修改

T2的修改覆盖了T1的修改:

丢失修改
丢失修改

读脏数据

T1修改一个数据,T2随后读取这个数据,如果T1撤销了这次修改,那么T2读取的数据就是脏数据

读脏数据
读脏数据

不可重复读

T2读取一个数据,T1对该数据进行了修改。如果T2再次读取这个数据,结果会和第一次读取的结果不同

不可重复读
不可重复读

幻影读

T1读取某个范围的数据T2在这个范围内插入新的数据T1再次读取这个范围的数据,结果会和第一次读不同

总结

  • 主要原因破坏了事务的隔离性
  • 解决方法:通过并发控制来保证隔离性
  • 如何实现:通过封锁,但需要用户自己控制,相当复杂
  • 数据库管理系统提供了事务的隔离级别,可以让用户轻松的处理并发一致性问题

三、封锁

封锁粒度

MySQL中提供了两种封锁粒度行级锁以及表级锁

应该尽量只锁定需要修改的那部分数据。锁定的数据量越少,发生锁争用的可能就越小,系统的并发程度就越高。

但是加锁需要消耗资源,锁的各种操作(包括获取锁、释放锁、检查锁状态)都会增加系统开销。因此封锁粒度越小,系统开销就越大

在选择锁粒度时,需要在锁开销和并发程度之间做一个权衡

封锁类型

1. 读写锁
  • 排它锁(Exclusive):简写为X,又称为写锁
  • 共享锁(Shared):简写为S,又称为读锁
2. 意向锁

使用意向锁(Intention Locks)可以更容易的支持多粒度封锁

意向锁在原来的X/S锁之上引入了IX/IS,都是表级锁,用来表示一个事务想要在表中的某个数据行上加X锁或S**锁。有以下两个规定:

  1. 一个事务在获得某个数据行对象的S锁之前,必须获得表的IS锁或更强的锁
  2. 一个事务在获得某个数据行对象的X锁之前,必须先获得表的IX

封锁协议

1. 三级封锁协议

一级封锁协议

事务T修改数据A必须加X直到T结束才释放锁.

可以解决丢失修改的问题,因为不能同时有两个事务对同一个数据进行修改,那么事务就不会被覆盖

二级封锁协议

在一级的基础上,要求读取数据A时必须加S读取完马上释放S

可以解决读脏数据的问题,因为如果一个事务在对数据A进行修改,根据一级封锁协议,会加X锁,那么就不能再加S锁了,也就不会读入脏数据

三级封锁协议

在二级的基础上,要求读取数据A时必须加S,直到事务结束了才能释放S

可以解决不可重复读的问题,因为读A时,其他事务不能对AX锁,从而避免了在读的期间数据发生改变

2. 两段锁协议

加锁解锁分为两个阶段进行。

可串行化调度是指,通过并发控制,使得并发执行的事务结果串行执行的事务结果相同**。

事务遵循两段锁协议是保证可串行化调度的充分条件。例如以下操作满足两段锁协议,是可串行化调度:

lock-x(A)...lock-s(B)...lock-s(C)...unlock(A)...unlock(C)...unlock(B)

不是必要条件,例如以下操作不满足两段锁协议,但它仍是可串行化调度:

lock-x(A)...unlock(A)...lock-s(B)...unlock(B)...lock-s(C)...unlock(C)

MySQL 隐式与显式锁定

MySQLInnoDB存储引擎采用两段锁协议,会根据隔离级别在需要的时候自动加锁,并且所有的锁都是在同一时刻被释放,这被称为隐式锁定

InnoDB也支持使用特定的语句进行显式锁定

SELECT ... LOCK In SHARE MODE;
SELECT ... FOR UPDATE;

四、隔离级别

未提交读 READ UNCOMMITTED

事务中的修改,即使没有提交,对其他事务也是可见的

提交读 READ COMMITED

一个事务只能读取已经提交事务所做的修改。也就是说,一个事务所做的修改在提交之前对其他事务是不可见的

可重复读 REPEATABLE READ

保证在同一个事务中多次读取同样数据的结果是一样的

可串行化 SERIALIZABLE

强制事务串行执行

五、多版本并发控制

多版本并发控制(Multi-Version Concurrency Control,MVCC)是MySQLInnoDB存储引擎实现隔离级别的一种具体方式,用于实现提交读可重复读这两种隔离级别。

  • 未提交读隔离级别总是读取最新的数据行无需使用 MVCC
  • 可串行化隔离级别需要对所有读取的行都加锁单纯使用 MVCC 无法实现

版本号

  • 系统版本号:是一个递增的数字每开始一个新事务,系统版本号就会自动递增
  • 事务版本号:事务开始时的系统版本号

隐藏的列

MVVC每行记录后都保存着两个隐藏的列,用来存储两个版本号

  • 创建版本号:表示创建一个数据行的快照时的系统版本号
  • 删除版本号:如果该快照的删除版本号大于当前事务的版本号,则该快照有效,否则表示该快照已经被删除了

Undo 日志

MVCC 使用到的快照存储在Undo日志中,该日志通过回滚指针一个数据行(Record)的所有快照连接起来。

更新中…

参考文章

  1. 数据库系统原理 | CS-Notes