как заблокировать таблицу БД или диапазон строк для записи? - PullRequest
1 голос
/ 11 ноября 2010

У меня есть простая таблица с первичным ключом. Большинство операций чтения выбирают одну строку по точному значению ключа.

Данные в каждой строке поддерживают некоторую связь со строками до и после нее в ключевом порядке. Поэтому, когда я вставляю новую строку, мне нужно прочитать 2 строки, между которыми она собирается войти, выполнить некоторые вычисления и затем вставить.

Очевидно, что проблема заключается в том, что в то же время другое соединение может добавить строку со значением ключа в том же интервале. Я покрываюсь, если это точно такое же значение ключа, что и вторая вставка потерпит неудачу, но если значение ключа отличается, но в том же интервале, связь может быть нарушена.

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

Я использую ODBC с libodbc ++ оболочкой для C ++ в клиентской программе и бесплатной редакции IBM DB2 (хотя выбор БД все еще может измениться). Вот что я подумал сделать:

  • запуск соединения в режиме автоматической фиксации и изоляции по умолчанию
  • когда нужно добавить новую строку, установить автоматическую фиксацию в false и режим изоляции в сериализованный
  • читать строки до и после нового значения ключа
  • вычислить и вставить новую строку
  • 1022 * совершить *
  • возврат в режим автоматической фиксации и изоляции по умолчанию

Будет ли это делать работу? Будет ли разрешено чтение других транзакций одновременно? Есть ли другие / лучшие способы сделать это?

Кстати, я не вижу в libodbc ++ i / f способа указать транзакцию только для чтения. Это возможно в odbc?

РЕДАКТИРОВАТЬ: спасибо за очень полезные ответы, у меня были проблемы с выбором одного.

Ответы [ 3 ]

2 голосов
/ 11 ноября 2010

Если ваша база данных находится в режиме SERIALIZABLE, у вас не возникнет никаких проблем.Учитывая ключ K, чтобы получить предыдущий и следующий ключи, вы должны выполнить следующие запросы:

select key from keys where key > K order by key limit 1;      # M?
select key from keys where key < K order by key desc limit 1; # I?

Выше работает в MySQL.Этот эквивалентный запрос работает в DB2 (из комментариев):

select key from keys where key = (select min(key) from keys where key > K);
select key from keys where key = (select max(key) from keys where key < K);

Первый запрос устанавливает блокировку диапазона, которая не позволяет другим транзакциям вставлять ключ больше K и меньше или равно M.

Второй запрос устанавливает блокировку диапазона, которая запрещает другим транзакциям вставлять ключ меньше K и больше или равен I.

Уникальный индекс первичного ключа не позволяет дважды вставить K,Таким образом, вы полностью охвачены.

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

Примечание. Для этого требуется база данных, которая поддерживает истинную сериализуемость.К счастью, DB2 делает.Другие СУБД, которые поддерживают истинную сериализуемость: SQLServer и MySQL / InnoDB.СУБД, которые этого не делают: Oracle, PostgreSQL!

1 голос
/ 11 ноября 2010

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

SELECT * FROM MYTABLE WHERE PKCOL BETWEEN 6 AND 10

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

1 голос
/ 11 ноября 2010

Если ваша база данных и механизм хранения позволяют это, вы должны ввести SELECT FOR UPDATE для обеих строк, которые вы пытаетесь вставить между ними.

Это будет конфликтовать с любым одновременным SELECT FOR UPDATE.

Недостатком является то, что блокировка строк 10 и 12 (для вставки 11) также не позволит выбрать 8 и 10 (для вставки 9).

InnoDB в MySQL также может поставить блокировку next-key на индекс, то есть блокировку записи индекса и промежуток между следующей записью.

В этом случае вам нужно всего лишь ввести SELECT FOR UPDATE в первой строке и, таким образом, одновременно вставить строку до этого.

Однако для этого требуется форсировать индекс и предоставить range условие для индекса, которое может или не может быть возможным в зависимости от вашего запроса.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...