Как создать фантомные чтения?

21

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

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

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

    
задан Erik 26.03.2011 в 20:55
источник

6 ответов

15

Эрик,

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

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

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

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

Более подробная информация: Ссылка

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

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

    
ответ дан dani herrera 28.11.2011 в 22:51
  • Спасибо. Это более или менее объясняет это. Мне нужно будет изучить эту «проблему» через месяц или два. И так называемая проблема просто показывает, что это может произойти ... в другой форме базы данных. –  Erik 20.12.2011 в 14:53
  • @deFreitas Я не написал этот ответ. Я только что отредактировал его. Вы должны перенаправить свой комментарий на danihp, который написал ответ. –  Gili 08.02.2018 в 05:51
  • Моя ошибка, комментарий был направлен на @danihp –  deFreitas 08.02.2018 в 06:33
  • @danihp «Вы никогда не найдете фантомы в mysql InnoDB с прочитанным или более ограниченным уровнем изоляции». На основе моих тестов это не так для READ COMMITTED, только для REPEATABLE READ, Ran SELECT, затем вставил / обновил запись на другом сеансе, а затем снова выполнил выбор в первом сеансе, записи изменились, моя версия MySQL - 5.7. 20 и мой стол использует innoBD –  deFreitas 08.02.2018 в 06:33
6

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

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

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

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

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

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

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

Чтобы воспроизвести, установите правильный уровень транзакции (в ij - клиент Derby SQL):

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

Т1:

SELECT * FROM TableN;

Т2:

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

Т1 снова:

SELECT * FROM TableN;

Теперь T1 должен увидеть еще одну строку;

    
ответ дан Nulldevice 28.11.2011 в 13:21
  • И спасибо вам также! В MySQL это кажется невозможным, поэтому мне может понадобиться использовать Derby или некоторые другие для демонстрации. –  Erik 20.12.2011 в 14:55
1

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;
    
ответ дан Bill Karwin 16.12.2016 в 07:44
0

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

Резьба1

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)

В википедии есть еще один пример фантомного чтения: Фантомные чтения | википедия

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

РЕДАКТИРОВАТЬ Пример использования функции сна mysql (не тестировался):

%pr_e%

НАЧАТЬ СДЕЛКУ; Обновить таблицуN установить X = 2, где X = 1 ВЫБЕРИТЕ СОН (30) ОТ ДВОЙНОГО; выберите TableN, где X = 1; COMMIT;

- в другой теме, до 20 секунд;

НАЧАТЬ СДЕЛКУ; вставить в TableN (id, X) значения (55,1);

COMMIT;     

ответ дан CronosNull 26.03.2011 в 22:37
  • Я думаю, что OP ищет фактический код, который может быть выполнен в MySQL, чтобы продемонстрировать это. –  Martin Smith 26.03.2011 в 23:17
  • Вы правы, Мартин. Я знаю несколько способов, которые теоретически могут дать фантомное чтение, но я никогда не мог его показать. Некоторые из моих учеников старались изо всех сил, но безрезультатно. –  Erik 17.04.2011 в 09:18
  • С потоками java, используя autocommit = false и синхронизацию потоков, которые вы можете создать. –  CronosNull 19.04.2011 в 00:33
  • Нет, я не могу. И не нужно, чтобы Java входила в сцену, так как это просто проблема с базой данных. –  Erik 23.04.2011 в 00:09
  • Затем попробуйте использовать функцию сна (отредактирован ответ, чтобы показать непроверенный пример). –  CronosNull 24.04.2011 в 17:37
0

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

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

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

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

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

    
ответ дан John 10.12.2013 в 18:04