MySQL事务隔离级别的实现
标准SQL事务隔离级别实现原理
READ-UNCOMMITTED(读取未提交)
事务对当前被读取的数据不加锁;
事务在更新某数据的瞬间(就是发生更新的瞬间),必须先对其加行级共享锁,直到事务结束才释放。
READ-COMMITTED(读取已提交)
事务对当前被读取的数据加行级共享锁(当读到时才加锁),一旦读完该行,立即释放该行级共享锁;
事务在更新某数据的瞬间(就是发生更新的瞬间),必须先对其加行级排他锁,直到事务结束才释放。
REPEATABLE-READ(可重复读)
事务在读取某数据的瞬间,必须先对其加行级共享锁,直到事务结束才释放;
事务在更新某数据的瞬间(就是发生更新的瞬间),必须先对其加行级排他锁,直到事务结束才释放。
SERIALIZABLE(可串行化)
事务在读取数据时,必须先对其加表级共享锁 ,直到事务结束才释放;
事务在更新数据时,必须先对其加表级排他锁 ,直到事务结束才释放。
可以看到,在只使用锁来实现隔离级别的控制的时候,需要频繁的加锁解锁,而且很容易发生读写的冲突(例如在RC级别下,事务A更新了数据行1,事务B则在事务A提交前读取数据行1都要等待事务A提交并释放锁)。
为了不加锁解决读写冲突的问题,MySQL引入了MVCC机制。
InnoDB事务隔离级别实现原理
需要了解的概念:
一致性非锁定读和一致性锁定读
一致性非锁定读
致性的非锁定读( consistent nonlocking read)是指InnoDB存储引擎通过行多版本控制(multi versioning)的方式来读取当前执行时间数据库中行的数据。
如果读取的行正在执行 DELETE或 UPDATE操作,这时读取操作不会因此去等待行上锁的释放。相反地, InnoDB存储引擎会去读取行的一个快照数据。之所以称其为非锁定读,因为不需要等待访问的行上X锁的释放。快照数据是指该行的之前版本的数据,该实现是通过undo段来完成,而undo段是用来事务中回滚数据,因此快照数据本身是没有额外的开销。此外,读取快照数据是不需要上锁的,因为没有事务需要对历史的数据进行修改操作。非锁定读机制极大地提高了数据库的并发性。在InnoDB存储引擎的默认设置下,这是默认的读取方式,即读取不会占用和等待表上的锁。
但是在不同事务隔离级别下,读取的方式不同,并不是在每个事务隔离级别下都是采用非锁定的一致性读。此外,即使都是使用非锁定的一致性读,但是对于快照数据的定义也各不相同。
在事务隔离级别 READ COMMITTED和 REPEATABLE READ(InnoDB存储引擎的默认事务隔离级别)下, InnoDB存储引擎使用非锁定的一致性读。
然而,对于快照数据的定义却不相同。在 READ COMMITTED事务隔离级别下,对于快照数据,非一致性读总是读取被锁定行的最新一份快照数据。而在 REPEATABLE READ事务隔离级别下,对于快照数据,非一致性读总是读取事务开始时的行数据版本。
一致性锁定读
在默认配置下,即事务的隔离级别为REPEATABLE READ模式下, InnoDB存储引擎的SELECT操作使用一致性非锁定读。但是在某些情况下,用户需要显式地对数据库读取操作进行加锁以保证数据逻辑的一致性。而这要求数据库支262某6章皱部拼吾持加锁语句,即使是对于SELECT的只读操作。InnoDB存储引擎对于SELECT语句支持两种一致性的锁定读(locking read)操作:
1. SELECT… FOR UPDATE
2. SELECT… LOCK IN SHARE MODE
SELECT… FOR UPDATE对读取的行记录加一个X锁,其他事务不能对已锁定的行加上任何锁。SELECT… LOCK IN SHARE MODE对读取的行记录加一个S锁,其他事务可以向被锁定的行加S锁,但是如果加X锁,则会被阻塞。
当前读和快照读
当前读
读取的是最新版本,像UPDATE、DELETE、INSERT、SELECT ... LOCK IN SHARE MODE、SELECT ... FOR UPDATE这些操作都是一种当前读,为什么叫当前读?就是它读取的是记录的最新版本,读取时还要保证其他并发事务不能修改当前记录,会对读取的记录进行加锁。
快照读
读取的是快照版本,也就是历史版本,像不加锁的SELECT操作就是快照读,即不加锁的非阻塞读;
READ-UNCOMMITTED(读取未提交)
事务对当前被读取的数据不加锁,都是当前读;
事务在更新某数据的瞬间(就是发生更新的瞬间),必须先对其加行级共享锁,直到事务结束才释放。
READ-COMMITTED(读取已提交)
事务对当前被读取的数据不加锁,且是快照读;
事务在更新某数据的瞬间(就是发生更新的瞬间),必须先对其加行级排他锁,直到事务结束才释放。
REPEATABLE-READ(可重复读)
事务对当前被读取的数据不加锁,且是快照读;
事务在更新某数据的瞬间(就是发生更新的瞬间),必须先对其加行级排他锁(Record,GAP,Next-Key),直到事务结束才释放。
通过间隙锁,在这个级别MySQL就解决了幻读的问题
通过快照,在这个级别MySQL就解决了不可重复读的问题
SERIALIZABLE(可串行化)
事务在读取数据时,必须先对其加表级共享锁 ,直到事务结束才释放;
事务在更新数据时,必须先对其加表级排他锁 ,直到事务结束才释放。