MVCC
MVCC
MVCC(Multi-Version Concurrency Control) 多版本并发控制。
为每次事务生成一个新版本的数据,在读数据时选择不同版本的数据可以实现对事务的完整性读取
特点:
- 每行数据都存在一个版本,每次数据更新时都更新该版本。
- 修改时Copy出当前版本随意修改,各个事务之间无干扰。
- 保存时比较版本号,如果成功(commit),则覆盖原记录;失败则放弃copy(rollback)
以下数据库存储引擎默认使用Innodb
三个隐藏字段
InnoDB会为每个使用InnoDB存储引擎的表添加三个隐藏字段,用于实现数据多版本以及聚集索引,他们的作用如下:
DB_TRX_ID(6字节): 它是最近一次更新或者插入或者删除该行数据的事务ID(若是删除,则该行有一个删除位更新为已删除。但并不是真正的进行物理删除,当InnoDB丢弃为删除而编写的更新撤消日志记录时,它才会物理删除相应的行及其索引记录。此删除操作称为清除,速度非常快)
DB_ROLL_PTR(7字节): 回滚指针,指向当前记录行的undo log信息(指向该数据的前一个版本数据)
DB_ROW_ID(6字节): 随着新行插入而单调递增的行ID。InnoDB使用聚集索引,数据存储是以聚集索引字段的大小顺序进行存储的,当表没有主键或唯一非空索引时,innodb就会使用这个行ID自动产生聚簇索引。如果表有主键或唯一非空索引,聚簇索引就不会包含这个行ID了。这个DB_ROW_ID跟MVCC关系不大。
快照读和当前读
快照读:使用普通的select 语句进行查询时会生成快照,进行快照读,快照读不会上锁,根据可见性判断,来决定是读取该行记录的最新版本还是旧版本。(只有使用普通的select语句进行查询才会用到快照读,才享受到了MVCC机制的读写非阻塞的优越性)
当前读:使用select … lock in share mode,select … for update,insert,update,delete 语句等语句进行查询或者更新时,会使用相应的锁进行锁定,查询到的肯定数据库中该行记录的最新版本。
从最新记录开始找
当前事务ID 小于 未提交事务最小ID 可读
当前事务ID 最小ID <= 事务ID <= 最大ID 且 在未提交数组里,不可读
当前事务ID 大于 已提交事务最大ID 不可读
RR隔离级别下当执行普通的select查询时,若是innodb默认会执行快照读,类似于在目前的状态拍了一张照片,时机是第一次Select 语句,以后执行select 的时候就会返回当前照片里面的数据,当其他事务提交了也对你不造成影响,和你没关系,这就实现了可重复读。
RR返回的readview是第一条记录的,RC每次查询都返回最新的readview。
总结
- MVCC的实现主要依赖于数据库在每个表中添加的三个隐藏字段以及事务在查询时创建的快照(read view)和数据库支持多版本数据,数据库中存在数据版本链(Undo log)。
- InnoDB支持多版本数据,在更新或者删除数据时,并不会立马删除原有行记录,而是将旧版本存入回滚段中的Undo log内,并通过回滚指针形成一个数据链,可以通过这个指针访问链上的历代数据版本,正是这种机制使得数据库数据产生了多个版本,为通过MVCC进行快照读提供了可能。
- 并不是所有的查询都是进行快照读,使用普通的select 语句进行查询时会生成快照,进行快照读;使用select … lock in share mode,select … for update,insert,update,delete 语句等语句进行查询或者更新时还是会使用锁机制,进行锁阻塞。
- 使用MVCC的作用(意义)是非阻塞的解决了事务读写冲突,提高了并发性能。
- MVCC只支持RC(读取已提交)和RR(可重复读)隔离级别。
- MVCC能解决脏读、不可重复读问题,不能解决丢失更新问题和幻读问题。
- InnoDB使用锁机制和MVCC来共同作用,进行并发控制的,虽然MVCC不能解决幻读和丢失更新问题,但通过与锁机制(行级锁的Next-Key Locks算法的使用、排他锁等)一起作用可以达到可串行化隔离级别的效果,禁止了幻读、更新丢失等问题