一致性非锁定读与锁定读的区别主要针对的是读操作在加锁方式上的差别。这里先看下锁的分类吧。
mysql的行锁分为读锁与写锁。
读锁即S锁,也叫共享锁,当事务读取一行数据时,会尝试在记录上加S锁;
写锁即X锁,也叫排它锁,当事务要更新或者删除一行数据时,会尝试在记录上加X锁;
只有S锁和S锁是互相兼容的,其余情况都是不兼容的,需要等待。
看这个表的意思,在读取时,需要先加S锁,实际上真的会吗?
答案是:否。
下面开始介绍锁定读与非锁定读。
非锁定读定义:https://dev.mysql.com/doc/refman/5.7/en/innodb-consistent-read.html
锁定读定义:https://dev.mysql.com/doc/refman/5.7/en/innodb-locking-reads.html
mysql官方的介绍还是比较全面的。
非锁定读全称是“Consistent Nonlocking Reads”,包含了两方面含义,一个是nonlocking,表明这种读操作不需要加S锁,所以即便其他事务已经在该数据行加了X锁,读操作仍然可以执行,不会阻塞。同样,一旦执行了读操作,其他事务仍然可以执行写操作,不会被阻塞。另一层含义是consistent,这个“一致性”取决于当前的隔离级别,在不同隔离级别下,读到的值是不同的。非锁定读可以保证即便其他事务修改了数据,但是当前读操作是不会被阻塞的,并且保证返回相应一致性要求下的快照数据。这个原理是由一种叫做MVCC的机制实现的。非锁定读是非序列化隔离级别下的默认读取方式。
锁定读全称是“Locking Read”,意味着当前的读操作需要先加锁(具体加什么锁取决于隔离级别和语法)。这样,如果当前记录已经有了X锁,那么读操作会阻塞。同样,一旦执行了读操作,其他事务的更新操作也会被阻塞。锁定读有如下几种方式:
1.在serializable隔离级别下,读操作加S锁;
2.非serializable隔离级别下使用select in share mode语法加S锁,使用select for update语法加X锁;
3.更新操作如update,insert以及delete,如果在语句中需要读,也是锁定读;
锁定读读到的肯定是最新的值;非锁定读是MVCC机制,取决于隔离级别;
这里顺便说一下select in share mode与select for update的用处。
简言之,select in share mode会加S锁,那么使用该语法,可以避免当前事务读取数据后,其他事务修改这一行数据。select for update会加X锁,那么使用该语法,可以避免当前事务读取数据后,其他事务也读取该行数据,这一点在防止更新覆盖时会非常有用,后面的博客会给出例子。
下面看几个例子:
1.serializable隔离级别下锁定读
T1:
T1先更新一条数据,上了X锁;
T2:
T2读取数据时,由于是锁定读,需要加S锁,于是发生了锁等待;
2.for update锁定读:
T1:
T1先更新一条数据,上了X锁;
T2:
T2读取数据,如果不写for update,那么由于是在repeatable read隔离级别下,使用的是非锁定读,是可以读的(见下面的例子);但是由于加了for update,就需要先上X锁,那么会锁等待。
3.不加for update会用非锁定读:
例子:
T1:
T1先更新一条数据,上了X锁;
T2:
T2由于是非锁定读了,不需要加锁,所以仍然可以读取到数据,不会被阻塞。
4.select in share mode例子:
T1:
T2:
T2使用lock in share mode查询时,由于要加S锁,被阻塞。
小结:
锁定读与非锁定读的本质区别:读操作时是否需要加锁!!!