SQL Server 2025 зависает при online баз: проверка workers
АйТи Фреш
1С и базы данных

Сервер встал после перезапуска, а Always On нет: как доказать, что SQL Server 2025 исчерпал workers

Автор: , директор ООО «АйТи-Фреш» · · ~17 мин чтения
Сервер с заполненными потоками и очередью баз: исчерпание workers в SQL Server 2025
Потоки закончились раньше, чем пользователи пришли.

Если после перезапуска SQL Server 2025 с десятками баз не пускает клиентов, а процессор и диск простаивают, проверьте workers: активных потоков почти столько же, сколько max_workers_count, а в очереди висят задачи THREADPOOL. Always On для этого не нужен. Ниже три запроса через DAC, отличие от долгих запросов и порядок действий.

Как понять, что экземпляр встал именно на потоках, а не на диске или блокировках

Картина всегда одна и та же. Служба SQL Server запущена, порт отвечает, но клиенты получают таймауты, а SSMS подключается минуту или не подключается вовсе. Диск молчит, память свободна, процессор на единицах процентов. Админ в такой момент обычно идёт проверять сеть, антивирус и «не легла ли 1С». Я сам так делал первые годы. Теперь первым делом проверяю другое: не кончились ли у экземпляра рабочие потоки. Если у вас 1С на SQL Server с десятками баз на одном экземпляре, эта проверка занимает пять минут и сразу отсекает половину гипотез.

Worker — это поток, который выполняет задачи SQL Server: запросы, логины, выходы из сессий. Их число ограничено параметром max worker threads. Значение по умолчанию 0 означает, что SQL Server сам рассчитывает лимит при старте по числу логических процессоров. Для 64-битной машины, начиная с SQL Server 2017, документация даёт такие значения: до четырёх процессоров включительно 512, на восьми 576, на шестнадцати 704, на тридцати двух 960. Когда свободных потоков нет, новая задача встаёт в очередь и ждёт. Этот тип ожидания называется THREADPOOL.

Важный нюанс из документации: в большинстве случаев такое ожидание означает, что потоки заняты долгими запросами, а не что лимит маленький. Поэтому одного факта «есть THREADPOOL» мало. Надо ответить на вопрос, чем заняты потоки. Если пользовательских запросов почти нет, а потоки заняты, значит, их держит что-то фоновое. На SQL Server 2025 это один из известных сценариев, и ему посвящён следующий раздел.

Почему Always On тут ни при чём, хотя виновата функция для вторичных реплик

В SQL Server 2025 появилась функция persisted statistics for readable secondary replicas. Суть из документации: первичная реплика сохраняет статистику как постоянную, и её можно отправлять на все вторичные реплики. Механизм построен на инфраструктуре Query Store для читаемых вторичных реплик, а она в SQL Server 2025 включена по умолчанию. Отдельного переключателя на уровне базы у функции нет: документация прямо пишет, что она работает, пока включено автосоздание статистики и два параметра READABLE_SECONDARY_TEMPORARY_STATS_AUTO_CREATE и READABLE_SECONDARY_TEMPORARY_STATS_AUTO_UPDATE, а это конфигурация по умолчанию.

Теперь главное. В списке известных проблем SQL Server 2025 есть пункт: сервер может стать медленным или перестать отвечать после создания или перевода в online большого числа баз. Причина названа в самом пункте: фоновый рабочий поток на каждую базу, который создаётся в рамках этой функции. Он появляется, когда база переходит в online, и создаёт нагрузку на пул потоков и падение отзывчивости экземпляра, даже если вторичных реплик нет. Корпоративный Always On вам для этого не нужен, достаточно версии 2025 и большого числа баз.

Поэтому в поисковых запросах «THREADPOOL без Always On» и «статистика вторичных реплик» кажутся несвязанными. Админ видит у себя одиночный сервер, читает про вторичные реплики и справедливо думает, что это не про него. Но функция включена безусловно. Чтобы не гадать, быстро убедитесь, что реплик у вас действительно нет, и идите к измерениям. Свойство IsHadrEnabled возвращает 0, если компонент Always On availability groups выключен, и 1, если включён. Следите, чтобы NULL не принимали за ноль: это означает неверный запрос или ошибку.

Эту историю я уже разбирал с другой стороны: как лечить и куда вписывать флаг, в статье про THREADPOOL и флаг 15608. Здесь я иду от обратного: как доказать диагноз за несколько минут и не перепутать его с обычной перегрузкой. Если вы ещё не уверены, что это ваш случай, начинайте с этой статьи, а к лечению переходите потом.

Документация Microsoft сообщает, что исправление для этой проблемы определено для будущего выпуска SQL Server 2025. Перед любыми действиями откройте страницу Known Issues и заметки к вашему накопительному обновлению: возможно, у вас уже стоит сборка с исправлением, и тогда флаг не нужен.
Схема: старт службы, базы в online, фоновый поток на базу, пул workers и очередь THREADPOOL
Источник потоков функция по умолчанию, а не Always On.

Три запроса, которые за пять минут покажут исчерпание workers

Первая проблема: когда экземпляр завис, обычное подключение может не пройти. Для такого случая у SQL Server есть выделенное административное подключение, DAC. Оно доступно только членам роли sysadmin, по умолчанию только с самого сервера и допускает одно соединение на экземпляр. Подключаться можно через sqlcmd с ключом -A или с префиксом admin: перед именем экземпляра. Я рекомендую сразу указывать базу master: если база по умолчанию у вашей учётной записи не в online, подключение вернёт ошибку 4060. В SSMS перед этим отключите IntelliSense в новом окне, иначе редактор попытается открыть второе соединение и потеряет DAC.

Вот как это выглядит на сервере. Запрос на DAC не должен быть тяжёлым: документация просит не гонять через него ресурсоёмкие DMV и не запускать BACKUP или RESTORE. Все запросы ниже лёгкие.

sqlcmd -S admin:SQL01 -E -d master

Запрос номер один даёт потолок и момент старта. Колонка max_workers_count показывает максимальное число workers, которое может быть создано, а sqlserver_start_time помогает связать картину со временем перезапуска. Запрос номер два суммирует состояние обычных планировщиков. Исключаем планировщик DAC, поэтому фильтруем по точному значению статуса. Колонка active_workers_count считает активные потоки, у которых есть задача, а work_queue_count показывает задачи, которые ждут свободного потока.

-- 1. Потолок потоков и время старта
SELECT sqlserver_start_time, cpu_count, scheduler_count, max_workers_count
FROM sys.dm_os_sys_info;

-- 2. Занятость потоков по обычным планировщикам
SELECT SUM(current_workers_count) AS workers_total,
       SUM(active_workers_count)  AS workers_active,
       SUM(runnable_tasks_count)  AS runnable,
       SUM(work_queue_count)      AS queued_tasks
FROM sys.dm_os_schedulers
WHERE status = 'VISIBLE ONLINE';

-- 3. Кто стоит в очереди на поток прямо сейчас
SELECT wait_type, COUNT(*) AS tasks, MAX(wait_duration_ms) AS max_wait_ms
FROM sys.dm_os_waiting_tasks
WHERE wait_type = 'THREADPOOL'
GROUP BY wait_type;

Как читать результат. Если workers_active подошёл вплотную к max_workers_count, в queued_tasks есть число больше нуля, а в третьем запросе есть строки THREADPOOL, потоки исчерпаны. Если THREADPOOL нет, а потоки заняты не полностью, значит, дело в другом, и этот раздел вас не касается. В документации на sys.dm_os_schedulers есть удобный ориентир: если work_queue_count больше нуля, то задачи ждут, пока их подхватит worker, а в примере с нулевой очередью и большим runnable_tasks_count потоков хватает, задачи просто ждут своей очереди на планировщике. Я читаю это как нехватку процессорного времени. Это разные болезни с разным лечением.

Для истории полезен ещё и накопительный счётчик ожиданий. Он не сбрасывается до перезапуска службы или команды очистки, поэтому сразу покажет, сталкивался ли экземпляр с нехваткой потоков со времени старта.

SELECT wait_type, waiting_tasks_count, wait_time_ms, signal_wait_time_ms
FROM sys.dm_os_wait_stats
WHERE wait_type = 'THREADPOOL';

Как отличить нехватку потоков из-за online баз от долгих запросов

Это главное умение в диагностике. THREADPOOL сам по себе — симптом, а причин у него две принципиально разные. Либо потоки держат пользовательские запросы, которые долго выполняются или стоят на блокировках. Либо потоки держит служебная работа, а пользователей почти нет. Лечить их надо по-разному, и если перепутать, то в первом случае вы зря будете править параметры запуска, а во втором будете искать «плохой запрос», которого нет.

Я смотрю на три вещи. Первая: время. Привязываю картину к sqlserver_start_time и ко времени, когда базы переходили в online. Если проблема началась в течение минут после старта службы и пользовательских сессий ещё мало, это подозрительно. Вторая: состав баз. Запрос по sys.databases с группировкой по state_desc покажет, сколько баз уже в ONLINE, а сколько ещё в RECOVERING, RECOVERY_PENDING или другом состоянии. Если часть баз проходит восстановление, а потоки уже заняты, связь очевидна. Третья: пользовательская нагрузка. Если запросов от клиентов в этот момент нет или их единицы, а потоков занято почти всё, держит их не пользователь.

SELECT state_desc, COUNT(*) AS dbs, SUM(CAST(is_auto_close_on AS int)) AS auto_close_dbs
FROM sys.databases
GROUP BY state_desc;

SELECT COUNT(*) AS user_requests
FROM sys.dm_exec_requests AS r
JOIN sys.dm_exec_sessions AS s ON s.session_id = r.session_id
WHERE s.is_user_process = 1;

Колонка is_auto_close_on в запросе нужна не случайно. Если у баз включён AUTO_CLOSE, они закрываются и открываются заново, и каждое открытие — это снова путь базы в online. Документация по sys.databases отдельно отмечает, что при включённом AUTO_CLOSE и закрытой базе часть колонок имеет значение NULL. На серверах, где собраны десятки мелких баз, я встречаю AUTO_CLOSE до сих пор, и это отдельная причина, которую нужно знать до применения любых флагов. Если auto_close_dbs не ноль, сначала обратите на это внимание.

Ещё один слой проверки взят прямо из документации по max worker threads. Если число потоков превышает настроенное значение, вы можете получить список задач, которые порождены системными сессиями: запрос объединяет sys.dm_exec_sessions, sys.dm_exec_requests, sys.dm_os_tasks и sys.dm_os_workers с условием is_user_process = 0. Я использую его, когда хочу увидеть, чем заняты потоки, если пользователей нет. Запрос не самый лёгкий, поэтому через DAC выполняйте его осознанно и один раз, а не в цикле.

Вывод по таблице ниже я проверяю на каждом новом случае. Она не заменяет измерений, а помогает расставить гипотезы по приоритету. | Что видно | Вероятная причина | Что делать первым | |---|---|---| | Потоки заняты, пользователей мало, недавно был старт службы | фоновые потоки при переводе баз в online | сверить версию со списком известных проблем и запланировать флаг запуска | | Потоки заняты, много длинных пользовательских запросов | долгие запросы, блокировки, ввод-вывод | искать корневую причину, не трогать лимит | | Очереди нет, растёт runnable_tasks_count | нехватка процессора | смотреть планы и нагрузку на CPU | | THREADPOOL с самого старта на любой версии | слишком много активных сессий или заниженный лимит | пересмотреть max worker threads вместе с нагрузкой |

Не поднимайте max worker threads вслепую. Документация Microsoft прямо советует сначала искать корневую причину: чаще потоки заняты долгими запросами, а не лимит мал. Параметр расширенный и меняется командой sp_configure с последующим RECONFIGURE, перезапуск службы не требуется, но каждая лишняя нить потребляет ресурсы.
Дерево решений: при THREADPOOL и отсутствии пользовательских запросов включить флаг 15608
Сначала доказываем, чем заняты потоки, потом лечим.

Что делать после подтверждения: флаг 15608 при запуске и порядок действий

Документированное временное решение Microsoft для этого сценария — включить флаг трассировки 15608 в параметрах запуска и перезапустить SQL Server. В справочнике по флагам он описан так: отключает функцию persisted statistics for readable secondary replicas, область действия только запуск. Эту деталь легко упустить. Включить флаг командой DBCC TRACEON на работающем экземпляре бессмысленно: в известных проблемах прямо сказано, что включение после старта не останавливает потоки, уже созданные для баз, которые перешли в online. И в сценарии без вторичных реплик флаг всё равно нужен как временная мера, чтобы фоновый поток не создавался при запуске базы.

На Windows путь такой. Открываем SQL Server Configuration Manager, раздел SQL Server Services, свойства службы Database Engine, вкладка Startup Parameters. Добавляем параметр -T15608, нажимаем OK и перезапускаем службу. Здесь есть правило из документации по параметрам запуска: букву T пишем заглавной и без пробела перед номером. Строчная t тоже принимается, но включает другие внутренние флаги, нужные только инженерам поддержки Microsoft, и вам это ни к чему. Конфигурационный менеджер сохраняет параметры так, что экземпляр запускается с ними каждый раз.

На Linux используется утилита mssql-conf. Команда включает флаг для запуска службы, затем службу нужно перезапустить.

sudo /opt/mssql/bin/mssql-conf traceflag 15608 on
sudo systemctl restart mssql-server

После перезапуска проверьте, что флаг действует. Для этого есть DBCC TRACESTATUS с номером флага и параметром -1, он покажет глобальный статус. Но сначала про главное: перезапуск — это простой, и на экземпляре с десятками баз он может сам повторить проблему. Поэтому заранее договоритесь с бизнесом об окне, сделайте свежие копии и проверьте, что восстановление из них реально работает. Как проверять, я описывал в статье про то, что реально восстанавливается из резервных копий баз 1С. Перезапускать экземпляр, не имея проверенной копии, я не буду ни в каком случае.

Отдельно скажу про приоритеты. Если сервер завис прямо сейчас, а ждать окна нельзя, самое безопасное — подождать, пока все базы перейдут в online и фоновая работа закончится, и параллельно через DAC убедиться, что это вообще тот случай. Поднимать лимит потоков в аварийном режиме можно, но это именно временная мера, и после неё нужно вернуть значение 0. Устранять проблему целиком имеет смысл флагом при запуске. Изменять число баз, ставить обновление, выносить архивы — это уже плановые действия, делать их в панике я не советую.

Было и стало: 143 базы и 253 из 256 потоков против 91 базы и около 60 из 512 после флага 15608
Флаг, штатный лимит и уборка вернули запас.

Разбор: «Чемпион-клуб», 27 рабочих мест, 143 базы на одном экземпляре

Условный пример из моей практики. Сеть спортивных залов «Чемпион-клуб»: 27 рабочих мест, один сервер на Windows Server 2022 под SQL Server 2025, 4 виртуальных процессора, 16 ГБ памяти. По таблице из документации автоматический лимит потоков для четырёх процессоров — 512, но, как выяснилось позже, у них он был другим. Баз на экземпляре накопилось 143: шесть рабочих баз 1С и учёта абонементов, остальное — ежемесячные архивы журналов турникетов, тестовые копии и служебные базы. Наращивали их годами, и никто не считал.

В воскресенье в 06:40 сервер перезагрузился после установки обновлений Windows. В 07:05 администратор клуба позвонил: на ресепшене не открывается кассовая программа, хотя зал открывается в восемь. Я подключился с сервера по DAC. Ни процессор, ни диск проблемой не были. Первый запрос показал max_workers_count = 256, а не 512. Запрос к sys.configurations подтвердил: max worker threads выставлен вручную в 256, когда-то прежний подрядчик так «экономил память». Второй показал workers_active = 253 и queued_tasks = 37. Третий нашёл 21 задачу в ожидании THREADPOOL, максимальное ожидание около 41 секунды. Пользовательских запросов в sys.dm_exec_requests было три. По sys.databases на тот момент 97 баз были в ONLINE, остальные ещё проходили восстановление.

Решающий признак появился через двадцать минут. Все 143 базы стали ONLINE, пользователей по-прежнему было трое, а workers_active держался в районе 170: по порядку величины это поток на каждую из 143 баз плюс системная работа. То есть потоки занимали не клиенты, а служебная фоновая работа после старта. Ручной лимит 256 превратил неприятность в аварию: пока базы проходили восстановление, к фоновым потокам добавлялись задачи recovery, и запаса не осталось. Версия экземпляра у них была до исправления, так что мы сверили её со списком известных проблем на странице Microsoft и убедились, что описание совпадает один к одному: SQL Server 2025, много баз, переход в online, вторичных реплик нет. К 07:40 нагрузка спала, экземпляр ответил, простой для бизнеса составил около 35 минут и закончился до открытия зала.

Дальше работали планово. В следующее воскресенье в окно я вернул max worker threads в 0 через sp_configure и RECONFIGURE, добавил параметр запуска -T15608 через Configuration Manager и перезапустил службу, предварительно проверив свежие резервные копии. Из 143 баз 52 архивные мы вынесли из экземпляра: сняли копию, проверили восстановление на тестовом сервере и только потом удалили. Остались 91 база. После этого старт службы до готовности к работе занял около четырёх минут, а workers_active на простое держался около 60 из 512. Если же вы думаете, что этих мер хватит навсегда, то нет: когда выйдет сборка с исправлением, флаг надо убрать и проверить, что поведение нормальное. Заметку об этом я оставил в паспорте сервера, чтобы через полгода никто не удивлялся лишнему параметру.

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

Частые вопросы

Нужен ли Always On, чтобы SQL Server 2025 упёрся в потоки при запуске баз?

Нет. По списку известных проблем Microsoft фоновый поток на каждую базу создаётся в рамках функции persisted statistics for readable secondary replicas, она включена по умолчанию, и нагрузка на потоки возможна, даже если вторичных реплик нет.

Как подключиться к зависшему экземпляру, чтобы выполнить диагностику?

Через выделенное административное подключение: sqlcmd -S admin:имя_экземпляра -E -d master или ключ -A. Нужна роль sysadmin, по умолчанию подключение разрешено только с самого сервера, одновременно возможно одно DAC-соединение.

Можно ли включить флаг 15608 командой DBCC TRACEON без перезапуска?

Нет. Для флага указана область действия «только запуск», а Microsoft уточняет, что включение после старта не остановит потоки, уже созданные для баз в online. Нужен параметр запуска и перезапуск службы.

Достаточно ли просто поднять max worker threads?

Как временная мера возможно: изменение вступает в силу после RECONFIGURE без перезапуска. Но документация советует сначала найти корневую причину, и в этом сценарии лимит не устраняет источник потоков. Постоянным решением это делать не стоит.

Как узнать, что у меня нет Always On?

Выполните SELECT SERVERPROPERTY('IsHadrEnabled'). Значение 0 означает, что компонент Always On availability groups выключен, 1 означает, что включён. Свойство относится только к группам доступности.

Когда флаг 15608 можно убрать?

Когда установлена сборка с исправлением. Microsoft пишет, что исправление определено для будущего выпуска SQL Server 2025, поэтому следите за заметками к накопительным обновлениям и проверяйте поведение после снятия флага в тестовом окне.

Столкнулись с похожей задачей? Обращайтесь — решим

Если у вас происходит что-то из описанного в этой статье — или любая другая проблема с ИТ-инфраструктурой, — обращайтесь в любое время. Мои специалисты и я лично разберём ситуацию, найдём настоящую причину и доведём до решения.

Возьмёмся и за разовую задачу, и за постоянное обслуживание. Первичная консультация — бесплатно и без обязательств.

📞 +7 903 729-62-41 ✈ Telegram @ITfresh_Boss

С уважением, Семёнов Евгений Сергеевич, директор «АйТи Фреш» — IT-аутсорсинг для компаний до 50 рабочих мест, 15+ лет практики

Источники

© ООО «АйТи-Фреш» · Москва · Все статьи