Возможно ли сделать фантомное чтение строки, которую кто-то только что обновил? - PullRequest
0 голосов
/ 20 января 2019

Из глоссария MySQL:

фантом: строка, которая появляется в наборе результатов запроса, но не в наборе результатов более раннего запроса. Например, если запрос выполняется дважды внутри транзакции, а тем временем другая транзакция фиксируется после вставки новой строки или , обновляя строку так, чтобы она соответствовала предложению WHERE запроса .

Правильно ли выделена деталь? Если у меня есть

CREATE TABLE  t1 (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `c1` varchar(45) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB;

и уровень изоляции - ПОВТОРЯЕМЫЙ ЧИТАТЬ, а я

mysql> start transaction;
mysql> SELECT * FROM t1 WHERE c1 < 10;
+----+------+
| id | c1   |
+----+------+
|  1 | 4    |
+----+------+
mysql> SELECT * FROM t1 WHERE c1 < 10;
+----+------+
| id | c1   |
+----+------+
|  1 | 4    |
+----+------+

Иногда я могу получить другой результат из более позднего запроса, даже если никто не делает никаких INSERT, а только ОБНОВЛЕНИЯ? Моя версия MySQL 5.7.

Стандарт SQL указывает, что фантомные чтения связаны только с одновременными вставками, хотя слово generate немного сбивает с толку. Из ИСО / МЭК 9075: 1992, Язык базы данных SQL - 30 июля 1992 г. (Второй проект неофициального обзора):

P3 («Фантом»): SQL-транзакция T1 считывает набор строк N, которые удовлетворяют некоторому условию поиска. SQL-транзакция T2 затем выполняет SQL-операторы, которые генерируют одну или несколько строк, которые удовлетворяют условию поиска, используемому SQL-транзакцией T1. Если SQL-транзакция T1 затем повторяет начальное чтение с тем же условием поиска, она получает другую коллекцию строк.

Ответы [ 2 ]

0 голосов
/ 20 января 2019

Уровень изоляции транзакции InnoDB REPEATABLE-READ предотвращает появление фантомных строк, но только в том случае, если ваш запрос SELECT является неблокирующим запросом.

Таким образом, вы можете ВЫБРАТЬ с одними и теми же условиями запроса несколько раз во время транзакции, и вы гарантированно будете получать один и тот же результат раз за разом, даже когда другие сеансы вставляют, обновляют или удаляют строки способами, которые могут повлиять на ваш набор результатов. Как только вы начнете новую транзакцию, ваш запрос увидит изменения в строках, которые происходили за это время.

Но у InnoDB есть странный случай: если вы запускаете блокирующий запрос на чтение , например, один из следующих:

SELECT * FROM t1 WHERE c1 < 10 FOR UPDATE

SELECT * FROM t1 WHERE c1 < 10 LOCK IN SHARE MODE

SELECT * FROM t1 WHERE c1 < 10 FOR SHARE -- MySQL 8.0 syntax

Тогда SELECT «увидит» результаты одновременных изменений данных, как если бы ваша транзакция была запущена как транзакция READ-COMMITTED.

Вы можете даже переключаться между блокирующими запросами на чтение и неблокирующими запросами на чтение в одной и той же транзакции REPEATABLE-READ, и вы увидите разные наборы результатов для каждого. Так что помните об этом, если вы используете блокирующие операторы SELECT.

Я думаю, что слово «генерировать» в приведенной вами выдержке предназначено для обозначения INSERT или UPDATE. Им нужен был термин, чтобы подать заявку в обоих случаях, потому что, я думаю, им не хотелось писать более четкую фразу типа «вставить или обновить».

0 голосов
/ 20 января 2019

Насколько мне известно, невозможно (не так ли?) Получить фантомную строку в транзакции с использованием REPEATABLE READ.

При создании транзакции в рамках блокировки повторяемого чтения mysql создает «снимок» данных после запуска первого запроса в транзакции.

Таким образом, любые операторы select получат данные в этом снимке.

Два предостережения из известных мне документов могут привести к неожиданным результатам:

  1. Любые запросы на чтение, сделанные в транзакции, будут влиять на будущие операторы SELECT (что, вероятно, не является неожиданным, но может вызвать путаницу).
  2. Другие запросы записи, сделанные в базу данных из других транзакций, могут повлиять на запросы записи, сделанные в текущей транзакции.

Пункт 2 - более запутанный - см. примечание в документах здесь для более подробного объяснения. Это также более подробно описывает уровень изоляции повторяемого чтения.

Похоже, InnoDB защищает от фантомных рядов .

Чтобы предотвратить фантомы, InnoDB использует алгоритм, называемый блокировкой следующего ключа, который сочетает блокировку строки индекса с блокировкой пробела. InnoDB выполняет блокировку на уровне строк таким образом, что при поиске или сканировании индекса таблицы он устанавливает общие или эксклюзивные блокировки для записей индекса, с которыми сталкивается. Таким образом, блокировки на уровне строк фактически являются блокировками записей индекса.

Блокировка зазора может быть отключена, как описано в Раздел 15.7.1, «Блокировка InnoDB» . Это может вызвать фантомные проблемы, потому что другие сеансы могут вставлять новые строки в промежутки, когда блокировка промежутка отключена.

Я бы хотел знать, есть ли у вас другая обстановка, которая создает такую ​​ситуацию в вашем случае! Кто-нибудь из связанных документов проясняет это? Если это довольно «глубокий» вопрос mysql, возможно, лучше попробовать обмен стеком DBA.

...