7.5.8.2 Чтение с блокировкой
В некоторых случаях использовать согласованное чтение нецелесообразно.
Приведем пример. Допустим, что необходимо добавить новую строку в таблицу
CHILD
, предварительно убедившись, что для нее имеется родительская строка
в таблице PARENT
.
Предположим, что для чтения таблицы PARENT
было использовано согласованное
чтение, и в таблице была обнаружена родительская строка. Можно ли теперь
безопасно добавить дочернюю строку в таблицу CHILD
? Нет, потому что в это
время другой пользователь мог без вашего ведома удалить родительскую
строку из таблицы PARENT
.
В данной ситуации необходимо выполнить операцию SELECT
в режиме
блокировки, LOCK IN SHARE MODE
.
SELECT * FROM PARENT WHERE NAME = 'Jones' LOCK IN SHARE MODE;
Выполнение чтения в режиме совместного использования (share mode
)
означает, что считываются самые новые доступные данные и производится
блокировка строк, чтение которых осуществляется. Если последние данные
принадлежат еще не зафиксированной транзакции, мы ждем, пока транзакция
не будет зафиксирована. Блокировка в режиме совместного использования не
позволяет другим пользователям обновить или удалить читаемую строку. После
того, как указанный выше запрос вернет родительскую строку 'Jones'
, мы
можем безопасно добавить дочернюю строку в таблицу CHILD
и зафиксировать
транзакцию. В этом примере показано, как использовать целостность
ссылочных данных в своей программе.
Рассмотрим еще один пример. Пусть у нас есть поле целочисленного счетчика
в таблице CHILD_CODES
, которое мы используем для назначения уникального
идентификатора каждой дочерней записи, добавляемой к таблице CHILD
.
Очевидно, что использование согласованного чтения или чтения в режиме
совместного доступа для получения текущего значения счетчика не подходит,
так как два пользователя базы данных могут получить одно и то же значение
счетчика и создать дублирующиеся ключи при добавлении двух дочерних
записей в таблицу.
Для этого случая возможны два способа произвести чтение и увеличить
значение счетчика: (1) сначала обновить значение счетчика, увеличив его на
1, и только после этого прочитать его или (2) сначала прочитать счетчик в
режиме блокировки FOR UPDATE
, а после этого увеличить его значение:
SELECT COUNTER_FIELD FROM CHILD_CODES FOR UPDATE;
UPDATE CHILD_CODES SET COUNTER_FIELD = COUNTER_FIELD + 1;
Оператор SELECT ... FOR UPDATE
прочитает последние доступные данные с
установкой отдельной блокировки на каждую считываемую строку. Таким
образом, блокировка на строки устанавливается точно так же, как и в случае
поиска по UPDATE
.