后台InnoDB存储引擎实现了两种标准的行级锁:S锁和X锁。S锁称为共享锁,允许事务读取一行数据。X锁被称为排他锁,它允许一个事务删除或更新一行数据。一致非锁读是指如果一条记录被X锁锁定,其他事务仍然可以读取这条记录。一致性锁读是指一个事务可以通过SELECT语句对一条记录加X锁或X锁。举个小栗子,假设有一个表和两个事务,表名为mytest,事务名为t1和t2:t1t2t3t4abbbbccct1和t2的执行时序如下:这里我先抛出两个问题:在上面的标记A处,很明显t1给记录加了X锁,修改了事务中的数据。t2此时看到的是什么数据?上面标记B处的事务t1提交后,t2看到的是什么数据?Rowmulti-version控制rowmulti-version是指innodb为每条行记录存储了多个版本。请记住,这里是多个版本,而不是两个版本。刚开始接触多版本的时候,我的疑问是innodb每一行存储多个版本是多么浪费存储空间?然而进一步了解,所谓的多版本不过是InnoDB巧妙的谎言。通过undolog实现多版本。这里可以这样理解,既然undolog包含了所有用于恢复历史版本数据的信息,那么我们只需要将“不同版本”指针指向不同时间节点的undolog,这样在读取的时候,我们就可以通过还原不同时间节点版本数据的undolog得到不同的数据。同时读取undo日志不需要加锁,大大提高了数据库的并发性。这里回答上面第一个问题:此时t2看到的应该是数据的历史版本,即t1修改前的数据,如下:mysql>select*frommytestwhereet2='bb';+------+------+------+------+|t1|t2|t3|t4|+------+------+------+------+|a|bb|bb|ccc|+------+------+------+-----+1rowinset(0.00sec)READCOMMITTEDandREPEATABLEREAD这里回顾一下SQL标准定义的四种隔离级别:解决幻读问题,READCOMMITTED的值是一个事务可以读取其他事务提交的数据,而REPEATABLEREAD需要一个事务在事务内重复读取一条记录,所以上面第二个问题的答案是什么t2看到此时。数据库的隔离级别是相关的。比如此时的隔离级别为:mysql>select@@tx_isolation;+----------------+|@@tx_isolation|+-----------------+|REPEATABLE-READ|+----------------+1rowinset(0.00sec)所以t2在标记B旧数据应该可以看到的地方:mysql>select*frommytestwheret2='bb';+-----+-----+-----+-----+|t1|t2|t3|t4|+-----+-----+-----+-----+|a|bb|bb|ccc|+-----+------+-----+------+1rowinset(0.00sec)mysql>如果此时的事务隔离级别是READCOMMITTED,那么t2在标记处查看B应该是新数据。在一致锁读和一致非锁读的情况下,即使记录因为UPDATE被X-locked,其他事务仍然可以无阻塞地读取记录。而如果一个事务在读记录的时候想要锁定记录,不允许其他事务修改怎么办?也就是说,SELECT...FORUPDATE,SELECT...FORUPDATE显式地给一条记录加了X锁,这样其他Transaction就不能获取这条记录的任何锁。我们也可以使用SELECT...LOCKINSHAREMODE显式地给记录加上S锁,这样其他事务就可以获得记录的S锁,但不能获得记录的X锁。两种说法都有具体的应用场景。综上所述,综上所述,一致非锁读是指一条记录加了X锁,其他事务仍然可以读取,不会被阻塞。它是通过InnoDB的行多版本实现的,行多版本实际上并不存储多版本记录,而是通过undo来实现的。一致性锁读到我可以通过SELECT语句显式的给一条记录加上X锁,来保证特定应用场景下的数据一致性。