我们知道SQL标准的事务隔离级别包括:读未提交(read uncommitted)、读提交(read committed)、可重复读(repeatable read)、串行化(serializable)。
简述下四个级别的含义:
- 读未提交:一个事务还没提交时,它做的变更就能被别的事务看到。
- 读提交(RC): 一个事务提交之后,它做的变更才会被其他事务看到。
- 可重复读(RR): 一个事务执行过程中看到的数据,总是跟这个事务在启动时看到的数据是一致的。当然在可重复读隔离级别下,未提交变更对其他事务也是不可见的。
- 串行化:对于同一行记录,“写”会加“写锁”,“读”会加“读锁”。当出现读写锁冲突的时候,后访问的事务必须等前一个事务执行完成,才能继续执行。
数据库的多版本并发控制(即常说的
MVCC
):
同一条记录在数据库系统中存在多个版本。
InnoDB中每个事务都有一个唯一的事务ID,叫作transaction id。它是在事务开始的时候向InnoDB事务系统申请的,且严格按照申请顺序递增。因为每个事务的id是有大小顺序的。可重复读(RR)隔离级别下,会对当下的数据产生一个快照,这个快照保证了RR的特性。但是这个快照并不是真的把所有库的数据都拷贝了一份,而是根据有着大小之分的transaction id
来确定每行数据的哪个版本在当前事务可见。若该行数据的某个版本的transaction id
比当前事务的transaction id
小且该版本已提交,则可见;反之不可见。
看到上图,该行数据有四个版本,分别是V1,V2,V3,V4(当前最新版本是V4,前三个值不是真实存在的,而是通过回滚日志undo log
根据需要随时可以通过计算获取到的虚拟存在)。此时k的最新值为22,若一个事务trx_id
等于18,那么对这个事务来说,该行数据的k值就是11,保证了RR级别下的一致性读。
这里特别提醒,虽然上述例子中,trx_id
等于18的事务获取到的k值是11,但若是trx_id
等于25的事务对k值先进行了更新,假如设置k=100
,如果trx_id
等于18的事务中要更新k值,那么一定是在k=100
的基础上更新。
更新数据都是先读后写的,而这个读,只能读当前的值,称为“当前读”(current read)。
且由于行锁的存在,trx_id
等于18的事务会被堵住,等待trx_id
等于25的事务提交后才能更新。