
Массовые блокировки в работе ИС (часть 1)
Конфликты блокировок — пожалуй, самая распространенная и, в то же время, сложнорешаемая проблема при работе с базой данных. Если, например, для ускорения неоптимального кода можно просто нарастить аппаратные «мышцы»
Конфликты блокировок — пожалуй, самая распространенная и, в то же время, сложнорешаемая проблема при работе с базой данных. Если, например, для ускорения неоптимального кода можно просто нарастить аппаратные «мышцы» и на время забыть о проблеме, то с блокировками такой трюк не пройдет: бездумные решения тут почти не дают отдачи, для исправления ситуации приходится включать голову и искать корень проблемы. В этой статье мы как раз и разберем наиболее популярные «корешки», с которыми чаще всего приходится сталкиваться, распутывая случаи зависания пользователей.
Отметим, что сами по себе блокировки ни в коем случае не являются непримиримым злом, с которым нужно бороться всеми возможными способами. Наоборот, блокировки ресурсов — чрезвычайно важный механизм многопользовательской системы, благодаря которому обеспечивается непротиворечивость и цельность обрабатываемых данных. Блокировка, наложенная пользователем, гарантирует, что необходимые данные не будут изменены другой транзакцией, а значит — пользователь получит ожидаемый результат выполнения операции. Но вот когда транзакции начинают блокировать избыточные ресурсы, необязательные для выполнения текущей задачи — начинаются проблемы, «зависания» пользователей, эффективность работы резко падает. Именно с такими проблемами мы и будем бороться.
Прежде чем начать разбор проблем, оговорим пару важных моментов:
Первое. Здесь не будет описания классических артефактов чтения, способов борьбы с ними; уровни изоляции транзакций будут рассмотрены бегло. Все вышеперечисленное — достаточно общая теория, которая и так наверняка известна читателю. При необходимости освежить знания можно в том же MSDN ( Уровень изолированности транзакций ), а в этой статье, ориентированной больше на практику, пространный теоретический экскурс будет явно лишним.
Второе. В статье рассматривается работа Microsoft SQL Server в связке с 1C: Предприятие. Первое название тут важнее второго, поскольку в списке разобранных проблем будут и такие, которые специфичны именно для указанной СУБД. Сервер приложений 1С привносит гораздо меньше специфики: тут, скорее, можно говорить о том, какими командами он не оперирует и, значит, какие проблемы для него неактуальны. Но сказанное не означает, что пользователям других СУБД и приложений статья будет бесполезна — часто можно провести параллели, увидеть схожие механизмы, так что приведенная информация поможет найти решение проблем, возникающих во время работы.
Давным-давно, в далекой-далекой…
Начнем наш обзор с небольшого экскурса в прошлое. В те времена, когда двухъядерными процессорами хвастались не смартфоны, а серверные стойки, самым популярным способом автоматизировать бизнес была платформа 1С 7.7. А самой популярной картинкой, описывающей проблемы в работе пользователей, могла бы быть такая:
Видно, что более 20 пользователей висят на блокировке, ожидая завершения транзакции пользователя 449. Поиск в MSDN по «типу ожидания» подсказывает, что все эти пользователи собираются что-то записать в таблицу; описание ресурса («TAB») говорит о том, что заблокирована не какая-то мелкая область, а целая таблица.
Небольшое расследование показывает, что чаще всего блокировки возникают на таблице _1SJOURN — едином журнале документов в 1С 7.7. В этой таблице хранятся дата, номер и идентификатор каждого документа, выписанного в программе. Проблема в том, что в момент проведения любого документа накладывается эксклюзивная блокировка на всю таблицу. Именно так: даже если пользователи проводят разные виды документов, в один момент времени может быть записан только один документ. При большом входящем потоке документов (например, большое число заявок из мелкооптового магазина) такое поведение становится серьезной проблемой.
К сожалению, штатными средствами исправить такую ситуацию невозможно: платформа 1С 7.7 не предоставляет никаких возможностей для изменения механизма проведения документов. Сразу напрашивается простое, очевидное неправильное решение: просто отключить каким-то образом блокировку на таблице журнала (например, прямым изменением бинарника платформы — автор ни к чему не призывает, но мы же сейчас перебираем все гипотетические решения?) Эйфория от чудесного ускорения работы пользователей испарится сразу, как только два менеджера смогут два раза, независимо друг от друга, продать одну и ту же единицу товара со склада. Или два кассира выдадут больше денег, чем есть в кассе. Или случится что-то еще, что в теории баз данных именуется артефактом Dirty Read и от чего как раз и защищают блокировки. Получается, что, избавляясь от избыточной блокировки журнала, необходимо позаботиться о куче более мелких блокировок на ресурсах, с которыми работают документы. По такому принципу работают, например, Гибкие Блокировки — они переопределяют механизм проведения, исключая блокировку журнала документов, но, в то же время, гарантируют непротиворечивость данных, накладывая дополнительные блокировки там, где это необходимо.
...Иногда они возвращаются
Перенесемся во времени немного вперед, в 2002 год, когда была выпущена платформа 1С 8.0. Архитектура платформы претерпела кардинальные изменения, проблема бутылочного горлышка в виде единого журнала документов осталась в прошлом, параллельность работы пользователей значительно улучшилась. Но жалобы пользователей на блокировки не прекратились.
Дерево блокировок показывает подозрительно знакомую картину: ожидание эксклюзивной блокировки (LCK_M_IX), причем заблокирована целая таблица (TAB). Ну хоть таблица отдельного документа, а не единого журнала — и то хорошо! Что, опять 1С «поленилась»? На самом деле вины разработчика платформы тут нет. 1С 8.х в своих запросах никогда не накладывает табличных блокировок (за исключением случаев работы с временными таблицами — но ведь такие таблицы «живут» только в рамках одного сеанса, нам и так их не с кем делить, так что можно смело блокировать ее всю — никто не обидится). В такой ситуации в игру вступают более глубокие механизмы — а именно, эскалация блокировок на уровне MS SQL.
Каждая блокировка сама по себе — объект, облуживание которого требует процессорного времени и оперативной памяти. Да, конечно, одна блокировка в масштабах современного сервера заметна не больше, чем пчела в огромном ангаре. Но вот когда таких пчёл наберется целый рой… Примерно так рассуждает СУБД, оценивая количество блокировок, накладываемых на таблицу. Изначально, если приложение не даёт явных указаний (а 1С таких указаний не даёт!), на таблицу накладываются «точечные» блокировки уровня записи или страницы данных. Но когда СУБД видит большое число блокировок на одну и ту же таблицу, из одной и той же транзакции, либо же когда в системе ощущается дефицит свободной оперативной памяти, принимается решение: вместо роя мелких блокировок наложить одну, но большую. В результате нагрузка на аппаратные ресурсы снижается, правда захваченными оказываются даже те строки таблицы, которые в транзакции вовсе не были нужны. Пользователи начинают чаще сталкиваться из-за конкуренции за одни и те же таблицы, зато ресурсы используются более экономно.
Перед тем, как бороться с эскалациями блокировок неплохо будет понять, из-за чего они все-таки возникают. Возможно, в программе используются слишком долгие транзакции, изменяющие данные большими пачками. Либо, действительно, системе просто недостаточно свободной оперативной памяти. В таких случаях необходимо лечить причину, а не следствие — каждая из ситуаций чревата и другими проблемами, эскалация блокировок — не самая страшная из них.
Если же понятно, что аппаратных ресурсов системе достаточно, а большие объёмы изменений в таблицах оправданы, эскалацию блокировок можно отключить. Сделать это можно на уровне всего сервера, включив флаг трассировки 1211. Но, начиная с SQL Server 2008, эскалацию блокировок можно отключить и на уровне отельной таблицы, выполнив для нее скрипт: ALTER TABLE имя_таблицы SET LOCK_ESCALATION=DISABLE
Возвращаясь к 1С, следует учитывать, что любые манипуляции с таблицами напрямую в базе данных (изменение опций, добавление индексов и т.п.) не запоминаются конфигуратором и могут быть перезаписаны при очередной реструктуризации. Так что стоит отдельно подготовить скрипт, отключающий эскалацию по нужным таблицам, и выполнять его после обновления конфигурации. Благо, сама операция отключения эскалации занимает доли секунды.
В заключение, пара слов о применимости приведенных советов. В начале раздела было сказано про 2002 год; можно подумать, что описанная проблема для современных систем уже не актуальна. На самом деле это совсем не так: как показывает практика, чем более нагружена система, тем чаще запросы к ней превышают условные пороги включения эскалации. Возможно, в дальнейших редакциях SQL Server такие пороги будут увеличены, но и тогда, а, тем более, сейчас, администраторам высоконагруженных систем следует внимательно следить за динамикой эскалаций блокировок и, при необходимости, вносить правки в работу этого механизма, чтобы обеспечить необходимый уровень параллельной работы пользователей.
С другой стороны — в администрировании баз данных не бывает «правильных» решений, которые применимы везде и всегда. Если пользователи редко сталкиваются с табличными блокировками, преобладают короткие транзакции или база вообще не доросла до объемов, когда эскалации станут проблемой, отключение эскалации не поможет ускорить работу пользователей, но, наоборот, приведет к избыточному потреблению ресурсов. Как и всегда в администрировании БД — любое средство следует использовать только при наличии явных показаний и при ясном понимании последствий.
Все приведенные иллюстрации получены с применением системы мониторинга PerfExpert
Статьи по теме:
- Повышенная нагрузка на диски сервера баз данных
- Аудит производительности 1С: почему висит 1С, почему вылетает 1С - все проблемы 1С
- Увеличение производительности 1С