Мониторинг MS SQL Server для 1С и учётных систем: ключевые счётчики и готовый шаблон Zabbix
Клиент всегда формулирует одинаково: «1С тормозит, сделайте что-нибудь». В девяти случаях из десяти виноват не код и не конфигурация. Упёрся MS SQL Server — не хватает памяти под буферный пул, диск не успевает отдавать страницы, tempdb захлёбывается во временных таблицах. Ниже — какие счётчики и DMV мы смотрим первыми, какие пороги нас настораживают и как за один вечер всё это разворачивается в Zabbix, чтобы больше не бегать с DBCC вручную при каждой жалобе.
Почему разбор всегда начинается с SQL Server, а не с конфигурации 1С
На нашем сопровождении — десятки баз 1С:Предприятие 8.3 в клиент-серверном варианте. От «Бухгалтерии» на пять пользователей до УТ и ЗУП на 40–50 рабочих мест. Платформа почти везде 8.3.20–8.3.27, СУБД — MS SQL Server 2016, 2019 или 2022 (Standard Edition, реже Web Edition на совсем небольших конфигурациях). Когда прилетает жалоба «стало тормозить», первый вопрос — не «что поменяли в конфигурации», а куда упёрся сервер БД. В память? В диск? В конкурентный доступ? Вот с этого мы и начинаем.
Причина в архитектуре 1С: тонкий или толстый клиент формирует запрос, платформа превращает его в T-SQL, а дальше вся тяжесть — на СУБД. Если у SQL Server не хватает оперативной памяти под буферный пул, каждый повторный запрос читает данные заново с диска. Если диск под базой или под tempdb не тянет по IOPS и латентности, любая операция с временными таблицами (а 1С генерирует их массово — виртуальные таблицы регистров, временные таблицы СКД) начинает залипать на PAGEIOLATCH. Пользователь при этом видит только зависшую форму и жалуется на «1С», хотя конфигурация тут ни при чём.
Дальше по делу: конкретные счётчики, которые мы снимаем при диагностике, пороги, которые на нашей практике сигнализируют о проблеме (где это не рекомендация Microsoft, а наш опыт — отмечу отдельно), и готовый шаблон для Zabbix. Разворачиваем его клиенту сразу, чтобы в следующий раз не приезжать на диагностику руками.
Память и буферный пул: где на самом деле «тормозит 1С»
Первое, что проверяем, — не хватает ли SQL Server оперативной памяти. Смотрим счётчики объекта SQLServer:Memory Manager и SQLServer:Buffer Manager (доступны и через Perfmon, и через DMV sys.dm_os_performance_counters).
- Total Server Memory (KB) — сколько памяти SQL Server реально держит сейчас;
- Target Server Memory (KB) — сколько он хочет держать при текущих настройках
max server memory. Если Total стабильно ниже Target — сервер ещё не дорос до лимита, это нормально сразу после рестарта службы. Если Total почти равен Target и оба близки к выделенномуmax server memory— буферный пул исчерпан, дальше расти некуда; - Page Life Expectancy (PLE) — сколько секунд страница в среднем живёт в буферном пуле, прежде чем её вытеснят. Классический порог из документации Microsoft — 300 секунд, но это правило родом из эпохи, когда типовой буферный пул был 4 ГБ. Для современных серверов с 32–128 ГБ памяти под SQL Server мы в ITfresh считаем ориентиром формулу
(объём буферного пула в ГБ / 4) × 300— это оценка по практике, а не официальный норматив, но на клиентских серверах она ловит деградацию памяти куда раньше, чем плоские 300 секунд; - Buffer cache hit ratio — доля обращений, которые обслужены из памяти без похода на диск. У здоровой базы 1С на постоянной нагрузке — обычно выше 98–99%. Просадка ниже 95% на протяжении рабочего дня — повод смотреть в сторону памяти или в сторону резко выросшей базы данных.
Отдельно проверяем настройку max server memory (MB) в свойствах сервера — на терминальных серверах, где на одной машине крутится ещё и сама 1С, RDS-сессии или файловый сервер, дефолтный «неограниченный» лимит SQL Server регулярно душит остальные службы по памяти. Мы всегда выставляем явный лимит, оставляя операционной системе и остальным сервисам не менее 20–25% RAM, а на серверах с NUMA — считаем отдельно на узел через SQL Server:Buffer Node.
| Счётчик / источник | Что показывает | Порог, на который ориентируемся |
|---|---|---|
| SQLServer:Buffer Manager \ Page life expectancy | Среднее время жизни страницы в буферном пуле, сек. | <300 сек — официальный порог MS; для серверов >16 ГБ используем формулу (ГБ/4)×300 (оценка по практике) |
| SQLServer:Memory Manager \ Total Server Memory (KB) | Память, фактически занятая под буферный пул | Стабильно ≈ Target Server Memory и ≈ max server memory — сигнал «упёрлись в потолок» |
| SQLServer:Buffer Manager \ Buffer cache hit ratio | Доля чтений из кэша, а не с диска | <95% на рабочей нагрузке — оценка по практике, повод разбираться |
| sys.dm_os_process_memory (physical_memory_in_use_kb) | Реальное потребление процесса sqlservr.exe | Сверяем с max server memory и физической RAM хоста |
Диск: латентность важнее, чем «сколько всего IOPS»
Второй блок — подсистема хранения. Здесь мы смотрим не абсолютные IOPS (они у всех разные — HDD RAID, SSD, NVMe, виртуальный диск на гипервизоре), а именно латентность отклика, потому что 1С крайне чувствительна к задержкам на операциях с журналом транзакций и с tempdb.
- PhysicalDisk \ Avg. Disk sec/Read и Avg. Disk sec/Write — среднее время одной операции чтения/записи на диске, где лежат файлы БД. Для файлов данных приемлемо до ~10–20 мс, для журнала транзакций (
.ldf) — счёт должен идти на единицы миллисекунд, потому что запись в лог синхронная и блокирует commit транзакции; - PhysicalDisk \ Avg. Disk Queue Length — длина очереди запросов к диску; устойчиво выше 2 на один физический диск — диск не успевает;
- sys.dm_io_virtual_file_stats — DMV, которая даёт то же самое, но в разрезе конкретного файла базы (данные / лог / tempdb) без необходимости настраивать Perfmon на сервере. Мы обычно смотрим по нему
io_stall_read_ms / num_of_readsиio_stall_write_ms / num_of_writes— это и есть средняя латентность операции в миллисекундах по каждому файлу отдельно.
Как понять, что сервер ждёт именно диск? Смотрим агрегированную статистику ожиданий:
SELECT wait_type, wait_time_ms, waiting_tasks_count FROM sys.dm_os_wait_stats WHERE wait_type IN ('PAGEIOLATCH_SH','PAGEIOLATCH_EX','WRITELOG','ASYNC_IO_COMPLETION') ORDER BY wait_time_ms DESC;
По документации Microsoft, если средние ожидания PAGEIOLATCH стабильно выше 10 мс — подсистема ввода-вывода под давлением: либо не хватает IOPS, либо диск физически не тот класс, что нужен под нагрузку. Ожидание WRITELOG отдельно указывает на журнал транзакций — часто это RAID-контроллер без battery-backed write cache или журнал, лежащий на том же массиве, что и файлы данных. Один из типовых P0-фиксов у нас на выезде — просто развести .mdf и .ldf по разным физическим томам, и жалобы на «тормозит проведение документов» снимаются без единого изменения в 1С.
CPU и параллелизм: когда тормозит не диск, а сам план запроса
Третий сценарий, который мы встречаем регулярно: SQL Server упирается не в диск и не в память, а в CPU. Причина — неудачно распараллеленные запросы. 1С генерирует немало тяжёлых аналитических запросов: отчёты, СКД, закрытие месяца. На многоядерных серверах SQL Server порой «раздувает» план на десятки потоков там, где выгоднее было бы выполнить его последовательно — и вместо ускорения получаем деградацию.
- CXPACKET (и его «преемник» в новых версиях — CXCONSUMER) — ожидание синхронизации потоков параллельного плана. Само по себе не патология, но высокая доля этого ожидания в сумме по серверу — сигнал пересмотреть
MAXDOPиCost Threshold for Parallelism; - На типовом сервере 1С мы обычно выставляем
MAXDOPне больше числа ядер в одном NUMA-узле (для большинства терминальных серверов клиентов это 4–8) и поднимаемCost Threshold for Parallelismс дефолтных 5 до 50–75 — дефолтное значение 5 осталось от SQL Server 2000-х и на современных отчётах 1С почти всегда занижено; - SQLServer:SQL Statistics \ Batch Requests/sec и Compilations/sec — если компиляций почти столько же, сколько запросов, это означает, что план запроса не переиспользуется (частая причина — динамически формируемые запросы СКД без параметризации), и CPU греется на компиляции, а не на полезной работе.
Диагностируем текущую нагрузку по CPU без сторонних утилит через sys.dm_exec_requests и sys.dm_exec_sessions — в паре с sys.dm_exec_sql_text это даёт список самых тяжёлых сейчас запросов с указанием конкретной сессии 1С (по host_process_id и program_name обычно видно, чей это клиентский процесс).
Блокировки и конкурентный доступ: когда «зависла» одна операция, а не весь сервер
Отдельная история — жалобы типа «завис у одного, у остальных всё нормально». Это почти всегда блокировки на уровне строк или таблиц, а не общая просадка сервера. Смотрим:
- LCK_M_* (например,
LCK_M_X,LCK_M_IX) вsys.dm_os_wait_stats— ожидание блокировок конкретного типа; - sys.dm_tran_locks в связке с
sys.dm_exec_requests— кто кого блокирует прямо сейчас, поblocking_session_id; - RESOURCE_SEMAPHORE — по документации Microsoft это ожидание при нехватке памяти на memory grant для выполнения запроса (обычно тяжёлые сортировки/хэш-соединения в отчётах); если это ожидание регулярно в топе — снова возвращаемся к разделу про память, часто помогает не «добавить памяти вообще», а поднять
min memory per queryлибо переписать отчёт; - Уровень изоляции — читаем, включён ли
READ_COMMITTED_SNAPSHOTна базе. 1С в клиент-серверном варианте по рекомендациям платформы должна работать с включённым RCSI: это резко снижает блокировки на чтение за счёт версионирования строк вtempdb, но одновременно повышает нагрузку именно на tempdb — отсюда прямая связь со следующим разделом.
tempdb: самое частое узкое место в базах 1С, которое не видно из интерфейса программы
tempdb в 1С работает на порядок интенсивнее, чем в среднем OLTP-приложении: временные таблицы для виртуальных таблиц регистров, промежуточные результаты СКД, версии строк при включённом RCSI (см. предыдущий раздел) — всё это уходит именно туда. Если tempdb сконфигурирован «по умолчанию, одним файлом», то на нагруженной базе это стабильно узкое место.
Что проверяем и приводим в порядок:
- Количество файлов данных
tempdb— по рекомендации Microsoft стартуем с одного файла на ядро CPU, до 8 файлов, все файлы одинакового размера и с одинаковым автоприростом; - Начиная с SQL Server 2016 поведение trace-флагов 1117 (совместный автоприрост всех файлов) и 1118 (выделение полными экстентами, без примеси в
tempdb) уже включено по умолчанию — отдельно их поднимать не нужно, но на старых серверах 2014 и ниже мы их выставляем вручную; - Ожидания PAGELATCH_UP/EX (не путать с PAGEIOLATCH — это про латч в памяти, а не про физический диск) на страницах GAM/SGAM/PFS — классический маркер, что файлов
tempdbмало для числа параллельных сессий; - Физическое размещение — по возможности отдельный быстрый диск/том под
tempdb, не общий с файлами пользовательских баз.
Короткий скрипт, которым проверяем текущее число файлов tempdb и их размер — делаем это ещё до выезда к клиенту:
SELECT name, physical_name, size/128 AS size_mb, growth FROM tempdb.sys.database_files;
Готовый шаблон в Zabbix: как мы это автоматизируем у клиента
Ручной прогон DMV — разовая история. Чтобы не приезжать каждый раз, когда «опять тормозит», мы разворачиваем клиенту постоянный мониторинг на Zabbix. У большинства он уже стоит для мониторинга инфраструктуры — добавить туда MS SQL Server вопрос одного вечера.
Официальный путь — плагин MSSQL для Zabbix agent 2 (входит в поставку начиная с Zabbix agent 2 версии 6.0) и типовой шаблон «Microsoft SQL Server by Zabbix agent 2» из официального репозитория. Плагин подключается к SQL Server напрямую по протоколу TDS (порт 1433 по умолчанию), поэтому дополнительный ODBC-драйвер на сервере агента не требуется — только сетевая доступность и учётная запись SQL с правами VIEW SERVER STATE.
Шаги внедрения, которые мы повторяем у каждого клиента:
- Заводим отдельного SQL-логина только для мониторинга (не sa!) и даём ему
GRANT VIEW SERVER STATE TO [zbx_monitor]— этого достаточно для чтения всех нужных DMV, права на изменение данных не нужны; - Ставим или обновляем Zabbix agent 2 прямо на сервере с СУБД — либо на соседнем хосте, если агент там, но сетевой доступ к порту 1433 есть.
- В конфиге агента прописываем сессию подключения к базе.
- Импортируем в Zabbix Server шаблон Microsoft SQL Server by Zabbix agent 2, привязываем к узлу сети, назначаем макрос с именем сессии;
- Дефолтные пороги шаблона заточены под абстрактный «типовой» сервер. Для терминалок с 1С это не работает. Поэтому мы всегда подгоняем триггеры под конкретного клиента — PLE, латентность диска, RESOURCE_SEMAPHORE — ориентируясь на значения из таблиц выше.
Фрагмент конфигурации агента (zabbix_agent2.conf или отдельный .d-файл плагина):
Plugins.MSSQL.Sessions.MAIN1C.Uri=sqlserver://SQLSRV01:1433
Plugins.MSSQL.Sessions.MAIN1C.User=zbx_monitor
Plugins.MSSQL.Sessions.MAIN1C.Password=<пароль_из_секрет-хранилища>
Быстрая проверка, что плагин вообще видит сервер — не дожидаясь опроса по расписанию:
zabbix_get -s 127.0.0.1 -k mssql.ping[MAIN1C]
| Группа items в шаблоне | Что мониторим | Наш триггер (клиентский профиль 1С) |
|---|---|---|
| Instance discovery | Автообнаружение баз данных и job-ов SQL Agent на инстансе | Информационно, база для остальных LLD-правил |
| Performance counters (Buffer Manager) | Page life expectancy, buffer cache hit ratio | PLE ниже расчётного порога 15+ минут подряд — Warning; ниже 300 сек — Average |
| Memory Manager | Total/Target Server Memory, Memory Grants Pending | Memory Grants Pending > 0 дольше 5 минут — Average |
| Database status | Состояние БД (ONLINE/RESTORING и т.д.), размер файла лога | Статус ≠ ONLINE — Disaster (немедленно) |
| Backup discovery | Время последнего успешного backup по каждой базе | Полный backup давностью > 26 часов — Average (у 1С обычно ночной full + журнальные) |
| Job discovery (SQL Agent) | Статус последнего выполнения job-ов обслуживания (реиндексация, backup) | Последний запуск job = Failed — High |
Отдельно на уровне ОС держим классический шаблон Windows by Zabbix agent (или Linux-аналог для SQL Server на Linux) для диска и памяти хоста — латентность и очередь диска технически относятся к ОС, а не к плагину MSSQL, и без них шаблон MSSQL показывает только «внутреннюю» картину сервера БД, без физического диска под ней.
Как читать дашборд целиком, а не по одному счётчику
Главная ошибка при самостоятельной настройке мониторинга — реагировать на один сработавший триггер без контекста. PLE ниже порога сам по себе ещё не диагноз. Если это случилось сразу после ночной реиндексации или рестарта службы SQL Server — всё нормально, буферный пул просто прогревается заново. Мы сталкивались с этим не раз: инженер поднимал панику ночью, а наутро оказывалось, что поводов не было.
Наш порядок разбора инцидента в Zabbix — сверху вниз, по убыванию вероятной причины:
- Первым делом смотрим триггеры Database status и Backup. База не в статусе ONLINE? Backup не проходил давно? Это приоритет №1 — к производительности не относится, но само по себе критично.
- Дальше смотрим Memory Grants Pending и PLE в паре. Оба красные и держатся уже несколько часов? Значит, проблема в памяти — идём разбираться с буферным пулом.
- Параллельно поднимаем графики диска на уровне ОС за тот же период: латентность и очередь. Если они тоже выросли — первопричина именно в диске, а память тут ни при чём. SQL Server просто агрессивнее цепляется за страницы, когда подкачка с диска работает медленно.
- Память в норме, диск в норме, а жалобы не прекращаются? Смотрим CPU и метрики RESOURCE_SEMAPHORE, CXPACKET. На нашей практике это почти всегда один тяжёлый отчёт или неудачный план запроса — никакой системной деградации, просто конкретная проблема в конкретном месте.
Этот порядок разбора мы фиксируем прямо в поле Description триггеров в Zabbix. Дежурный инженер, увидевший алерт в два часа ночи, сразу понимает, куда копать — и не звонит нам по каждому чиху.
Что это даёт на практике: конкретный пример из нашего сопровождения
Один показательный случай из практики. Терминальный сервер плюс SQL Server 2019 Standard на одной машине, база УТ 11 на 30+ рабочих мест. Жалобы «зависает при закрытии месяца» шли из месяца в месяц — и каждый раз списывались на тяжёлую операцию: мол, надо просто подождать. После того как мы развернули описанный мониторинг, картина прояснилась уже за первую неделю. PLE регулярно проваливался в момент ночного бэкапа: полная резервная копия писалась на тот же физический том, где лежали файлы данных, и вытесняла буферный пул. Одновременно в это же окно кратно подскакивали ожидания WRITELOG — журнал транзакций находился на том же диске. Два часа работы, одна понятная картина, решение очевидно.
Решение оказалось не в «добавить памяти» и не в «оптимизировать 1С», а в разнесении файлов: .ldf перенесли на отдельный SSD-том, окно backup сдвинули на менее нагруженное время и добавили сжатие backup средствами SQL Server, чтобы сократить время самой операции записи. После этого жалобы на закрытие месяца прекратились полностью, а метрики PLE и WRITELOG в Zabbix стали ровной линией без ночных провалов — именно эти графики мы и показываем клиенту как подтверждение, что проблема закрыта, а не «вроде стало лучше на слух».
Частые вопросы
- Обязательно ли ставить ODBC-драйвер SQL Server, чтобы мониторить его через Zabbix?
- Нет, если используете официальный плагин MSSQL для Zabbix agent 2 — он общается с сервером напрямую по протоколу TDS (тот же порт 1433, что и клиентские подключения 1С), отдельный ODBC-драйвер не требуется. ODBC нужен только если вы собираете метрики альтернативным способом через odbc.get и самописные шаблоны.
- Почему Page Life Expectancy иногда падает даже на здоровом сервере?
- Кратковременное падение PLE — ожидаемое поведение сразу после рестарта службы SQL Server, после ночной реиндексации/обновления статистики или сразу после резервного копирования, которое вытесняет часть буферного пула. Тревожный сигнал — не разовое падение, а устойчиво низкое значение в течение рабочего дня под обычной нагрузкой.
- Нужно ли клиенту с 1С в файловом варианте (без сервера СУБД) что-то из этого мониторинга?
- Нет, всё описанное относится к клиент-серверному варианту 1С:Предприятие с MS SQL Server. Файловая база (.1CD) работает без отдельной СУБД, и там симптомы медленной работы почти всегда связаны с сетевым диском, антивирусом или самим файлом базы, а не со счётчиками SQL Server.
- Сколько занимает внедрение такого мониторинга у клиента, если Zabbix Server уже стоит?
- По нашей практике — один визит: заведение SQL-логина для мониторинга с правом VIEW SERVER STATE, установка/обновление Zabbix agent 2, настройка сессии подключения к MSSQL-плагину и импорт официального шаблона с донастройкой порогов под конкретный сервер укладываются в несколько часов работы.
- Что делать, если RESOURCE_SEMAPHORE регулярно в топе ожиданий, а памяти на сервере вроде достаточно?
- Это ожидание указывает не на нехватку памяти вообще, а на нехватку memory grant под конкретный тяжёлый запрос (обычно сортировки или хэш-соединения в отчётах). Сначала находим сам запрос через sys.dm_exec_requests и sys.dm_exec_query_stats, и часто решение — переписать отчёт или обновить статистику, а не увеличивать max server memory.
