· 15 мин чтения

max server memory ограничен, а sqlservr.exe ест больше: куда уходит память SQL Server

Раз в квартал у кого-нибудь из клиентов повторяется один и тот же разговор: «мы поставили max server memory в 24 гигабайта, а в диспетчере задач sqlservr.exe жрёт 28 — у вас там утечка». Утечки нет. Есть непонимание того, что именно измеряет этот параметр. Ниже — как я объясняю это техническим специалистам и руководителям клиентов, с конкретными DMV-запросами, формулами и тем, что в лимит принципиально не попадает.

Кейс, с которого всё началось

У одного из наших клиентов (юрлицо на 40 рабочих мест, типовая связка «1С:Предприятие 8.3 + MS SQL Server на одной физической машине») мы подняли RAM сервера с 16 до 32 ГБ и подняли max server memory (MB) с 12288 до 24576. Через три дня работы под нагрузкой бухгалтер прислал скриншот диспетчера задач: строка sqlservr.exe — 27,4 ГБ. Вопрос закономерный: «вы настроили лимит 24 ГБ, почему он его не соблюдает?»

Это тот самый повторяющийся паттерн, из-за которого я и сел писать этот материал: администратор сравнивает Target Server Memory (то, что регулирует менеджер памяти SQL Server) с полным потреблением процесса sqlservr.exe в Windows — и любое расхождение записывает в утечку. На деле это два разных измерения, и разница между ними объясняется документированным списком категорий памяти, которые менеджер памяти SQL Server сознательно не контролирует.

Настроим память SQL Server и 1С под вашу реальную нагрузку

Если у вас на сервере 1С и MS SQL Server живут вместе, а диспетчер задач показывает цифры, которые не бьются с настроенным max server memory — пришлите нам вывод sys.dm_os_process_memory и sys.dm_os_memory_clerks через форму обратной связи на itfresh.ru. Разберём конкретно вашу конфигурацию и посчитаем корректный лимит под вашу нагрузку.

Что физически ограничивает max server memory (MB)

Официальная документация Microsoft описывает область действия max server memory (MB) и min server memory (MB) как границы для буферного пула и «прочих кэшей ядра БД». В более развёрнутом виде — в Memory Management Architecture Guide — это сформулировано так: настройка «управляет выделением памяти SQL Server, compile-памятью, всеми кэшами (включая буферный пул), грантами памяти на выполнение запросов, памятью диспетчера блокировок и памятью CLR — фактически любым клерком памяти, который виден в sys.dm_os_memory_clerks».

Начиная с SQL Server 2012 (11.x) в эту область добавили ещё и CLR-аллокации — до этого CLR-память жила отдельно. Если у вас в 1С используются внешние компоненты или CLR-функции (не типично для типовых конфигураций, но встречается в доработках), с 2012-й версии их память тоже учитывается лимитом.

КомпонентВходит в max server memory?Где смотреть
Буферный пул (страницы данных и индексов)Да, основной потребительMEMORYCLERK_SQLBUFFERPOOL в sys.dm_os_memory_clerks
План-кэш (кэш планов запросов, adhoc/proc cache)ДаCACHESTORE_SQLCP, CACHESTORE_OBJCP
Compile-память и гранты памяти на сортировки/хэшиДаsys.dm_exec_query_memory_grants
Память диспетчера блокировокДаsys.dm_os_memory_clerks, категория LOCK
CLR (с SQL Server 2012 и новее)ДаMEMORYCLERK_SQLCLR
Columnstore и In-Memory OLTP (Hekaton) объектыДа, но с отдельными клерками для мониторингаMEMORYCLERK_XTP*, клерки columnstore

Отдельно обращаю внимание на нюанс, который я нашёл при сверке текущей редакции документации: формулировка «Server Memory Configuration Options» в разделе про сам параметр сузилась до «max server memory ограничивает только размер буферного пула SQL Server», а память под расширенные хранимые процедуры, COM-объекты и несовместно загруженные DLL прямо названа «нерезервируемой областью», которую лимит не трогает. Это не противоречие, а уточнение: широкий список из architecture guide описывает, что менеджер памяти SQL Server учитывает как «серверную» память (Total/Target Server Memory), а формулировка в Server Memory Configuration Options напоминает, что процесс sqlservr.exe в целом шире этой границы.

Ещё один источник ложных тревог — рост план-кэша на боевой базе 1С. Каждый уникальный текст запроса, который платформа 1С формирует динамически (а генератор запросов 1С этим славится — параметризация отличается от вызова к вызову), создаёт отдельный вход в CACHESTORE_SQLCP. За несколько недель без чистки план-кэш на активной базе может вырасти до нескольких гигабайт — это не утечка, а результат накопления planов, и находится внутри лимита max server memory, просто занимает в нём растущую долю от буферного пула. Диагностируется тем же запросом к sys.dm_os_memory_clerks из раздела про диагностику — по имени клерка сразу видно, что выросло: буферный пул или план-кэш.

Как эта граница менялась начиная с SQL Server 2012 — и почему это важно после апгрейда

До версии 2012 (11.x) SQL Server считал память через пять разных механизмов, и лимитом были охвачены далеко не все из них. Таблица ниже — прямая выжимка из Memory Management Architecture Guide, она объясняет, почему после миграции старой базы 1С с сервера SQL 2008 R2 на SQL 2019 или 2022 у клиентов резко меняется фактическое потребление при том же значении max server memory.

Тип аллокацииSQL Server 2005 / 2008 / 2008 R2SQL Server 2012 и новее
Single-Page Allocator (блоки ≤ 8 КБ)Учитывается лимитомУчитывается (объединено в «any size» allocator)
Multi-Page Allocator (блоки > 8 КБ)Не учитываетсяУчитывается
CLR-аллокацииНе учитываетсяУчитывается
Стеки потоков (thread stacks)Не учитываетсяНе учитывается
Прямые аллокации Windows (DWA: xp_ DLL, sp_OA, linked server)Не учитываетсяНе учитывается

Вывод для практики: если вы когда-то подбирали max server memory «на глаз» под SQL Server 2008 R2 и просто перенесли то же число на новый сервер 2019/2022/2025 — пересчитайте. С 2012 версии под тем же значением лимита реально помещается меньше нагрузки, потому что туда же теперь попадают multi-page и CLR аллокации, которые раньше жили в «нерезервируемой» зоне (о ней — в следующем разделе).

Пять категорий памяти, которые лимит не видит принципиально

Вот полный список, который я держу перед глазами при разборе жалоб «сервер ест больше лимита», собранный по Memory Management Architecture Guide и Server Memory Configuration Options:

КатегорияЧто конкретноКак оценить объём
Стеки потоков worker'овКаждый поток SQL Server резервирует стек фиксированного размераРазмер стека × число worker-потоков (формула ниже)
memory_to_reserve (VAS-резерв)Область адресного пространства под multi-page/CLR/стеки/DWA до 2012, сейчас — под стеки и DWAПо умолчанию 256 МБ, sp_configure 'memory to reserve'
DLL расширенных хранимых процедур, объекты sp_OA, провайдеры Linked ServerСторонний код, загруженный в адресное пространство sqlservr.exeИнвентаризация xp_-процедур и связанных серверов в конфигурации
Буферы резервного копированияВыделяются под операции BACKUP/RESTOREЗависит от BUFFERCOUNT и MAXTRANSFERSIZE в задании бэкапа
Кратковременный over-commit сверх Target Server MemoryПри нехватке непрерывной памяти для multi-page запросов (rebuild columnstore-индекса, batch mode, большие гранты)Сравнение счётчиков Total Server Memory и Target Server Memory в perfmon

Формула для стеков актуальна почти всегда, поэтому распишу её отдельно. Размер стека на 64-битном SQL Server на 64-битной ОС — 2048 КБ на поток (для Itanium было 4096 КБ, для 32-битного SQL Server на 64-битной ОС — 768 КБ; в 2026 году это уже история, но полезно знать при аудите legacy-инсталляций). Число потоков — это рассчитанное значение max worker threads, которое зависит от количества логических процессоров, закреплённых за инстансом. Пример: сервер с 8 CPU обычно получает порядка 512 worker threads по умолчанию, значит под стеки резервируется около 512 × 2048 КБ ≈ 1 ГБ адресного пространства. Это резерв виртуального адресного пространства, но по мере роста нагрузки часть его реально коммитится в физическую память — и именно эта часть добавляется к тому, что вы видите в Task Manager поверх Target Server Memory.

Отдельно стоит multi-page over-commit: начиная с SQL Server 2012, если для запроса нужен блок памяти больше 8 КБ, а из-за фрагментации нет подходящего непрерывного куска в рамках лимита, SQL Server временно выделяет память сверх max server memory, а затем Resource Monitor старается вернуть Total Server Memory к Target Server Memory. Такое поведение типично при перестроении columnstore-индексов, batch mode на rowstore и крупных грантах памяти. В SQL Server 2019 для ускорения этой уборки есть трейс-флаг 8121, а начиная с SQL Server 2022 (16.x) это поведение включено по умолчанию без флага.

Про буферы резервного копирования — цифры, которые я привожу клиентам, чтобы разговор был предметным, а не абстрактным: объём памяти под один поток backup примерно равен MAXTRANSFERSIZE × BUFFERCOUNT. При значениях по умолчанию (обычно 1 МБ на MAXTRANSFERSIZE и небольшое число буферов) это единицы-десятки мегабайт, но если задание бэкапа явно завышает BUFFERCOUNT ради скорости на быстрых дисковых массивах, кратковременный расход памяти на операцию бэкапа может доходить до сотен мегабайт сверх max server memory — и это ожидаемое, документированное поведение, а не повод искать утечку.

Совмещённый сервер 1С + SQL Server: наш типовой случай у клиентов до 50 РМ

У большинства наших клиентов сервер 1С:Предприятие и MS SQL Server живут на одной машине — отдельный SQL-хост экономически не оправдан при штате до 50 человек. Здесь к описанным выше пяти категориям добавляется ещё один источник путаницы: процессы кластера 1С (rphost.exe, rmngr.exe, ragent.exe) — это отдельные процессы, их память вообще не относится ни к sqlservr.exe, ни к max server memory. Но в диспетчере задач директор смотрит на сервер целиком и складывает всё подряд, получая пугающую сумму.

Формула, по которой я считаю max server memory на совмещённом сервере, отталкивается от подхода из официальной рекомендации Microsoft (вычесть из общего объёма память ОС, стеки, DWA), но добавляет отдельную строку под 1С:

На практике для сервера с 32 ГБ RAM под связку 1С + SQL Server на 35–40 пользователей это чаще всего даёт диапазон 20–24 ГБ под max server memory — это оценка по нашей практике внедрений, не норматив Microsoft, подбирается по факту нагрузки на конкретном сервере.

Диагностика: четыре запроса, которыми я подтверждаю клиенту, что утечки нет

Вместо того чтобы объяснять на словах, я показываю клиенту вывод DMV — цифры убеждают лучше, чем презентация. Вот минимальный набор, который закрывает 90% споров «куда делась память».

1. Сколько сервер реально считает своим (Total/Target Server Memory) и что в процессе сверх этого:

SELECT physical_memory_in_use_kb / 1024 AS physical_memory_in_use_mb,
    large_page_allocations_kb / 1024 AS large_page_mb,
    locked_page_allocations_kb / 1024 AS locked_page_mb,
    virtual_address_space_committed_kb / 1024 AS vas_committed_mb,
    memory_utilization_percentage,
    process_physical_memory_low,
    process_virtual_memory_low
FROM sys.dm_os_process_memory;

Если process_physical_memory_low и process_virtual_memory_low равны 0 — процесс не испытывает дефицита, а разница между этим выводом и max server memory объясняется именно категориями из предыдущего раздела, а не утечкой.

2. Что происходит на уровне всей ОС (свободно ли системе памяти в принципе):

SELECT total_physical_memory_kb / 1024 AS total_physical_mb,
    available_physical_memory_kb / 1024 AS available_physical_mb,
    system_memory_state_desc
FROM sys.dm_os_sys_memory;

3. Топ-клерки памяти внутри SQL Server — что именно занимает Target Server Memory:

SELECT TOP 15 type,
    name,
    pages_kb / 1024 AS pages_mb
FROM sys.dm_os_memory_clerks
ORDER BY pages_kb DESC;

Если наверху этого списка не буферный пул и не план-кэш, а что-то нетипичное — вот здесь уже стоит разбираться предметно, но это уже не вопрос «max server memory не работает», а вопрос «какой именно кэш разросся».

4. Значения текущего лимита и то, что реально применено:

SELECT [name], [value], [value_in_use]
FROM sys.configurations
WHERE [name] IN ('max server memory (MB)', 'min server memory (MB)');

Дополнительно смотрю журнал ошибок SQL Server на сообщение 17890 («A significant part of sql server process memory has been paged out») — если оно есть, это признак, что физической памяти на сервере не хватает уже на уровне ОС, и здесь действительно нужно либо снижать max server memory, либо добавлять RAM, либо включать Lock Pages in Memory (следующий раздел).

Lock Pages in Memory и большие страницы — когда это нужно совмещённому серверу

Lock Pages in Memory (LPIM) — это право Windows (SeLockMemoryPrivilege), которое позволяет sqlservr.exe держать буферный пул в физической памяти и не выгружаться на диск при внешнем давлении на память со стороны ОС. С SQL Server 2012 (11.x) трейс-флаг 845 для этого больше не нужен даже в Standard Edition — достаточно выдать привилегию учётной записи службы SQL Server.

Важный момент из документации: LPIM не меняет динамическое управление памятью — буферный пул по-прежнему растёт и сжимается по запросу других клерков. Но если вы включаете LPIM, Microsoft прямо рекомендует обязательно задать конкретное значение max server memory, а не оставлять значение по умолчанию 2147483647 МБ — иначе на совмещённом сервере с 1С заблокированный буферный пул может выесть память, нужную процессам rphost/rmngr, и словить нестабильность уже на стороне 1С.

Проверить текущий режим памяти можно одним запросом:

SELECT sql_memory_model_desc FROM sys.dm_os_sys_info;

Три возможных значения: CONVENTIONAL — LPIM не выдан; LOCK_PAGES — выдан и используется; LARGE_PAGES — LPIM выдан в Enterprise-режиме вместе с трейс-флагом 834 (большие страницы; в документации отдельно отмечено, что это продвинутая конфигурация, не рекомендуемая для большинства сред). На типовых серверах наших клиентов до 50 РМ большие страницы не включаю — выигрыш не окупает рост сложности диагностики, а вот LPIM + явный max server memory ставлю по умолчанию на виртуальных машинах, где гипервизор потенциально может забирать память хоста.

Пять ошибок в интерпретации, которые я вижу чаще всего

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

Пошаговый регламент, который я применяю при настройке памяти у клиента

Ниже — последовательность, которую использую при каждом внедрении или пересмотре конфигурации памяти на сервере с 1С и SQL Server:

  1. Снимаю фактическую нагрузку за неделю: пиковый рабочий набор rphost/rmngr через Resource Monitor и Total/Target Server Memory через perfmon-счётчики SQL Server.
  2. Считаю max server memory по формуле из раздела про совмещённый сервер (RAM минус ОС минус 1С минус стеки минус 20–25% на DWA).
  3. Проверяю редакцию SQL Server и версию — от неё зависит расчёт max worker threads и, соответственно, резерв под стеки потоков.
  4. Применяю значение через T-SQL, без перезапуска службы:
EXECUTE sp_configure 'show advanced options', 1;
GO
RECONFIGURE;
GO
EXECUTE sp_configure 'max server memory', 20480; -- 20 ГБ, пример
GO
RECONFIGURE;
GO
  1. Задаю min server memory на уровне 40–50% от max — это не даёт SQL Server отдавать буферный пул слишком агрессивно при кратковременных всплесках нагрузки со стороны 1С (например, при регламентных операциях закрытия месяца).
  2. Если сервер виртуальный — выдаю LPIM учётной записи службы и повторно фиксирую явное значение max server memory (см. предыдущий раздел).
  3. Через неделю повторяю снятие метрик через sys.dm_os_process_memory и sys.dm_os_memory_clerks, сравниваю с базовой линией, при необходимости корректирую значение на 10–15% в любую сторону.

Отчёт клиенту в итоге состоит из трёх цифр: Target Server Memory (что регулирует лимит), физическая память процесса sqlservr.exe по sys.dm_os_process_memory, и отдельно — рабочий набор процессов 1С. Когда эти три цифры разложены по полочкам, вопрос «а где утечка» обычно закрывается сам собой.

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

Правда ли, что max server memory ограничивает вообще всю память процесса sqlservr.exe?
Нет. Лимит регулирует память, которой управляет менеджер памяти SQL Server: буферный пул, все кэши включая план-кэш, compile-память, гранты памяти на запросы, диспетчер блокировок и CLR (начиная с SQL Server 2012). Стеки потоков, DLL расширенных хранимых процедур, объекты sp_OA, провайдеры Linked Server и буферы резервного копирования живут вне этого лимита и добавляются к общему потреблению процесса в диспетчере задач.
Сколько памяти закладывать на стеки потоков при расчёте лимита?
На 64-битном SQL Server на 64-битной ОС размер стека — 2048 КБ на поток. Умножьте это на рассчитанное значение max worker threads для вашего числа логических процессоров — получите резерв, который стоит вычесть из общего объёма RAM ещё до расчёта max server memory.
Нужно ли включать Lock Pages in Memory на каждом сервере с SQL Server?
На виртуальных машинах, где гипервизор хоста может изымать память у гостя, LPIM оправдан почти всегда — но только вместе с явно заданным max server memory. На физическом сервере с достаточным запасом RAM и стабильной нагрузкой можно обойтись без LPIM, если в журнале ошибок нет сообщения 17890 о вытеснении памяти на диск.
Что делать, если 1С и SQL Server стоят на одной машине и памяти постоянно не хватает?
Считать max server memory не как «всю RAM минус немного на ОС», а с явным вычетом рабочего набора процессов rphost/rmngr кластера 1С, снятого за неделю пиковой нагрузки через Resource Monitor. Эти процессы отдельные от sqlservr.exe и в лимит SQL Server не входят, но конкурируют с ним за физическую память сервера.
Как быстро проверить, куда ушла лишняя память, без остановки сервера
Три запроса без даунтайма: sys.dm_os_process_memory — физическая память процесса и признаки нехватки памяти; sys.dm_os_memory_clerks с сортировкой по pages_kb — что конкретно занимает Target Server Memory внутри SQL Server; sys.configurations — текущее значение max server memory и min server memory. Вместе они за минуту показывают, укладывается ли сервер в лимит или действительно есть проблема.
📄
Скачайте подробный разбор в PDF Кейсы, статистика, типовые ошибки и чек-лист самопроверки — 12 страниц
Скачать PDF

Подпишитесь на разборы ITfresh

Раз в неделю — практичные материалы по ИТ для бизнеса: без спама, только польза.

Письмо придёт в течение минутыНе нашли его во «Входящих» — загляните в папку «Спам» или «Промоакции» и нажмите «Не спам». Так все следующие выпуски будут приходить прямо в основную почту.