1

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

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

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

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

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

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

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

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

EDIT: спасибо за очень полезные ответы, у меня возникли проблемы с выбором.

ответ

2

Если ваша база данных находится в режиме 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 и меньше или равна М.

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

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

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

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

+1

Что такое «истинная сериализуемость»? – Quassnoi

+0

Спасибо. К сожалению, DB2 не поддерживает 'LIMIT 1'. Я использую 'ORDER BY' и беру первую строку. Будет ли он блокировать всю таблицу? Я думал об изменении его на 'SELECT * из mytable WHERE key = (SELECT max (key) FROM mytable, где клавиша davka

+0

Просто исправил мои запросы, забыли предложение ORDER BY. '' 'SELECT max (key) FROM mytable, где ключевой

1

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

Это противоречит любому совпадению SELECT FOR UPDATE.

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

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

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

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

+0

Большое спасибо! Несколько разъяснений, пожалуйста: я полагаю, мне нужно быть в режиме «ручной фиксации», верно? Режим изоляции по умолчанию (Read Committed) подходит для этого? Будут ли другие операторы SELECT (не для обновления) читать заблокированные строки? – davka

+0

@davka: ручная фиксация, определенно. Для 'InnoDB' режим изоляции по умолчанию -' REPEATABLE READ', который требуется для блокировки пробелов, если вы не хотите их, в любом режиме изоляции. Параллельные утверждения без предложения FOR FORDATE смогут видеть заблокированные записи (в 'SQL Server', для этого вам нужно включить' SNAPSHOT ISOLATION'). – Quassnoi

+0

Стандарт SQL рекомендует SERIALIZABLE. В этом случае вам не нужно делать SELECT FOR UPDATE. –

1

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

SELECT * FROM MYTABLE WHERE PKCOL BETWEEN 6 AND 10 

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