В на Europe PASS Summit я читал доклад о диагностике памяти. В нём упомянул, что SQL Server будет использовать большие страницы (Large Pages), если включён trace flag 834. На конференции Кристиан Болтон, хорошо известный MVP из Великобритании, заметил, что видел в ERRORLOG сообщения о «large pages», хотя trace flag у него был выключен. Тогда я ответил, что не представляю, как такое возможно. Что ж, Кристиан, вы ничего не выдумали.
Вскоре тема всплыла снова, когда я помогал коллегам из CSS разбираться с тем же вопросом. Самое время углубиться и разобраться, что же там на самом деле происходит.
Сначала: что такое «большая страница»? Вкратце: ядро ОС может организовывать память страницами большего размера, что обычно ускоряет трансляцию виртуальных адресов. Подробнее см. раздел «Large and Small Pages» в книге «Microsoft Windows Internals» Марка Руссиновича и Дэвида Соломона. Для примера: обычный размер страницы в Windows на x64 — 4 КБ, а для больших страниц — 2 МБ.
SQL Server поддерживает большие страницы при выделении памяти под некоторые внутренние структуры и буферный пул. Для этого мы используем Large Page Support, который предоставляет Windows через VirtualAlloc(). Как вы увидите, большие страницы для буферного пула подходят не всем: их нужно использовать осмотрительно и только после тщательных тестов.
Два термина, которые важны, когда речь о больших страницах в SQL Server:
- LargePagesAllocator — компонент SQLOS решает, может ли он воспользоваться Large Page Support из VirtualAlloc.
- Большие страницы для буферного пула — буферный пул решает использовать ли LargePagesAllocator из SQLOS для выделения памяти буферного пула.
Сначала поговорим о LargePageAllocator. Когда SQLOS «загружается» (то есть при старте SQL Server), он решает, может ли использовать поддержку больших страниц со стороны Windows. Для этого должны выполняться все три условия:
- SQL Server в редакции Enterprise;
- на компьютере установлено не менее 8 ГБ физической оперативной памяти;
- для сервисного аккаунта задана привилегия «Lock Pages in Memory».
Это требование Windows для использования флага MEM_LARGE_PAGES в VirtualAlloc(). Важно: эта проверка не имеет отношения к механизму «Locked Pages» для буферного пула при использовании AWE API. Просто память больших страниц не входит в рабочий набор процесса и не может быть выгружена на диск. Поэтому, как и для памяти, выделяемой через AWE API, требуется привилегия Lock Pages in Memory.
Если условия выполняются, SQLOS «инициализирует» LargePageAllocator для каждого memory node на компьютере. В ERRORLOG вы увидите примерно такие строки:
2009-06-04 12:21:08.16 Server      Large Page Extensions enabled.
2009-06-04 12:21:08.16 Server      Large Page Granularity: 2097152
2009-06-04 12:21:08.21 Server      Large Page Allocated: 32MB
Large Page Granularity — минимальный размер «большой страницы» на данной платформе Windows. Движок SQL просто вызывает Windows API GetLargePageMinimum(), чтобы это узнать. На моём x64 сервере минимум — 2 МБ. Следующая строка описывает, что делает LargePageAllocator при инициализации: он заранее выделяет 32 МБ памяти большими страницами, чтобы система была готова для компонентов, которым это потребуется. Такие строки будут для каждого memory node, созданного SQL Server.
Все это происходит даже если trace flag 834 не включён. Поэтому я и думаю, что Кристиан видел такие сообщения в ERRORLOG. Но они появятся только если соблюдены условия (Enterprise, 8 ГБ+ RAM и привилегия Lock Pages). К тому же эта память LargePageAllocator не пропадает впустую: начиная с SQL Server 2008 внутренние структуры управления блокировками и buffer hash могут использовать большие страницы.
Если вы видите такие сообщения, можно также убедиться по DMV sys.dm_os_process_memory (эта DMV есть только с SQL Server 2008), что большие страницы действительно используются. Запрос на моей системе (при выключенном trace flag 834) показал:
select large_page_allocations_kb, locked_page_allocations_kb
from sys.dm_os_process_memory
large_page_allocations_kb  locked_page_allocations_kb
-------------------------  --------------------------
61440                      49140
Как видите, есть и «large», и «locked» страницы. Large — это для LargePageAllocator (обратите внимание, что выделено больше исходных 32 МБ). Locked — это использование AWE API (система 64‑битная, редакция Enterprise, привилегия Lock Pages задана). Locked‑страницы используются буферным пулом — это то, что обычно называют «Locked Pages».
Итак, поддержка больших страниц включена и используется движком даже без trace flag 834. Но памяти на это уходит немного, и буферный пул при этом большие страницы не использует. Теперь об этом.
Вторая ситуация — когда включён trace flag 834 (описан, например есть в руководстве «Tuning options for SQL Server 2005 that is running in high performance workloads»).
Когда вы включаете trace flag 834 на 64‑битной системе, вы приказываете движку SQL Server использовать LargePageAllocator из SQLOS для всего объёма памяти буферного пула. Значит, сначала LargePageAllocator должен быть включён (по условиям, описанным выше).
Далее, если при старте движок обнаруживает trace flag 834, он выделяет память буферному пулу через LargePageAllocator. В отличие от обычного старта сервер выделяет весь объём памяти буферного пула сразу. Причина в том, что выделение больших страниц через VirtualAlloc() может быть очень медленным. Если бы буферный пул рос динамически, это могло бы стоить производительности обычных запросов. Поэтому выделяем память одним махом. Вот тут и начинаются тонкости. Алгоритм старта такой:
- Пробуем выделить объём, равный минимуму из «max server memory» и общего объёма физической памяти машины. Если «max server memory» установлен в 0, мы фактически пытаемся забрать всю физическую RAM. Поэтому обязательно задайте подходящее значение «max server memory» для вашего компьютера — скорее всего, чуть меньше, чем общая RAM, чтобы оставить место для ОС и системного кэша.
- Отсюда следует: большие страницы стоит включать только на серверах, где SQL — единственное «сильно использующее память» приложение (то есть сервер реально обслуживает только SQL Server).
- Если движок не может выделить «max server memory» (или всю физическую память), он может попробовать меньший объём. Если и меньший объём выделить не удаётся, возникает ошибка, и сервер не стартует. То, насколько «меньше» он попробует, зависит от установленного «max server memory» или общего объёма RAM. Ниже — пример.
Вот примеры из ERRORLOG (во всех случаях «max server memory» был 0, машина с 16 ГБ RAM):
На этой машине одновременно работали виртуальные машины, так что шла конкуренция за ресурсы. В одном случае при старте серверу не удалось подобрать «меньший» объём памяти для больших страниц — сервер не запустился (показываю не все строки ERRORLOG):
2009-06-04 12:18:32.41 Server      Large Page Extensions enabled.
2009-06-04 12:18:32.41 Server      Large Page Granularity: 2097152
2009-06-04 12:18:32.41 Server      Large Page Allocated: 32MB
2009-06-04 12:18:32.46 Server      Using large pages for buffer pool.
2009-06-04 12:18:32.88 Server      0 MB of large page memory allocated.
2009-06-04 12:18:39.21 Server      Failed allocate pages: FAIL_PAGE_ALLOCATION 1
2009-06-04 12:18:39.22 Server
Memory Manager                                   KB
---------------------------------------- ----------
VM Reserved                                   54848
VM Committed                                  54572
Locked Pages Allocated                            0
Reserved Memory                                1024
Reserved Memory In Use                            0
2009-06-04 12:18:39.22 Server
Memory node Id = 0                               KB
.
.
2009-06-04 12:18:39.22 Server      Error: 17138, Severity: 16, State: 1.
2009-06-04 12:18:39.22 Server      Unable to allocate enough memory to start 'SQL OS Boot'. Reduce non-essential memory load or increase system memory.
В другом случае сервер смог выделить память, но лишь 2 ГБ RAM, о чём говорит запись ERRORLOG:
2009-06-04 14:20:31.13 Server      Large Page Extensions enabled.
2009-06-04 14:20:31.13 Server      Large Page Granularity: 2097152
2009-06-04 14:20:31.14 Server      Large Page Allocated: 32MB
2009-06-04 14:20:40.03 Server      Using large pages for buffer pool.
2009-06-04 14:27:56.98 Server      2048 MB of large page memory allocated.
Здесь проявляется одна из проблем больших страниц: запрашиваемый объём должен быть непрерывным (contiguous). Это хорошо описано в статье MSDN о больших страницах:
Области памяти больших страниц могут быть трудно получить после работы системы в течение длительного времени, так как физическое пространство для каждой большой страницы должно быть непрерывным, но память может стать фрагментированной. Выделение больших страниц в этих условиях может значительно повлиять на производительность системы. Поэтому приложения должны избегать повторяющихся выделений больших страниц и вместо этого выделять все большие страницы один раз при запуске.
В приведённом примере, даже если бы «max server memory» был 8 ГБ, сервер смог бы выделить только 2 ГБ, и это стало бы максимумом для буферного пула. Помните: при больших страницах буферный пул не растёт динамически, так что объём, выделенный при старте, — это всё, что у вас будет.
Ещё одна особенность — замедление старта сервера. Заметьте семиминутный разрыв в примере ERRORLOG между сообщением о включённом trace flag 834 («Using large pages…») и сообщением о том, сколько памяти выделено для буферного пула. VirtualAlloc() с большими страницами работает долго; к тому же, если не удаётся выделить весь объём («max server memory» или физическую RAM), мы пробуем несколько меньших значений подряд, пока не найдём подходящее или не откажем в запуске. Некоторые заказчики сообщали, что старт сервера при trace flag 834 занимал более 30 минут.
Итоги:
- Поддержка больших страниц включается на системах Enterprise Edition при объёме физической RAM >= 8 ГБ (и заданной политике Lock Pages in Memory).
- SQL Server будет выделять память буферному пулу большими страницами на 64‑битных системах, если поддержка больших страниц включена и активирован trace flag 834.
- Большие страницы для буферного пула — точно не для каждого. Используйте их только на серверах, посвящённых только SQL Server, и лишь после внимательной настройки (например, «max server memory») и тестов: добьётесь ли вы измеримого прироста производительности, прежде чем выводить в продакшн.
- Время старта SQL Server может заметно увеличиться при использовании trace flag 834.
Надеюсь, этот материал приоткрывает внутреннюю кухню работы с большими страницами и некоторые сопутствующие нюансы. И — чтобы Кристиан был уверен: он всё верно видел в ERRORLOG, даже когда trace flag 834 был выключен.

 
Комментариев нет:
Отправить комментарий