17.7.2.3 Постоянные чтение без блокировок
В этом случае InnoDB использует многоверсионность для представления запросу моментального снимка базы данных на определённый момент времени. Запрос видит изменения, внесённые транзакциями, которые были завершены до этого момента, но не видит изменений, внесённых более поздними или незавершенными транзакциями. Исключением из этого правила является то, что запрос видит изменения, внесённые предыдущими операторами в рамках той же транзакции. Это исключение приводит к следующей аномалии: если вы обновили некоторые строки в таблице, то SELECT видит последнюю версию обновлённых строк, но также может видеть более старые версии других строк. Если другие сессии одновременно обновляют ту же таблицу, то аномалия означает, что вы можете увидеть таблицу в состоянии, которое никогда не существовало в базе данных.
Если транзакция имеет изоляцию REPEATABLE READ (по умолчанию), все постоянные чтения в рамках одной транзакции считывают моментальный снимок, созданный первым таким чтением в этой транзакции. Вы можете получить более свежий моментальный снимок для своих запросов, завершив текущую транзакцию и после этого выполнив новые запросы.
При уровне изоляции READ COMMITTED каждое постоянное чтение в рамках транзакции устанавливает и считывает свой собственный свежий моментальный снимок.
Постоянное чтение является режимом по умолчанию, в котором InnoDB обрабатывает SELECT операторы в READ COMMITTED и REPEATABLE READ уровнях изоляции. Постоянное чтение не устанавливает никаких блокировок на таблицы, к которым оно обращается, поэтому другие сессии могут свободно изменять эти таблицы одновременно с выполнением постоянного чтения таблицы.
Предположим, что вы работаете в изоляции по умолчанию REPEATABLE READ. При выполнении постоянного чтения (то есть обычного SELECT оператора) InnoDB задаёт вашей транзакции временную метку, согласно которой запрос видит базу данных. Если другая транзакция удаляет строку и завершается после того, как вашей транзакции была назначена временная метка, вы не увидите, что строка была удалена. Вставки и обновления обрабатываются аналогично.
Моментальный снимок состояния базы данных относится к SELECT операторам в рамках транзакции, а не обязательно к другим операторам. Если вы вставляете или изменяете некоторые строки, а затем завершаете эту транзакцию, DELETE или UPDATE операторы, выпущенные другой конкурирующей REPEATABLE READ транзакцией, могут повлиять на эти только что завершенные строки, даже если сессия не может их запросить. Если транзакция обновляет или удаляет строки, завершенные другой транзакцией, эти изменения станут видимыми для текущей транзакции. Например, вы можете столкнуться с ситуацией, подобной следующей:
SELECT COUNT(c1) FROM t1 WHERE c1 = 'xyz';
-- Returns 0: no rows match.
DELETE FROM t1 WHERE c1 = 'xyz';
-- Deletes several rows recently committed by other transaction.
SELECT COUNT(c2) FROM t1 WHERE c2 = 'abc';
-- Returns 0: no rows match.
UPDATE t1 SET c2 = 'cba' WHERE c2 = 'abc';
-- Affects 10 rows: another txn just committed 10 rows with 'abc' values.
SELECT COUNT(c2) FROM t1 WHERE c2 = 'cba';
-- Returns 10: this txn can now see the rows it just updated.
Вы можете продвинуть свою временную метку, завершив свою транзакцию и затем выполнив ещё одно SELECT или START TRANSACTION WITH
CONSISTENT SNAPSHOT.
Это называется многоверсионным контролем конкуретности.
В следующем примере сессия A видит строку, вставленную сессией B, только когда B завершила вставку, а A также завершила, так что временная метка A переместилась за момент завершения B.
Session A Session B
SET autocommit=0; SET autocommit=0;
time
| SELECT * FROM t;
| empty set
| INSERT INTO t VALUES (1, 2);
|
v SELECT * FROM t;
empty set
COMMIT;
SELECT * FROM t;
empty set
COMMIT;
SELECT * FROM t;
---------------------
| 1 | 2 |
---------------------
Если вы хотите увидеть “самое свежее” состояние базы данных, используйте либо уровень изоляции READ
COMMITTED, либо:
SELECT * FROM t FOR SHARE;
При уровне изоляции READ COMMITTED каждое постоянное чтение в рамках транзакции устанавливает и считывает свой собственный свежий моментальный снимок. При FOR SHARE происходит блокирующее чтение: сессия SELECT блокируется до тех пор, пока не закончится транзакция, содержащая самые свежие строки (см. Раздел 17.7.2.4, «Блокирующие чтения»).
Постоянное чтение не работает с определёнными операторами DDL:
Постоянное чтение не работает с
DROP TABLE, потому что MySQL не может использовать таблицу, которая была удалена, аInnoDBуничтожает таблицу.Постоянное чтение не работает с операциями
ALTER TABLE, которые создают временную копию исходной таблицы и удаляют исходную таблицу, когда временная копия построена. При повторном выполнении постоянного чтения в рамках транзакции строки в новой таблице не видны, потому что эти строки не существовали, когда был создан моментальный снимок транзакции. В этом случае транзакция возвращает ошибку: , “Структура таблицы изменилась, пожалуйста, повторите транзакцию”.
Тип чтения изменяется для запросов в предложениях, таких как INSERT INTO ...
SELECT, UPDATE
... (SELECT) и CREATE TABLE ...
SELECT, которые не указывают FOR
UPDATE или FOR SHARE:
По умолчанию
InnoDBиспользует более сильные блокировки для этих операторов, и частьSELECTдействует какREAD COMMITTED, где каждое постоянное чтение, даже в рамках одной транзакции, устанавливает и считывает свой собственный свежий моментальный снимок.Чтобы выполнить чтение без блокировок в таких случаях, установите уровень изоляции транзакции на
READ UNCOMMITTEDилиREAD COMMITTED, чтобы избежать установки блокировок на строки, считываемые из выбранной таблицы.
© 2025 Oracle
Licensed under the GPLv2 License.