为了提高并发性能MySQL、Oracle等数据库等关系型数据库都实现了多版本并发控制-MVCC(Multi-Version Concurrent Control),由于没有统一的实现标准,它们的实现机制并不相同。
可以认为MVCC是行级锁的一个变种,但是在大多数情况下避免了加锁,所以开销更低。虽然实现机制有所不同,但是大多实现了非阻塞的读操作,写操作也只是锁定了必要的行。
MVCC的实现,是通过保存数据某个时间点的快照来实现的。也就是说不管执行多长时间,每个事务看到的数据时一致的。根据事务开始时间的不通,每个事务对同一张表,同一时刻看到的数据可能是不一样的。
InnoDB的MVCC,是通过在每行记录后面保存两个隐藏的列来实现的。分别保存创建时间和过期时间。当然储存的不是实际的时间至,而是系统版本号(system version number)。每一个新事物开始,系统版本号都会自动递增。事务开始时刻的系统版本号会作为事务的版本号,用来和查询到的每行记录的版本号进行比较。下面看看REPEATABLE READ隔离级别下,MVCC具体是怎么操作的。
1. SELECT
InnoDB会根据一下两个条件检查每行记录:
- InnoDB只查找行的系统版本号小于或者等于事务的系统版本号,这样可以确保事务读取的行,要么是在事务开始前已经存在的,要么是事务自身插入或者修改过的
- 行的删除版本要么未定义,要么大于当前事务版本号。这可以确保事务读取到的行,在事务开始之前未被删除
只有满足以上两个条件,才能返回作为查询结果
2. INSERT
InnoDB为新插入的每一行保存当前系统版本号作为行版本号
3. DELETE
InnoDB为删除的每一行保存当前系统版本号作为删除标识
4. UPDADE
InnoDB为插入一行新纪录,保存当前系统版本号作为行版本号,同时保存当前系统版本号到原来的行作为行删除标识
这也解释了为什么自增ID的数据删除后,新增数据后会跳过当前ID
保存这两个额外系统版本号,是大多数读操作可以不用加锁。使读操作性能好,操作简单,能保证符合标准的行。不足是每行记录需要额外的存储空间,需要做更多行检查工作,以及一些额外的维护工作。
MVCC只在REPEATABLE READ 和READ COMMITTED两个隔离级别下工作。其它两个级别不兼容,因为,READ UNCOMMITTED总是读取到最新的数据行,而不是符合当前事务版本的数据行。而SERIALIZABLE则对所有读取的行都加锁