一、事务隔离级别
- 事务是指具有ACID四个属性的事务,即原子性、一致性、隔离性和持久性。mysql根据事务的控制由弱到强分为多个隔离级别,即读未提交、读已提交、可重复读和序列化读,innoDb默认为可重复读级别。
- 读未提交:事务中可以读到其他事务还未提交的数据,即脏读,相当于没有做事务控制。
- 读已提交:事务过程中读到的数据都是事务开始时已提交的数据,但事务中可能读到其他事务提交的更新,也可叫做不可重复读。
- 可重复读:整个事务中读到的数据都和事务开始时一样,不会读到事务中其他事务提交的更新,但是会读到其他事务添加的行,即幻读。
- 序列化读:所有事务串性化执行,不会出现幻读。
二、mvvc
- mysql采用mvvc多版本并发控制技术支持事务的不同隔离级别查询,且只在读提交、可重复读下有效。读未提交每次都读最新数据,序列化读通过加锁实现。
- 每次事务操作后mvvc给每个记录行都标记了事务id,相当于每个记录行的不同版本号。
- 事务开始时,会记录当前已提交的最新一个事务id,以及正在进行中的事务。在事务过程中读取数据时,根据记录的最新事务id与当前数据行的事务id进行比较,获取适当版本的数据,从而实现不同隔离级别。
- 例如,在可重复读隔离级别下,事务开始时数据行a有多个版本a(v1, v2, v3),以及正在进行中的事务v4。那么当前事务记录数据行a的版本号为v3,这么事务进行中读取数据行a时,即使事务v4提交了也会读取其v3版本的数据,从而实现可重复读。
- mvvc通过版本控制实现不同隔离级别,相当于为事务创建了一个视图,同时也不用复制数据,不用加锁,非常高效。
三、mysql锁
mysql通过mvvc实现了一致性读,但数据更新还是需要加锁来实现并发控制。
- 按照锁粒度划分:数据库锁、表锁、行锁。
- 按照兼容性划分:共享锁(读锁)、排他锁(写锁)。
- 具体的锁实现:记录锁(行锁)、间歇锁(gap)、临界锁(next-key)、表锁、意向锁(为了兼容表锁和行锁)。
- 锁模式:IS(意向共享锁)、S(共享锁)、IX(意向排他锁)、X(排他锁)。
不同锁介绍:https://dev.mysql.com/doc/refman/8.0/en/innodb-locking.html - 加锁原则,来自《mysql实战45讲-林奇》:
1. 加锁基本单位next-key
2. 查找过程中访问到的行才需要加锁
3.唯一索引上的等值查询,next-key将退化为行锁
4.索引上的等值查询,向后遍历最后一个不相等的记录的next-key将退化为间歇锁。
事务中数据的并发控制多数时候是通过多个锁共同控制完成的。通过以下命令开启事务详情查询:
set GLOBAL innodb_status_output_locks=ON;
set GLOBAL innodb_status_output=ON;
show engine innodb status;
查询锁和事务:
select from information_schema.innodb_locks;
select from information_schema.innodb_lock_waits;
select * from information_schema.innodb_trx;