Рубрики
Без рубрики

Как пессимистическая блокировка базы данных взаимодействует с ВСТАВКОЙ, ОБНОВЛЕНИЕМ и УДАЛИТЬ инструкции SQL

Автор оригинала: Vlad Mihalcea.

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

Как объяснялось ранее , существует два типа явных механизмов блокировки: пессимистические (физические) и оптимистические (логические). В этом посте я собираюсь объяснить, как явная пессимистическая блокировка взаимодействует с операторами DML без запросов (например, вставка, обновление и удаление).

Для предстоящих тестовых случаев мы будем использовать следующие объекты:

Сущность Post может иметь один или несколько комментариев к сообщению , которые связаны с их родительской сущностью через post_id Внешний ключ.

В зависимости от базовых возможностей базы данных запрос также может получить эксклюзивную (запись) или общую (чтение) блокировку строк базы данных, выбранных текущей операцией. Чтобы увидеть, как пессимистичные блокировки взаимодействуют с одновременными операторами INSERT, UPDATE и DELETE, мы можем использовать эксклюзивные блокировки, поскольку они лучше поддерживаются большинством реляционных баз данных.

В следующих примерах Алиса собирается выбрать все Комментарии к сообщению(комментариям) принадлежность к данному Опубликуйте сущность, а также получите эксклюзивную блокировку выбранных записей. Чтобы упростить получение блокировки, мы можем использовать PESSIMISTIC_WRITE LockModeType предлагается API сохранения данных Java. Поэтому запрос Алисы выглядит следующим образом:

List comments = session.createQuery(
    "select c " +
    "from PostComment c " +
    "where c.post.id = :id", PostComment.class)
.setParameter("id", 1L)
.setLockMode(LockModeType.PESSIMISTIC_WRITE)
.getResultList();

Таким образом, Hibernate создаст предложение о блокировке для конкретной базы данных от нашего имени.

Например, в Oracle и MySQL SQL-запрос выглядит следующим образом:

SELECT 
    pc.id as id1_1_, 
    pc.post_id as post_id3_1_, 
    pc.review as review2_1_ 
FROM post_comment pc 
WHERE pc.post_id = 1 
FOR UPDATE

В PostgreSQL также используется дополнительный псевдоним OF :

SELECT 
    pc.id as id1_1_, 
    pc.post_id as post_id3_1_, 
    pc.review as review2_1_ 
FROM post_comment pc 
WHERE pc.post_id = 1 
FOR UPDATE OF pc

В то время как на SQL Server синтаксис немного отличается от предыдущих СУБД:

SELECT 
    pc.id as id1_1_, 
    pc.post_id as post_id3_1_, 
    pc.review as review2_1_ 
FROM post_comment pc 
WITH (UPDLOCK, ROWLOCK) 
WHERE pc.post_id = 1 

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

Заявление об обновлении

Как только записи базы данных заблокированы, никакая инструкция UPDATE не сможет их изменить, даже в ядре базы данных MVCC .

До тех пор, пока Алиса не снимет блокировки, завершив свою транзакцию, заявление Боба об ОБНОВЛЕНИИ блокируется, и его транзакция больше не может продвигаться. После того, как Алиса зафиксировала, блокировки снимаются, и транзакция Боба может продолжаться.

Удалить заявление

Как и при ОБНОВЛЕНИИ, оператор УДАЛЕНИЯ также будет заблокирован блокировками на уровне строк, полученными ранее транзакцией Алисы:

Как только Алиса освободит блокировки, транзакция Боба может быть продолжена, и УДАЛЕНИЕ будет выполнено.

Вставить заявление

В то время как операторы UPDATE и DELETE работают последовательно в большинстве систем реляционных баз данных (например, MySQL, PostgreSQL), оператор INSERT ведет себя по-другому.

Инструкция Insert с MySQL

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

Такое поведение можно наблюдать только при использовании уровня изоляции ПОВТОРЯЕМОГО чтения по умолчанию. При переключении на ЧТЕНИЕ с ФИКСАЦИЕЙ MySQL ведет себя как PostgreSQL. Одним из объяснений может быть то, что блокировки, которые не соответствовали сканированию, снимаются после выполнения запроса. Для получения дополнительной информации прочитайте эту статью в Личном блоге.

Таким образом, ВСТАВКА Боба блокируется до тех пор, пока Алиса не освободит блокировку предиката, которая была получена для всех (существующих и будущих) Комментарий к записи записи, связанные с данным родителем Запись запись.

Инструкция Insert с помощью PostgreSQL

К сожалению, явные блокировки предикатов являются скорее исключением, чем правилом, поскольку большинство СУБД не поддерживают такую конструкцию . По этой причине многие реляционные базы данных ведут себя как PostgreSQL:

Инструкция Боба INSERT выполняется сразу же, даже если транзакция Алисы пыталась заблокировать все записи Оставить комментарий .

В предыдущей статье я писал об аномалии перекоса записи и о том, чем она отличается между 2PL и MVCC, теперь вы можете провести некоторое сходство между предотвращением аномалии перекоса записи и поддержкой физической блокировки предикатов.

Поэтому в большинстве СУБД явные физические блокировки могут предотвращать изменения записей только для записей базы данных, которые существовали на момент блокировки, в то время как будущие записи могут быть добавлены. MySQL является исключением из этого правила, предотвращающим ВСТАВКУ операторов в отношении ряда заблокированных записей.

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