Предотвращает ли изоляция REPEATABLE_READ вставки для запросов без диапазона?

Учитывая следующий псевдокод SQL:

... open transaction ...
SELECT * FROM users WHERE users.id = 5
... some calculation here ...
INSERT INTO users (id) VALUES (5)
... commit transaction ...

изоляция REPEATABLE_READ гарантирует предотвращение вставки Users[id=5] между запросом и вставить, или мне нужно использовать изоляцию SERIALIZABLE для этого?

ПРИМЕЧАНИЕ. Я ищу решение, не зависящее от базы данных, если это возможно. Если нет, предоставьте эмпирические данные из нескольких баз данных, чтобы мы могли работать против какого-то отраслевого консенсуса.

ОБНОВЛЕНИЕ: исходный вопрос содержал неверный запрос, в котором использовались блокировки диапазона. Поскольку цель этого вопроса — сосредоточиться на запросах, которые не требуют блокировки диапазона, я обновил вопрос.


person Gili    schedule 31.07.2013    source источник
comment
Как это может быть независимым от базы данных, когда разные платформы могут иметь разные реализации? Если вам просто нужна цитата из стандарта, почему бы вам не прочитать стандарт? Если вам нужны серьезные ответы здесь, вам, вероятно, следует сосредоточиться на реализациях, специфичных для поставщика, вместо того, чтобы ожидать ответа, как это работает на всех платформах баз данных...   -  person Aaron Bertrand    schedule 31.07.2013
comment
@AaronBertrand, спецификация практически нечитаема людьми, в любом случае :) Что касается ответов, специфичных для поставщика, я считаю, что по возможности следует разрабатывать интерфейс, а не детали реализации. По крайней мере, я ищу своего рода отраслевой консенсус. Кстати, на Stackoverflow есть множество вопросов/ответов, не зависящих от базы данных.   -  person Gili    schedule 31.07.2013
comment
К сожалению, большинство людей работают с одной платформой или с очень ограниченным подмножеством, а работа с платформой означает, что вы должны бросить классную комнату и заняться деталями реальной реализации, поэтому я не уверен, что есть очень большое количество людей, которые могли бы даже предоставить такое консенсус, не говоря уже о практическом применении. Я хорошо знаю, что на SO есть много вопросов/ответов, не зависящих от базы данных, это не значит, что все они хорошие вопросы с хорошими ответами или что их не задавали до того, как такие вопросы были более приняты в целом (стандарты сайта сильно изменились).   -  person Aaron Bertrand    schedule 31.07.2013
comment
Забавно, но я прекрасно читаю спецификацию. Интересно, какая раса делает меня...?   -  person RBarryYoung    schedule 27.08.2013
comment
@RBarryYoung, это настолько читабельно, что даже с наградой никто не смог опубликовать ответ на этот вопрос. Думаю, мы оба знаем ответ на этот вопрос, но указать источник не так-то просто. Если у вас есть хороший ответ, пожалуйста, опубликуйте его.   -  person Gili    schedule 28.08.2013


Ответы (2)


Нет. Уровень изоляции REPEATABLE_READ гарантирует, что вы увидите одни и те же данные только в том случае, если вы повторите SELECT в одной и той же транзакции, а это означает, что реализации (SQL) могут скрыться от вставок вашей транзакции, выполненных другими транзакциями.

Единственный уровень изоляции, способный предотвратить вставки, — это SERIALIZABLE, потому что он гарантирует, что если у вас есть несколько транзакций, работающих одновременно, результаты будут такими, как если бы они выполнялись последовательно в определенном порядке. Конкретный порядок в каждом случае не указывается и не сообщается, но гарантируется, что существует такой порядок, который дал бы такие же конечные результаты.

Одним из условий вопроса является независимость от базы данных (и я предполагаю, что ответ высокого уровня), поэтому я не буду упоминать, как разные уровни изоляции работают в разных реализациях БД или как они преобразуются в блокировки.

person Mario Rossi    schedule 02.09.2013
comment
publib.boulder.ibm.com/infocenter/wxsinfo/v7r0/ представляет собой пример того, как REPEATABLE_READ возвращает разные результаты при запросе диапазона данных. См.: Phantom reads are possible when you are using queries or indexes because locks are not acquired for ranges of data - person Gili; 30.09.2014

REPEATABLE_READ не блокирует таблицу. Это гарантирует, что транзакция увидит одни и те же строки в любой момент. Поясню на примере:

Time  Transaction 1                    Transaction2
 1    Begin Tx 1                        
 2                                     Begin Tx 2
 4    Select count(*) from my_tab;        
 5                                     Select count(*) from my_tab;
 6    Insert into ... my_tab;
 7    Commit;
 8                                     Select count(*) from my_tab;
 9                                     Insert into ... my_tab;
 10                                     Select count(*) from my_tab;
 11                                    Commit;
 12   Begin Tx3
 13   Select count(*) from my_tab;

Если my_tab имеет 10 строк, то результатом подсчета будет:

  • Время 4: 10 рядов
  • Время 5: 10 рядов
  • Время 8: 10 строк, потому что таблица находится в режиме repeateble_read, если режим транзакции read_commited, также будет 10 строк. Но если Tx установлен в read_uncommited, количество строк будет 11.
  • Время 10: поскольку он находится в режиме повторного чтения, количество строк будет равно 11 (десять оригиналов плюс одна вставка в текущей транзакции). Если режим tx read_commited, количество строк будет равно 12 (десять оригиналов плюс одна вставка tx1 и одна вставка текущего tx).
  • Время 13: здесь количество строк будет равно 12 для всех режимов транзакций.

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

ИЗМЕНИТЬ

Из стандарта SQL-92 (страницы 67 и 68), размещенного на http://www.contrib.andrew.cmu.edu/~shadow/sql/sql1992.txt (получено из Википедии):

SQL-транзакция имеет уровень изоляции READ UNCOMMITTED, READ COMMITTED, REPEATABLE READ или SERIALIZABLE. Уровень изоляции SQL-транзакции определяет степень, в которой операции с SQL-данными или схемами в этой SQL-транзакции подвержены влиянию и могут влиять на операции с SQL-данными или схемами в параллельных SQL-транзакциях. Уровень изоляции SQL-транзакции по умолчанию SERIALIZABLE. Уровень может быть явно задан с помощью .

Выполнение параллельных SQL-транзакций на уровне изоляции SERIALIZABLE гарантированно сериализуемо. Сериализуемое выполнение определяется как выполнение операций параллельного выполнения SQL-транзакций, которое производит тот же эффект, что и некоторое последовательное выполнение тех же самых SQL-транзакций. Последовательное выполнение — это такое, при котором каждая SQL-транзакция выполняется до завершения до начала следующей SQL-транзакции.

Уровень изоляции определяет, какие явления могут возникнуть при выполнении параллельных SQL-транзакций. Возможны следующие явления:

1) P1 («грязное чтение»): SQL-транзакция T1 изменяет строку. Затем SQL-транзакция T2 считывает эту строку до того, как T1 выполнит COMMIT. Если затем T1 выполнит ROLLBACK, T2 прочитает строку, которая никогда не была зафиксирована, и поэтому ее можно считать никогда не существовавшей.

2) P2 ("Неповторяемое чтение"): SQL-транзакция T1 читает строку. Затем SQL-транзакция T2 изменяет или удаляет эту строку и выполняет COMMIT. Если затем T1 попытается перечитать строку, он может получить измененное значение или обнаружить, что строка была удалена.

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

Четыре уровня изоляции гарантируют, что каждая SQL-транзакция будет выполнена полностью или не будет выполнена вообще, и никакие обновления не будут потеряны. Уровни изоляции различны в отношении явлений P1, P2 и P3. В таблице 9 «Уровни изоляции SQL-транзакций и три явления» указаны явления, которые возможны и невозможны для данного уровня изоляции.

           Level                P1         P2         P3
    ---------------------------------------------------------
    | READ UNCOMMITTED     | Possible | Possible | Possible |
    ---------------------------------------------------------
    | READ COMMITTED       | Not      |          |          |
    |                      | Possible | Possible | Possible |
    ---------------------------------------------------------
    | REPEATABLE READ      | Not      | Not      |          |
    |                      | Possible | Possible | Possible |
    ---------------------------------------------------------
    | SERIALIZABLE         | Not      | Not      | Not      |
    |                      | Possible | Possible | Possible |
    ---------------------------------------------------------------
    |                                                             |
    | Note: The exclusion of these phenomena or SQL-transactions  |
    |       executing at isolation level SERIALIZABLE is a        |
    |       consequence of the requirement that such transactions |
    |       consequence of the be serializable.                   |
    ---------------------------------------------------------------

ИЗМЕНИТЬ 2

Хорошо, вас интересуют блокировки и блоки, на данный момент то, как поставщики RDMS реализуют это, может различаться (например, SQL Server слишком странен), но для общей картины это может быть полезно, следующее объяснение применимо, когда вы пытаетесь изменять/читать одни и те же данные (строки). Существует два типа блокировок, когда RDMS выполняет транзакцию:

  1. Общие блокировки: этот тип блокировки позволяет другим транзакциям получать доступ к данным. Например, многие транзакции могут одновременно считывать одни и те же данные.
  2. Эксклюзивные блокировки: этот тип блокировки блокирует ресурс, избегая доступа к нему другой транзакции, например, две транзакции не могут одновременно изменять одни и те же данные, транзакция, которая пытается изменить ресурс, должна проверять, нет ли эксклюзивных блокировок. ресурс. Если на ресурсе нет монопольной блокировки, то транзакция получает монопольную блокировку, и пока она не освободит блокировку, ни одна транзакция не может получить блокировку и должна ждать освобождения монопольной блокировки.

Второй момент здесь: Что заблокировано?. Обычно существует два типа замков:

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

Примечание относительно Dead Lock, представьте себе следующий сценарий:

Time  Transaction 1                        Transaction 2
 1    Begin Tx 1                        
 2                                         Begin Tx 2
 4    Update table X set... where id = 5        
 5                                         Update table X set ... where id = 5;
 6    Update table X set... where id = 6
 7    Commit;
 8                                         Commit;

Если база данных настроена на блокировку на уровне строк, то транзакция 2 будет ждать от времени 5 до времени 7, потому что транзакция 1 первой получает монопольную блокировку. Теперь представьте следующий пример:

Time  Transaction 1                        Transaction 2
 1    Begin Tx 1                        
 2                                         Begin Tx 2
 4    Update table X set... where id = 5        
 5                                         Update table X set ... where id = 6;
 6    Update table X set... where id = 6
 7                                         Update table X set ... where id = 5;
 8    Commit;
 9                                         Commit;

Этот сценарий известен как «Мертвая блокировка», потому что во время 6 транзакция 1 будет ждать снятия блокировки, полученной транзакцией 2 во время 5, но во время 7 транзакция 2 должна ждать блокировки, полученной транзакцией 1 во времени. 4. Различные СУБД по-разному управляют тупиковой блокировкой, например, MySQL с InnoDB вызовет исключение тупиковой блокировки для транзакции 2, позволяя транзакции 1 завершиться нормально.

Есть интересные статьи:

С наилучшими пожеланиями

person Ernesto Campohermoso    schedule 31.07.2013
comment
Сохранится ли ваш ответ, если я заменю запрос на SELECT * FROM users WHERE users.id = 5? Это означает, что REPEATABLE_READ предотвращает вставки, пока я не использую блокировки диапазона? - person Gili; 31.07.2013
comment
Да, но в этом контексте вам необходимо проверить уровень блокировки, в зависимости от того, установлен ли он для таблицы или строки уровня, вы можете столкнуться с проблемами блокировки и взаимоблокировками. - person Ernesto Campohermoso; 31.07.2013
comment
Отложив пока в сторону взаимоблокировки, вы говорите, что мне нужно SERIALIZABLE для предотвращения вставок, если используются блокировки диапазона, и REPEATABLE_READ в противном случае. Можете ли вы предоставить ссылки на этот ответ в виде стандарта SQL или эмпирических данных из нескольких баз данных? - person Gili; 31.07.2013
comment
Хорошо, книга с теорией: shop.oreilly.com/product/mobile/0636920022343.do возможно, вы должны прочитать, как oracle, mysql и postgresql управляют повторным чтением с помощью MVCC. В Sql Server история совсем другая - person Ernesto Campohermoso; 31.07.2013
comment
«Время 12: здесь количество строк будет равно 13 для всех режимов транзакций». — либо 13 — опечатка, либо я что-то пропустил. Начальное количество строк равно 10, и каждая из двух транзакций до времени 12 вставляет одну строку. Всего в моей книге 12. - person Andriy M; 31.07.2013
comment
Я обновил вопрос, чтобы сосредоточиться исключительно на запросах, не требующих блокировки диапазона. Пожалуйста, обновите вопрос, чтобы он касался исключительно этого случая (удалите все несвязанное содержимое). - person Gili; 26.08.2013