Объясните механизм: почему две транзакции могут одновременно читать одну строку, но изменение этой строки требует дождаться их завершения?
В модели блокировок чтение обычно получает разделяемую блокировку, а изменение требует эксклюзивной блокировки. Разделяемые блокировки совместимы друг с другом, поэтому несколько читателей могут работать одновременно; эксклюзивная блокировка несовместима с ними и выдаётся только после их снятия.
Такой подход появился как базовый способ координации конкурентного доступа к общим данным. Он должен был предотвращать ситуации, когда одна транзакция читает или изменяет данные в момент, когда другая ещё не завершила свою операцию.
Разделение блокировок на разделяемые и эксклюзивные позволило не сериализовать все операции без необходимости: независимые чтения могут выполняться параллельно, а потенциально конфликтующие изменения — координироваться.
Если изменение строки разрешить во время её чтения другими транзакциями, читатели могут получить данные в промежуточном или уже устаревшем состоянии. Если же блокировать строку для любого доступа, даже параллельные чтения начнут ждать друг друга, что снизит пропускную способность.
Поэтому СУБД должна различать совместимые и несовместимые режимы доступа. Неверная оценка этого поведения приводит к неожиданным ожиданиям, тайм-аутам и снижению конкурентности.
При чтении транзакция может получить S-блокировку. Несколько S-блокировок на одной строке совместимы: каждая транзакция только наблюдает данные и не создаёт конфликт с другой операцией чтения.
Для изменения требуется X-блокировка. Она несовместима как с S-блокировками других транзакций, так и с другой X-блокировкой, поскольку изменение должно выполняться при контролируемом доступе к строке. Если строка уже занята читателями, запрос на X-блокировку переходит в ожидание либо завершается ошибкой по тайм-ауту или при обнаружении конфликта.
На практике точное поведение зависит от СУБД и уровня изоляции. В системах с MVCC обычное чтение часто получает версию строки из снимка и не удерживает блокировку, мешающую записи; тогда писатель может не ждать таких читателей, хотя специальные режимы чтения, например блокирующее чтение, снова используют блокировки.
Длительность удержания блокировки определяется политикой СУБД и уровнем изоляции. При строгой двухфазной блокировке блокировки, защищающие изменения, удерживаются до завершения транзакции, чтобы другая транзакция не увидела или не использовала промежуточный результат.
Компромисс таков: блокировки повышают предсказуемость и защищают от конфликтующих операций, но увеличивают ожидание и могут привести к взаимным блокировкам. Поэтому важно сокращать длительность транзакций, избегать ненужных блокирующих чтений и соблюдать единый порядок доступа к данным.
Сервис формирует отчёт, одновременно с которым другой процесс обновляет те же заказы. Вариант с блокировкой каждой строки для чтения обеспечивает стабильное чтение, но замедляет обновления и создаёт очереди. Вариант с обычным чтением через MVCC обычно даёт лучшую конкурентность, однако отчёт может строиться по снимку, который не включает самые свежие изменения.
Выбранный вариант зависит от требования к согласованности: для аналитического отчёта обычно выбирают неблокирующее чтение из согласованного снимка, а для операции, которая затем изменяет выбранные строки, используют блокирующее чтение и короткую транзакцию. Это сохраняет необходимую защиту без блокировки строк на всё время формирования отчёта.
1. Всегда ли обычное чтение блокирует запись?
Нет. В MVCC обычное чтение часто работает с версией строки из снимка и не удерживает блокировку, несовместимую с изменением. Поэтому вывод о том, что любой читатель обязательно заставляет писателя ждать, корректен только для конкретной блокировочной модели или специального блокирующего чтения.
2. Почему эксклюзивная блокировка требуется даже при одном читателе?
Она защищает не только от других читателей, но прежде всего от конкурирующих изменений. Без неё две транзакции могли бы одновременно менять одну строку, а итог зависел бы от порядка записи и мог бы потерять часть обновлений.
3. Что определяет, будет ли ожидание бесконечным?
Ожидание ограничивается настройками тайм-аутов, отменой запроса или обнаружением взаимной блокировки. Если транзакция удерживает блокировку слишком долго, другие операции могут накапливаться в очереди; поэтому длительные транзакции опасны даже без логической ошибки.