MySql - 事务 & 锁


事务四大特性(ACID)

简单理解,事务是一组原子性的的SQL操作,这一组SQL要么全部执行成功,要么全部执行失败

  • 原子性:一个事物是不可分割的最小工作单元,整个事务要么全部执行成功,要么全部执行失败,不可能只执行中间的一部分操作
  • 一致性:执行事务是使数据库从一个一致性状态到另一个一致性状态,如果事务最终没有被提交,那么事务所做的修改也不会保存到数据库中
  • 隔离性:通常,一个事务提交前对于其他事务是不可见的,但是隔离性需要参考“隔离级别”
  • 持久性:事务一旦被提交,那么对数据库的修改将会被永久保存,即时数据库奔溃后修改的数据也不会丢失

隔离级别

SQL 标准中定义了四种隔离级别

  • 未提交读:未提交读是指,在事务中的修改,即使没有提交,对其他事务也都是可见的,但是这样会出现脏读,一般情况下都不会使用 “未提交读”
  • 提交读:提交读指的是,一个事务所做的修改在提交之前对其他事务都是不可见的,这个级别也叫做 “不可重复读”,因为执行两次相同的操作,可能会得到不同的结果
  • 可重复读:可重复读解决了 “脏读” 的问题,这个级别保证了同一个事务多次读取同样记录的结果是一致的,但是这个隔离级别无法解决 “幻读” 的问题。所谓 “幻读” 就是当某个事务读取范围数据时,另一个事务又在该范围内插入了新的记录,当之前的事务再次读取该范围数据时,会产生 “幻行” 。 InnoDB 存储引擎通过 MVCC 解决了幻读的问题,可重复读是 MySQL 默认的事务隔离级别
  • 可串行化:是最高的隔离级别,避免了前面说到的 “幻读问题”。可串行化会给读取的每一行都加锁,所以可能导致大量超时和锁争用的问题,实际使用中很少使用这个隔离级别

死锁

死锁是指两个或者多个事务在同一资源上相互占用,并请求锁定对方占用的资源。
解决方案是:回滚一个或者多个事务

1
2
3
graph LR
A-->B
B-->A

MCVV

MVVC 是行锁的一个比那种,在很多情况下MVVC可以避免加锁,因此开销较小,而不同的事务型存储引擎对于MVVC的实现各有不同。

MVVC是通过保存数据在某个时间点的快照来实现的。不管执行多长时间,每个事务看到的数据都是一致的。根据事务的开始时间不同,每个事务对同一张表,同一时刻看到的数据可能不一样。

InnoDB 的 MVVC 通过在每行记录后面保存两个隐藏的列来实现。这两个列,一个是保存了行的创建时间,一个保存了行的过期时间,存储的不是实际时间,而是版本号。每个新开始的事务,系统版本号都会自动增加。事务开始时刻的系统版本号会作为事务的版本号,用来与查询到的每行记录的版本号作对比。


  • SELECT

InnoDB 会根据以下两个条件检查每条记录:

  1. 只查找版本小于等于失误版本号的行
  2. 只查找未定义删除事件或者删除时间大于事务版本号的行
  • INSERT

InnoDB 为新插入的每一行保存当前的系统版本号作为行版本号

  • DELETE

InnoDB 为删除的每一行保存当前的系统版本号作为行的删除版本号

  • UPDATE
  1. InnoDB 新增一条记录,保存当前系统版本号作为新增行的版本号
  2. 在被删除记录的原始航,保存当前系统版本号作为被删除记录行的删除版本号

优点
  1. 因为有了两个隐藏列来记录数据的状态,所以大多数读操作都可以不加锁
  2. 性能好,同时可以保证读取的数据是正确的
缺点
  1. 需要额外的空间记录每行的状态
  2. 需要行状态的维护和检查

参考自 掘金