您的位置:首页 > 数据库 > MySQL

(三)MySQL InnoDB非锁定一致性读与锁定读

2015-10-27 17:17 603 查看
MySQL InnoDB非锁定一致性读与锁定读

(一)MySQL InnoDB事务模型

(二)MySQL InnoDB锁模型

(三)MySQL InnoDB非锁定一致性读与锁定读

(四)MySQL InnoDB锁类型及幻象读问题

(五)MySQL InnoDB中各类语句加锁方式

(六)事务的提交与回滚极死锁检测、处理和预防

非锁定一致性读

一致性读,意味着InnoDB使用“多本版”向查询提供数据库在某个时间点的快照,这样一来,查询能够看到该时间点之前事务提交的更新而不能看到之后事务提交或未提交的更新。但也存在例外情况。(稍后会做详细介绍)

所谓一致性读,更具体的说,在REPEATABLE READ事务隔离级别下,同一事务内的一致性读均会读取到该事务中第一个读创建的快照,其他事务在之后提交或未提交的更新对当前事务的读均不可见,除非提交了该事务并开启新事务发起新查询。而在READ COMMIT隔离级别下,事务内的每个一致性读均会设置和读取自己新鲜的快照。其他事务在之后提交的更新对当前事务的读可见,未提交的更新对当前事务不可见。具体内容在介绍InnoDB四种隔离界别的时候已经做了相信介绍,具体可参考REPEATABLE READ、READ COMMIT节的内容。

一致性读是InnoDB引擎处理READ COMMIT和REPEATABLE READ隔离级别中SELECT的默认方式,不需要对SELECT访问的对象加锁,其他session中的事务可以在另一session中的事务读去的同时自由的修改相关对象,因此称为非锁定一致性读。

本节开始时提到一致性读存在例外情况。下面以一个例子加以说明:



可以看到,起初事务的隔离级别均为REPEATABLE-READ。SESSION 读取的内容为i=1,2,3的行,在SESSION B中的事务更新了i=3的行后SESSION A查询到的内容并没有改变,即时在SESSION B提交了该更新后SESSION A扔只能看到最开始的一致性读创建的快照。但是,在SESSION A中的事务执行UPDATE语句更新了被SESSION B更新的记录后(数据库快照只适用于SELECT语句,不适用与DML语句,所以事务中的DML语句是可以看到其他session中的事务的更新的,即时SELECT并不能看到这些)再次执行SELECT语句不仅可以看到快照中的数据,还可以看到更新后的数据。

上述提到的一致性读由多版本并发控制(MVCC)原理实现(利用了InnoDB的undo log)。需要注意的是,一致性读不适用于特定的DDL语句如DROP TABLE、ALTER TABLE。另外,对于 INSERT INTO ... SELECT, UPDATE ... (SELECT)和CREATE TABLE ... SELECT 中未指定FOR UPDATE或LOCK IN SHARE MODE的SELECT默认情况下行为和READ COMMIT隔离级别下的普通SELECT一样,同一事务内设置和读取自己的新鲜快照。

锁定读

上一节介绍了一下InnoDB中的非锁定一致性读,与之对应的是锁定读。顾名思义,非锁定一致性读在某个事务读取记录时不加任何锁其他事务可以修改记录,而锁定读意味着某个事务读取记录时会加锁。锁定读分为两种类型:SELECT...FOR UPDATE和SELECT...LOCK IN SHARE MODE,前者会对读取的记录加X锁,阻塞其他事务的读请求和修改请求,直至事务提交释放锁资源;后者会对读取的记录加S锁,阻塞其他事务的修改请求但不会阻塞读取请求,直至事务提交释放锁资源。也正因为SELECT...FOR UPDATE和SELECT...LOCK IN SHARE MODE分别需要对查询的记录加X锁和S锁,因此分别会被其他正在读写和写的事务阻塞,直到这些事务结束。需要注意的是SELECT...FOR UPDATE仅适用于autocommit=0或者通过START TRANSACTION明确开启事务的情况。

因为锁定读会阻塞其他事务的修改请求,因此可以有效解决上一节非锁定一致性读中提到的”异常“,也即,一个事物执行了普通SELECT后若其他的事务更新、插入了记录并提交,那么该事务内执行DML操作更新被其他事务更新或插入的记录后再次执行SELECT操作会看到更新后的结果。(具体可以查看上一节的示例)

锁定读的一个典型应用场景

假如要往子表插入一条记录,插入前首先要确认一下父表有无相关记录,只有在父表有对应记录时插入才能满足引用完整性约束。如果使用一致性读来查询父表来验证相关行存在,此时往子表插入时其他的session有可能更新或者删除刚才父表中查到的行,这样在子表中插入后就违反了引用完整性约束。为了避免该问题可使用锁定读SELECT...LOCK IN SHARE MODE,在事务中读取父表进行验证时,对相关记录加S锁,这样其他事务无法对相关记录进行DML操作,此时可在事务中安全的插入相关记录,待此操作完成并提交或回滚后其他事务才能对记录进行DML操作。

还有一种情况,比如有两个session需要读取某表中的一行,在成功读取后在同一事物中更新该行,并在另外的表中插入刚开始读取到的行。若此时使用SELECT...LOCK IN SHARE MODE则会对读取到的记录加S锁,两个session在同时申请X锁进行更新时便发生死锁。另外,由于读取到了同一行内容,两个session在向同一表插入数据时会导致键重复的错误。这种情况下用SELECT...FOR UPDATE较合适,在读取的时候阻塞其他事物的读和更新请求。

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: