При работе 1С:Предприятия 8 в клиент-серверном варианте используется режим блокировки на уровне записей таблицы базы данных, в отличие от файл-серверного варианта, в котором блокировка устанавливается на всю таблицу. Это означает, что в клиент-серверном варианте при выполнении транзакции блокироваться будут только те записи, которые необходимы для выполнения текущей операции. Это позволяет увеличить параллельность работы пользователей в конкурентном режиме (например - при вводе документов) и, тем самым, увеличить пропускную способность системы в целом.

Однако использование преимуществ данного режима блокировки требует более внимательного проектирования прикладного кода, осуществляющего доступ к данным в транзакции (например, процедуры обработки проведения документа). В противном случае можно получить эффект прямо противоположный – пропускная способность системы уменьшится. Основной причиной этого является появление взаимных блокировок (deadlocks) при конкурентном доступе к данным со стороны нескольких сессий 1С:Предприятия 8.

Взаимная блокировка (deadlock) – это ситуация, когда транзакции блокируют друг друга таким образом, что дальнейшее выполнение их становится невозможно. Ни одна из участвующих во взаимной блокировке транзакций не может отпустить уже захваченные ей ресурсы до того, как наложит блокировки на все ресурсы, которые ей необходимы для завершения. Получить все необходимые ресурсы мешают уже наложенные другой транзакцией блокировки. Таким образом, получается замкнутый круг. Естественно, и транзакций, и объектов в общем случае может быть сколь угодно много. Разрешить подобную ситуацию без внешнего вмешательства невозможно, и если не предпринимать специальных усилий, то транзакции будут находиться в состоянии ожидания бесконечно долго. Разрешить подобную ситуацию можно лишь путем отмены хотя бы одной из транзакций и в MS SQL Server имеются механизмы определения подобных тупиковых ситуаций и их устранения.

При выборе для отмены одной из участвующих во взаимной блокировке транзакции, MS SQL Server руководствуется следующими соображениями:
  • объем операций, выполненных транзакцией (определяется по количеству записей в журнале транзакций, которые необходимо "откатить");
  • количество операций, которые придется проделать, чтобы произвести отмену транзакции. Сервер будет стараться избегать отмены транзакции, которая практически завершена.

Самостоятельно Microsoft SQL Server отмененную транзакцию заново не запускает, а возвращает сообщение об ошибке. В клиентском приложении, при необходимости, можно предусмотреть обработку данной ситуации и, возможно, перезапуск отмененной транзакции.

Однако следует понимать недостатки данного решения:
  • Возрастает нагрузка на журнал транзакций, так как каждая неудачная попытка тоже будет приводить к записи в журнале, включая запись об откате.
  • Взаимная блокировка не обнаруживается мгновенно. Это снижает производительность системы в целом из-за того, что транзакции вынуждены ждать, пока Microsoft SQL Server не отменит одну из участвующих во взаимоблокировке транзакций.
  • Подобное решение в общем случае отрицательно влияет на масштабируемость системы, поскольку с увеличением нагрузки число взаимных блокировок будет возрастать, причем не линейно. Следовательно, будет расти количество ожидающих транзакций, что в итоге может привести к существенному снижению производительности системы.

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

Рассмотрим два типичных случая, которые могут приводить к взаимным блокировкам:




Различный порядок доступа к ресурсам


Последовательность действий, приводящая к взаимной блокировке:
  1. Транзакция Т1 записывает движения документа и обновляет записи в таблице остатков регистра Р1, устанавливая на эти записи эксклюзивную блокировку.
  2. Транзакция Т2 записывает движения документа и обновляет записи в таблице остатков регистра Р2, устанавливая на эти записи эксклюзивную блокировку.
  3. Транзакция Т1 пытается записать движения документа и обновить записи в таблице остатков регистра Р2. При этом она пытается установить на эти записи эксклюзивную блокировку, но ей это не удается, т.к. на эти записи установлена эксклюзивная блокировка транзакцией Т2. Транзакция Т1 приходится ждать, когда транзакция Т2 закончится и снимет установленную блокировку.
  4. Транзакция Т2 пытается записать движения документа и обновить записи в таблице остатков регистра Р1. При этом она пытается установить на эти записи эксклюзивную блокировку, но ей это не удается, т.к. на эти записи установлена эксклюзивная блокировка транзакцией Т1. Транзакция Т2 приходится ждать, когда транзакция Т1 закончится и снимет установленную блокировку.
Обнаружив такую ситуацию, Microsoft SQL Server принудительно отменяет одну из транзакций. Другая транзакция завершается нормально.

Избежать подобной ситуации можно обеспечив единую последовательность работы с ресурсами во всех транзакциях, т.е. сначала запись регистра Р1, затем Р2. В этом случае транзакция Т2 не смогла бы заблокировать ресурсы, необходимые транзакции Т1 для завершения, и взаимной блокировки не возникло бы, поскольку транзакции Т2 пришлось бы ожидать завершения транзакции Т1. При использовании автоматического режима записи движений документа в модуле проведения (набор записей регистра, в котором регистрируются движения документа, созданный в процедуре проведения документа, будет записан системой автоматически, если он не был записан с помощью метода Записать() в явном виде) единая последовательность обращения к регистрам поддерживается системой. В случае, если запись движений документа осуществляется в явном виде, непосредственно в процедуре проведения, необходимо самостоятельно обеспечить единую последовательность обращения к регистрам в процедурах проведения всех документов во избежание появления взаимных блокировок при одновременной записи и проведении документов различными сессиями 1С:Предприятия 8.

Попытка увеличения уровня блокировки ресурса в последующих операциях (например - чтение и последующая запись)

Последовательность действий, приводящая к взаимной блокировке:
  1. Транзакция Т1 выполняет запрос к таблице остатков регистра Р1 и устанавливает разделяемую блокировку на прочитанные записи.
  2. Транзакция Т2 выполняет запрос к таблице остатков регистра Р1 и устанавливает разделяемую блокировку на прочитанные записи. Поскольку разделяемые блокировки совместимы, то ей удается это сделать.
  3. Транзакция Т1 записывает движения документа и пытается обновить записи в таблице остатков регистра Р1. Для этого требуется установить на эти записи эксклюзивную блокировку. Ей это не удается потому, что на эти записи наложена транзакцией Т2 разделяемая блокировка , не совместимая с эксклюзивной. Транзакция Т1 приходится ждать, когда транзакция Т2 закончится и снимет установленную блокировку.
  4. Транзакция Т2 записывает движения документа и пытается обновить записи в таблице остатков регистра Р1. Для этого требуется установить на эти записи эксклюзивную блокировку. Ей это не удается потому, что на эти записи наложена транзакцией Т1 разделяемая блокировка , не совместимая с эксклюзивной. Транзакция Т2 приходится ждать, когда транзакция Т1 закончится и снимет установленную блокировку.

Можно заметить, что этот процесс никогда бы не закончился, если бы одна из транзакций не была отменена Microsoft SQL Server принудительно.

Можно заметить, что этот процесс никогда бы не закончился, если бы одна из транзакций не была отменена Microsoft SQL Server принудительно.

Избежать подобной ситуации можно, используя при выполнении запроса к таблице остатков регистра Р1 оператор "ДЛЯ ИЗМЕНЕНИЯ". В этом случае на прочитанные записи будет установлена блокировка более высокого уровня - блокировка обновления. Такая блокировка совместима с разделяемой, что позволит транзакциям, осуществляющим чтение данных, на которые установлена блокировка обновления, обращаться к этим данным беспрепятственно. А когда понадобится их обновить, то проблем быть не должно, так как блокировки обновления между собой несовместимы, и, значит, другие транзакции, читающие эти данные для последующего изменения (и естественно тоже запросившие их с блокировкой обновления), будут ждать, пока эти данные поменяются, не препятствуя другим сессиям.