Как произвести фантомное чтение? - PullRequest
23 голосов
/ 26 марта 2011

Используя «повторяемое чтение», можно создать фантомное чтение, но как?Мне это нужно для примера, обучающего студентов CS.

Я думаю, что я должен сделать «SELECT ... WHERE x <= 888» в неиндексированном поле x, с отсутствующим верхним пределом 888,и затем на другом соединении вставьте новую строку со значением чуть ниже 888. </p>

За исключением того, что это не работает.Нужен ли мне очень большой стол?Или что-то еще?

Ответы [ 6 ]

15 голосов
/ 29 ноября 2011

Эрик

Я пришел только из теста с очень большим количеством строк.

Вы никогда не найдете фантомов на InnoDB mysql с подтвержденным чтением или более ограниченным уровнем изоляции. Поясняется в документации:

REPEATABLE READ: для согласованных чтений существует существенное отличие от уровня изоляции READ COMMITTED: все согласованные чтения в рамках одной транзакции читают снимок, созданный при первом чтении . Это соглашение означает, что если вы выполняете несколько простых (неблокирующих) операторов SELECT в одной транзакции, эти операторы SELECT также согласуются друг с другом. См. Раздел 13.6.8.2, «Согласованные считывания без блокировки».

Но вы также не можете найти фантомы на уровне изоляции для чтения: это необходимо, потому что «фантомные строки» должны быть заблокированы, чтобы репликация и восстановление MySQL работали.

Более подробная информация: http://dev.mysql.com/doc/refman/5.1/en/set-transaction.html

Я думаю, вам нужно перейти к другому бренду базы данных, чтобы показать фантомам своим ученикам. Я использую MSSQLSERVER и Oracle .

Ну ... жаль, что ваш первый вопрос.

11 голосов
/ 16 декабря 2016

«Фантомное чтение» в MySQL на уровне изоляции RR скрыто глубоко, но все еще может воспроизвести его.Вот шаги:

  1. создать таблицу ab (первичный ключ int, b int);

  2. Tx1:начать;выберите * из ab;// пустой набор

  3. Tx2:начать;вставить в значения ab (1,1);совершить;
  4. Tx1:выберите * из ab;// пустой набор, ожидаемое фантомное чтение отсутствует.обновить ab set b = 2, где a = 1;// 1 строка затронута.выберите * из ab;// 1 строка фантом читайте тут !!!! совершить;
5 голосов
/ 28 ноября 2011

Возможность воспроизвести фантомные чтения для механизма InnoDB для уровня изоляции REPEATABLE READ сомнительна, потому что InnoDB использует Управление одновременностью нескольких версий - для каждой строки механизм MVCC знает номера транзакций, когда строка была вставлена ​​и удалена, и может воспроизводить историюобновлений строк.

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

Мне удалось воспроизвести PHANTOM READS для уровня изоляции REPEATABLE READдля базы данных Apache Derby , поскольку она не использует многоверсионный контроль параллелизма (версия 10.8.2.2 на момент написания этого ответа).

Для воспроизведения установите соответствующий уровень транзакции (в ij- SQL-клиент Derby):

-- Set autocommit off
autocommit off;
-- Set isolation level corresponding to ANSI REPEATABLE READ
set isolation rs;

T1:

SELECT * FROM TableN;

T2:

INSERT INTO TableN VALUES(55, 1);
COMMIT;

T1 снова:

SELECT * FROM TableN;

СейчасТ1 должен увидеть еще одну строку;

3 голосов
/ 16 декабря 2016

InnoDB должен защищать от фантомных чтений, как написали другие.

Но InnoDB имеет другое странное поведение, связанное с блокировкой.Когда запрос получает блокировку, он всегда получает блокировку для самой последней версии строки.Поэтому попробуйте следующее

CREATE TABLE foo (i INT PRIMARY KEY, val INT);
INSERT INTO foo (i, val) VALUES (1, 10), (2, 20), (3, 30);

Затем в двух одновременных сеансах (откройте два окна терминала):

-- window 1                               -- window 2
START TRANSACTION;
                                          START TRANSACTION;

                                           SELECT * FROM foo;

 UPDATE foo SET val=35 WHERE i=3;

                                           SELECT * FROM foo;

Это должно показать val = 10, 20, 30 в обоих SELECT, так как REPEATABLE-READ означает, что второе окно видит только те данные, которые существовали на момент начала транзакции.

Однако:

                                           SELECT * FROM foo FOR UPDATE;

Второе окно ожидает получения блокировки в строке 3.

COMMIT;

Теперь SELECT во втором окне завершается и показывает строки с val = 10, 20, 35, потому что блокировка строки приводит к тому, что SELECT видит самую последнюю принятую версию.Операции блокировки в InnoDB действуют так, как будто они выполняются в режиме READ-COMMITTED, независимо от уровня изоляции транзакции.

Вы можете даже переключаться назад и вперед:

                                           SELECT * FROM foo;

                                           SELECT * FROM foo FOR UPDATE;

                                           SELECT * FROM foo;

                                           SELECT * FROM foo FOR UPDATE;
0 голосов
/ 10 декабря 2013

В дополнение к хорошему ответу Дани, вы можете использовать Microsoft Sql Server, чтобы показать такое поведение своим студентам.

Сервер Sql показывает фантомные чтения на уровне изоляции повторяемого чтения, как указано в документации здесь .

Postgres подписывается под тем же понятием, что и InnoDb, как объяснено здесь . С Postgres также не происходит фантомного чтения при повторяемом чтении, и поэтому оно также не подходит для ваших дидактических целей.

Sql Server предлагает еще один уровень изоляции, моментальный снимок, который делает то, что MySql InnoDb и Postgres делает при повторяющемся чтении (это реализация без повторов, основанная на версии, реализация повторяемого чтения без фантомного чтения, но не сериализуемая). *

Sql Server Express бесплатен, хотя вам нужен компьютер с Windows. Вы также можете получить учетную запись Windows Azure и показать такое поведение с помощью Sql Azure в Интернете.

0 голосов
/ 27 марта 2011

Фантомное чтение может происходить, потому что не существует блокировок диапазона, например, (псевдокод):

Thread1

Transaction 1

Update TableN set X=2 where X=1

wait(s1)
Select TableN where X=1

Commit 

thread2

Transaction 2:

insert into tableN(id, X) values(55,1)
commit;
notify(s1)

В ВикипедииЕсть еще один пример фантомного чтения: Phantom Reads | wikipedia

Здесь важна синхронизация транзакций, вы можете использовать точки синхронизации.

EDIT Пример использования функции сна mysql (не тестировался):

--on thread 1 Create TableN(id int, x int); insert into TableN(id, X) values(1,1); insert into TableN(id, X) values(2,1); insert into TableN(id, X) values(3,1);</p> <p>BEGIN TRANSACTION; Update TableN set X=2 where X=1 SELECT SLEEP(30) FROM DUAL; select TableN from where X=1; COMMIT; </p> <p>--In other thread, before 20 secs;</p> <p>BEGIN TRANSACTION; insert into TableN(id, X) values(55,1);</p> <p>COMMIT;

...