1.当前读和快照读
- 当前读:读取的是记录的最新版本,并且读取之后还要保证其他并发事务不能修改当前记录,对读取的记录加锁
当前读:select…lock in share mode,select…for update
当前读:update,delete,insert - 快照读:可能读取的并不是当前记录的最新版本,可能是之前的历史版本
快照读:不加锁的非阻塞读,select
不加锁的条件是在当前事务隔离级别是非serializable前提下才成立,在SE下面,由于是串行读,所以此时的快照读也退化成当前读,即select…lock in share mode模式;
之所以出现快照读,是基于提高并发性能的考虑;
快照读的实现是基于多版本并发控制,即MVCC,可以认为MVCC是行级锁的一个变种,但是它在很多情况下避免的加锁操作,因此开销更低
2.RC和RR级别下的当前读和快照读
- 在RC隔离级别下:
当前读和快照读读取的结果是一样的 - 在RR隔离级别下:
快照读有可能读到数据的历史版本
也有可能读到数据的当前版本
创建快照的时机决定了读取数据的版本
快照读 ==> 另外一个线程修改 ==>当前读 ==> 快照读(最后这次快照读,读取的是历史版本)
另外一个线程修改 ==> 当前读 ==> 快照读(最后这次快照读,读取的是当前版本)
3.RC和RR级别下的InnoDB的快照读(即非阻塞读)是如何实现的
- 数据行里的隐式字段DB_TRX_ID、DB_ROLL_PTR、DB_ROW_ID
DB_TRX_ID:最后一次修改改行记录的事务的ID
DB_ROLL_PTR: 回滚指针,即写入回滚端undo日志记录,如果该行记录被更新,则undo log records包含重建该行记录被更新之前状态所包含的信息
DB_ROW_ID:行ID,用来生成默认聚簇索引 - undo 日志
当我们对行记录做了变更操作时,就会存undo记录
undo记录中存储的是老版数据,当一个旧的事物需要读取数据时,为了能读取到老版本的数据,需要顺着undo链,找到满足其可见性的记录
undo log主要分为两种insert undo log和update undo log
—insert undo log 事务对新纪录insert产生的undo log,只在事务回滚时需要,并且在事务提交后就可以立即丢弃
—update undo log 事务对记录进行delete或者update时,产生的undo log,不仅在事务回滚时需要,快照读也需要,所以不能随便删除,只有当数据库所使用的快照中不涉及该记录,对应的回滚日志才会被perge线程删除 - read view
做可见性判断
记录当前处于活动状态的所有事务ID,RR级别下,第一次快照读时创建,RC级别下,每次快照读均会创建新的
遵循一个可见性算法:主要是将要修改的DB_TRX_ID取出来,与系统其他活跃事务ID做对比,如果大于或者等于这些ID的话,就通过DB_ROLL_PTR指针去取出undo log从上层往下层的DB_TRX_ID,直到小于这些活跃事务ID为止,这样就保证了我们获取的事务版本是当前可见的最稳定的版本