一致的非锁定读取
一致的读取该装置InnoDB
在一个时间点使用多版本存在于查询数据库的快照。该查询将看到该时间点之前提交的事务所做的更改,而看不到以后或未提交的事务所做的更改。该规则的例外是查询可以看到同一事务中较早的语句所做的更改。此异常导致以下异常:如果更新表中的某些行,则SELECT
参见已更新行的最新版本,但也可能会看到任何行的旧版本。如果其他会话同时更新同一张表,则异常意味着您可能会以数据库中不存在的状态参见该表。
如果事务隔离级别为REPEATABLE READ
(默认级别),则同一事务中的所有一致读取将读取该事务中第一个此类读取所建立的快照。您可以通过提交当前事务并在此之后发出新查询来获取查询的更新快照。
使用READ COMMITTED
隔离级别,事务中的每个一致读取都会设置并读取其自己的新快照。
一致的读取是在默认模式InnoDB
进程SELECT
中的语句READ COMMITTED
和REPEATABLE READ
隔离级别。一致读取不会在它访问的表上设置任何锁,因此其他会话可以自由地在对表执行一致读取的同时修改这些表。
假设您以默认REPEATABLE READ
隔离级别运行。当您发出一致的读取(即一条普通SELECT
语句)时,InnoDB
将为您的事务提供一个时间点,根据该时间点您的查询将看到数据库。如果在分配了时间点后另一个事务删除了一行并提交,则看不到该行已被删除。插入和更新的处理方式相似。
注意数据库状态的快照适用
SELECT
于事务中的语句,而不一定适用于 DML语句。如果插入或修改某些行,然后提交该事务,则另一个并发事务发出的DELETE
orUPDATE
语句REPEATABLE READ
可能会影响那些刚刚提交的行,即使会话无法查询它们。如果某个事务确实更新或删除了另一个事务提交的行,则这些更改对于当前事务而言确实可见。例如,您可能会遇到以下情况:SELECT COUNT(c1)FROM t1WHERE c1 = 'xyz'; -- Returns 0: no rows match.DELETE FROM t1WHERE c1 = 'xyz'; -- Deletes several rows recently committed by other transaction.SELECT COUNT(c2)FROM t1WHERE c2 = 'abc'; -- Returns 0: no rows match.UPDATE t1SET c2 = 'cba'WHERE c2 = 'abc'; -- Affects 10 rows: another txn just committed 10 rows with 'abc' values.SELECT COUNT(c2)FROM t1WHERE c2 = 'cba'; -- Returns 10: this txn can now see the rows it just updated.
您可以通过提交事务,然后再执行另一个SELECT
或来提前时间点START TRANSACTION WITH CONSISTENT SNAPSHOT
。
这称为多版本并发控制。
在以下示例中,会话A仅在B提交了插入并且A也提交了时,才看到B插入的行,因此时间点比B的提交提前了。
Session ASession BSET autocommit=0;SET autocommit=0; time |SELECT *FROM t; |empty set |INSERT INTO tVALUES (1, 2); | vSELECT *FROM t;empty set COMMIT ;SELECT *FROM t;empty set COMMIT ;SELECT *FROM t; --------------------- | 1 | 2 | ---------------------
如果要参见数据库的“最新”状态,请使用READ COMMITTED
隔离级别或锁定读取:
SELECT *FROM tFOR SHARE ;
使用READ COMMITTED
隔离级别,事务中的每个一致读取都会设置并读取其自己的新快照。使用时FOR SHARE
,将发生锁定读取:A SELECT
阻塞直到包含最新行的事务结束(请参见“锁定读取”)。
一致读取不适用于某些DDL语句:
- 一致读取无法解决问题
DROP TABLE
,因为MySQL无法使用已删除InnoDB
的表并破坏该表。 - 一致读取无法解决问题
ALTER TABLE
,因为该语句将创建原始表的临时副本,并在构建临时副本时删除原始表。当您在事务中重新发出一致的读取时,新表中的行是不可见的,因为在获取事务的快照时这些行不存在。在这种情况下,事务返回一个错误:ER_TABLE_DEF_CHANGED
,“表的定义发生了变化,请重试交易”。
读取的类型因选择子句(如INSERT INTO ... SELECT
,)中的选择而有所不同UPDATE ...(SELECT)
,并且CREATE TABLE ... SELECT
未指定FOR UPDATE
或FOR SHARE
:
- 默认情况下,
InnoDB
使用更强的锁定,并且SELECT
部分的行为类似于READ COMMITTED
,其中每次一致的读取(即使是在同一事务中)也会设置并读取自己的新快照。 - 要在这种情况下,读一致性,事务隔离级别设置为
READ UNCOMMITTED
,READ COMMITTED
或REPEATABLE READ
(比其他任何东西SERIALIZABLE
)。在这种情况下,不会对从选定表读取的行设置锁定。