Каждый вечер, в час закрытия смен, кассы розничной сети начинали «залипать». Не падать - именно залипать: операции, которые днём летали, вечером шли рывками, кассиры нервничали, закрытие растягивалось. Сеть немаленькая - около 200 касс в трёх странах, - и у всех участников было готовое объяснение: «вечером все закрываются одновременно - это блокировки».
Объяснение красивое, логичное и подтверждаемое жизненным опытом каждого, кто работал с 1С под нагрузкой. У него был только один недостаток: оно оказалось неверным. Это статья-детектив о том, как мы искали вечернего душителя касс, почему главный подозреваемый оказался невиновен и при чём здесь настройки сервера СУБД, оставшиеся заводскими с эпохи четырёхъядерных процессоров.
Почему все всегда думают на блокировки
«Вечером тормозит - значит, блокировки» - это рефлекс, и рефлекс не беспочвенный. Закрытие смены - действительно конкурентная операция: одновременные записи в одни регистры, длинные транзакции, пересечения по товарам. Каждый администратор 1С видел в своей жизни настоящие вечерние блокировки, и опыт подсказывает: ищи, кто кого ждёт.
Опасность рефлекса в том, что он подменяет диагностику. Команда начинает «бороться с блокировками»: переписывать проведение, дробить транзакции, крутить управляемые блокировки - а вечер за вечером ничего не меняется, потому что лечат не ту болезнь.
Аудит: главный подозреваемый невиновен
Мы начали с того, с чего надо начинать любую охоту на вечерний пик: не с гипотез, а с замеров. Сняли картину ожиданий на сервере СУБД именно в часы пика - кто чего ждёт, сколько и почему.
Результат обескуражил заказчика: блокировок в пик - ноль. Не «мало», не «в пределах нормы» - ноль значимых ожиданий на блокировках. Сессии не ждали друг друга. Они ждали кое-чего другого: свободных потоков процессора.
Картина ожиданий показывала классические признаки перегретого параллелизма: доминирующие ожидания обмена между потоками параллельных планов и - главное - очередь запросов, которым не хватило рабочих потоков. Две трети запросов в пик просто стояли в очереди за ресурсом, которого формально было в избытке: на сервере 48 ядер.
Механика удушения: как 48 ядер становятся узким местом
Чтобы понять, как сервер с 48 ядрами может задыхаться от нехватки потоков, нужно посмотреть на две настройки, которые в этой системе никто никогда не менял:
cost threshold for parallelism = 5- порог «стоимости» запроса, после которого оптимизатор строит параллельный план. Значение 5 - заводское, и оно откалибровано под железо конца девяностых. По современным меркам «стоимость 5» - это лёгкий запрос: типичный отчётик, средней руки выборка. С таким порогом параллельным становится почти всё.MAXDOP = 0- максимальная степень параллелизма «без ограничений»: каждый параллельный запрос вправе разбежаться по всем ядрам сервера. Все 48.
Теперь сложим. Вечером сотни касс одновременно порождают поток запросов. Почти каждый из них (порог-то - 5) получает параллельный план. Почти каждый параллельный план (MAXDOP = 0) претендует на десятки потоков. Пул рабочих потоков сервера - конечный. Итог: лёгкие запросы, которым хватило бы одного ядра на долю секунды, выстраиваются в очередь за десятками потоков, которые им не нужны.
Это и есть вечерний душитель: не конкуренция за данные, а конкуренция за потоки, устроенная самим сервером из лучших побуждений. Днём, при меньшей плотности запросов, система прощала эти настройки. Вечером - переставала.
Отдельная серия: сосед по инстансу
У детектива был и второй сезон. SQL-сервер в этой инфраструктуре - общий: на одном инстансе живут десятки баз разных систем. В разгар работ весь сервер начал задыхаться уже не по вечерам, а среди дня. Виновник нашёлся быстро: отчёт из соседней базы - 24 параллельных потока, 4 часа 18 минут исполнения. Один запрос удерживал половину процессорных ресурсов сервера четыре часа подряд - при заводских настройках параллелизма он имел на это полное право.
Мораль для всех, кто живёт на общем инстансе: настройки параллелизма - общесерверные. Их выбирают не под одну базу, а под общежитие целиком, и «сосед с тяжёлым отчётом» - это ваш сосед, даже если база не ваша.
Лечение: две настройки, ноль перезапусков
Терапия заняла меньше времени, чем диагностика:
cost threshold for parallelism: 5 → 50. Лёгкие и средние запросы перестали получать параллельные планы - им и не нужно: одно ядро справляется быстрее, чем церемония раздачи работы десяткам потоков. Параллелизм остался тяжёлым отчётам и регламентным операциям - тем, кому он действительно помогает.MAXDOP: 0 → 8. Даже тяжёлый запрос теперь ограничен восемью потоками - достаточно для ускорения, безопасно для соседей.
Обе настройки применяются динамически, без перезапуска сервера и без остановки работы - с заранее согласованным планом отката (вернуть старые значения - те же секунды). Конкретные числа не догма: 50 и 8 - разумная отправная точка для нагруженной 1С на многоядерном сервере, но правильные значения подбираются по картине ожиданий вашей системы, а не копируются из статей. Включая эту.
Результат
Вечерние «залипания» касс прекратились в тот же вечер. Очередь за потоками рассосалась: лёгкие запросы исполняются на одном ядре и не толкаются, тяжёлые получают свои восемь потоков и не захватывают сервер. Жалобы кассиров - привычный вечерний фон проекта - закончились.
Отдельное удовольствие - реакция команды заказчика, которая готовилась к долгой войне с блокировками: перепроектированию проведения, переговорам о технологических окнах, месяцам работ. Вместо этого - две настройки и вечер наблюдений.
Чек-лист: блокировки или параллелизм?
Если у вас «вечером всё тормозит», прежде чем объявлять войну блокировкам:
- Начните с картины ожиданий в час пика - стандартными средствами мониторинга СУБД. Диагноз до лечения: смотрите, чего именно ждут сессии.
- Ждут блокировок? Тогда классика: искать долгие транзакции и горячие объекты. Это отдельная дисциплина - и отдельная статья.
- Ждут потоков и обмена между потоками? Проверьте
cost threshold for parallelismиMAXDOP. Заводские 5 и 0 на многоядерном сервере - мина, которая маскируется под «вечерние блокировки» десятилетиями. - Общий инстанс? Помните: параллелизм настраивается на весь сервер, и самый тяжёлый запрос соседа - тоже ваша проблема.
- Меняете настройки - фиксируйте базовую линию до и картину после. Изменения динамические и обратимые, но «стало лучше» должно быть измерением, а не ощущением.
- Не копируйте чужие значения вслепую - подбирайте по своей картине ожиданий. Отправная точка и метод важнее конкретных чисел.
А если доступа к SQL Server у вас нет
Совет "снимите картину ожиданий" упирается в неприятную реальность: у 1С-ника прав на сервер СУБД чаще всего нет, а у админа нет времени разбираться, что именно ему смотреть. Разговор превращается в "посмотри там, что с блокировками", и возвращается ответ "всё нормально". Поэтому просить надо конкретно.
Со стороны СУБД источников два, и путать их дорого.
sys.dm_os_wait_stats- накопительная статистика с момента запуска службы. Один снимок отсюда показывает среднее за месяцы работы, и вечерний эффект в нём утонет вместе с ночными бэкапами и дневным затишьем. Пользы от неё ровно столько, сколько от разницы двух снимков: сняли в 18:30, сняли в 20:00, вычли.sys.dm_os_waiting_tasks- кто ждёт прямо сейчас. Вот это и нужно в пик: три-четыре снимка с интервалом в минуту в тот самый час, когда кассы залипают.
Что означают типы ожиданий, которые вы там увидите:
CXPACKETиCXCONSUMER- обмен между потоками параллельного плана. Сами по себе не диагноз: параллелизм на то и параллелизм, потоки друг друга ждут по определению.THREADPOOL- вот это диагноз. Запросу не досталось рабочего потока, он стоит в очереди за ресурсом сервера. Именно это ожидание отличает "у нас работает параллелизм" от "у нас параллелизм съел сервер".LCK_M_с любым суффиксом - настоящие блокировки. Их отсутствие в пик и есть тот самый ноль из заголовка.PAGEIOLATCH_- ждём диск, разговор совсем другой.SOS_SCHEDULER_YIELD- упёрлись в процессор физически, а не в раздачу потоков.
Диагноз "перегретый параллелизм" ставится по THREADPOOL вместе с CX-ожиданиями. Диагноз "блокировки" - только по LCK_M_. Если в пик доминируют первые, а вторых нет, войну с проведением можно отменять, не дожидаясь конца квартала.
Проверка со стороны 1С: пятнадцать минут и ни одного права на SQL
Есть способ дешевле, и он целиком внутри платформы. Управляемые блокировки 1С живут не в СУБД, а в менеджере блокировок кластера, и в ожиданиях SQL Server их не видно вовсе. Зато они видны в технологическом журнале, и включается это одним файлом logcfg.xml.
Смотреть надо три события:
TLOCK- установка управляемой блокировки;TTIMEOUT- истёк таймаут ожидания на блокировке, то есть кто-то реально кого-то ждал и не дождался;TDEADLOCK- взаимоблокировка.
Логика простая до неприличия. Тормоза в пик есть, а TTIMEOUT и TDEADLOCK за этот час нет ни одного - значит на уровне платформы никто никого не ждал, и причина ниже, на стороне СУБД или железа. Это ровно тот аргумент, которого не хватает в разговоре "нам кажется, это блокировки": не мнение против мнения, а пустая выборка за час пика.
Вторая половина замера берётся из журнала регистрации: длительность одной и той же операции вечером и днём. Если закрытие смены днём укладывается в секунды, а вечером растягивается в минуты при том же объёме документов, у вас есть число, с которым можно идти к админу. Без числа разговор всегда заканчивается фразой "ну у нас всё в норме".
Итог этой части в одну строку: просить админа надо не "посмотреть блокировки", а снять sys.dm_os_waiting_tasks трижды в час пика и один раз в спокойный час. Разница между двумя выборками отвечает на вопрос быстрее, чем месяц переписки.
Вместо эпилога
Эта история - про цену готовых объяснений. «Вечером тормозит - значит, блокировки» звучало настолько убедительно, что система годами жила с диагнозом, который никто не проверял. А проверка стоила один час снятия ожиданий в пик.
Формальные признаки врут, готовые объяснения врут ещё увереннее. Измеряйте - особенно тогда, когда все вокруг уже «знают», в чём дело. Именно в этот момент проверка дешевле всего и нужнее всего.
Если у вас похожая картина - вечерние деградации, «блокировки», которые никто не видел живьём, общий инстанс с соседями - начните с пункта 1 чек-листа. А своими историями про заводские настройки, дожившие до продакшена, делитесь в комментариях: подозреваю, наша - далеко не рекордная.
Другие наши инструменты диагностики 1С:
- Чек-ап СУБД под 1С - 40+ проверок настроек SQL Server под платформу, включая MAXDOP и cost threshold, о которых эта статья.
- Трансформатор SQL в запрос 1С - что на самом деле делает конкретный медленный запрос из трассировки.
- Оптимизатор временных таблиц - как переписать то, что нашли, и разгрузить tempdb.
- Карта объёмов базы 1С - из чего состоит база и куда ушли гигабайты.
Вступайте в нашу телеграмм-группу Инфостарт