В предыдущей статье обсуждали регламентное обслуживание с акцентом на пересчет статистик. Операция крайне полезная, необходимая и чем интенсивнее меняются данные в базе, тем важнее актуальные статистики. Сегодня поговорим про еще одну регламентную операцию – пересчет индексов.
Еще каких-то 5-7 лет назад мы рекомендовали пересчитывать индексы ежедневно, т.к. далеко не все использовали или успели перейти на дисковые хранилища на основе SSD дисков и строили свои дисковые подсистемы на базе HDD. Соответственно, чем фрагментированнее индекс, тем труднее HDD перескакивать с одного сегмента диска на другой – магнитная головка, последовательное запись/чтение и всё такое. Казалось бы, в SSD эта проблема решена – нет механических частей, данные изначально записываются в произвольные сектора для равномерного использования ресурса диска и это не влияет на скорость чтения. Но нюансы есть.
Примерно год назад наткнулся на полезное видео. Видеохостинг заблокирован РКН, но ссылку всё равно даю, т.к. сходу не смог найти его рабочую версию на других ресурсах – https://www.youtube.com/watch?v=sBEHBq-wKiI. Очень интересное и глубокое исследование о том, нужно ли всё-таки пересчитывать индексы на SSD-дисках или нет. Влияет ли фрагментация индекса на скорость доступа к данным или нет. По её следам и в продолжение первой части цикла статей по регламентному обслуживанию базы данных и родился этот пост.
Пересказывать видео не буду, т.к. лучше автора этого не сделаю. Перейду сразу к выводам по итогам исследования, а потом порассуждаем. Но, как обычно, с высоты 1С:Предприятие, это важно!
Если совсем кратко, то да, фрагментация влияет на производительность, даже если база данных находится на SSD-дисках. Дело в физическом расположении ключей индекса по страницам дискового массива. У объекта с низкой фрагментацией страницы с данными находятся на физическом уровне одна за одной, и механизм read-ahead (упреждающее чтение) может инициировать чтение в buffer pool большими порциями – до 64 протяженных (смежных) страниц общим объемом 512 кБ. Читать 64 страницы протяженным участком быстрее, чем читать по несколько страниц, инициируя каждый раз операцию ввода/вывода. То есть для высокофрагментированного объекта потребуется больше времени на операции ввода/вывода (IO Time), а для низкофрагментированного объекта, где read-ahead может прочитать данные большими блоками и время ввода/вывода, намного меньше.
Всё верно, результаты исследования это подтвердили, но для 1С:Предприятие, как обычно, есть особенность. А именно, первичный ключ, по которому индекс упорядочивается.
Подавляющее большинство первичных ключей в 1С – это ссылки: на элементы номенклатуры, контрагентов, счетов БУ, первичные документы и т.д. Ссылка генерируется в виде GUID. В базе данных GUID’ы имеют тип binary(16) и сравниваются/сортируются побитно как бинарные данные.
Что это значит? А значит это то, что у ссылки нет автоинкремента, потому как GUID имеет условно случайную структуру – они так задумывались. Даже если использовать Time-Based GUIDs для первичных ключей, они получаются более сгруппированными (GUID’ы выдаются пулом по 32 штуки для каждого сеанса), но всё равно не последовательными. Некоторое ускорение при вставке в кластерный индекс Time-Based GUID даёт, но в целом индекс получается плохо кластеризован и как сказал сам Б.Нуралиев: «Механизм генерации ссылок обеспечивает только их уникальность. Возрастающая последовательность при их генерации не обеспечивается.».
Рассмотрим пример.
Дано: достаточно высоконагруженная база 1С (25-30 тыс запросов/сек). Размер базы 14 Тб. Только регистр бухгалтерии занимает более 1 Тб. На него, точнее на его таблицы, и посмотрим.

Перестроение индексов в базе данных настроено каждую ночь:

Если вернуться к предыдущему рисунку с индексами регистра бухгалтерии, то обратите внимание на процент фрагментации по одному из индексов, который в течение недели стабильно держится в диапазоне 38-39%:

Опрос индексов в мониторинге настроен на 00:00, т.е. до запуска регламентных заданий по пересчету индексов. Таким образом, утром индекс будет перестроен и дефрагментирован, а к концу дня фрагментация индекса Accnt21737ByExtDim_RR достигает тех самых 38-39%. По остальным индексам регистра бухгалтерии примерно такая же картина.
К сожалению, не могу показать картину фрагментации по часам, но, учитывая все вышесказанное про GUID’ы, ситуация с фрагментацией частоизменяемых таблиц будет схожей. Уже через несколько часов после начала активной пользовательской работы весь эффект от дефрагментации сойдет на нет.
К чему я веду. Несмотря на то, что выводы в исследовании, ссылку на которое я привел, очень даже правильные, бросаться заниматься постоянным перестроением индексов не стоит – эффект не такой значительный. В приведенном примере ситуация еще управляемая – пересчет индексов успевает выполниться в тех.окно. Но обычно в системах такого размера все не так радужно – индексы не успевают пересчитаться вместе со статистиками, приоритет отдается, естественно, пересчету статистик, а индексы… ну может быть в выходные.
Еще один важный момент, про который многие забывают – фрагментация индексов влияет на производительность в том случае, если в запросах чтение данных идет с диска, а не из buffer pool. Но если оперативной памяти достаточно, а может быть даже у вас вся база помещается в памяти, то это вообще не так важно.
Как это увидеть? Есть счетчик PLE (Ожидаемый срок жизни страницы памяти), который показывает эффективность использования буферного кэша. Идеальное его поведение – это рост в течение рабочего дня и "обрушение" только во время регламентных работ. На рисунке ниже видим, что график PLE (зеленый) за несколько дней явно далек от идеальной картины мира. Поэтому при выполнении запросов SQL-сервер обращается за данными к диску. Обратите внимание на синий график – как только PLE падает, начинаются значительные очереди к диску.

Собственно, если данные у вас будут на диске менее фрагментированы, то их чтение будет конечно чуть быстрее, но это точно не тот эффект, который вы ожидаете. Потому что бороться нужно с причиной, а не со следствием.
Посмотрим в мониторинге Perfexpert на трассу тяжелых запросов, которые нагружают память и диск:

В нижней панели рисунка – группы запросов, отсортированные по нагрузке на диск (доле физических чтений). Возьмем первый вид запроса с хешем 11630710602545325702. В верхней панели выведен весь список из 840 запросов этой группы. Хорошо видно, что каждый такой запрос делает очень много не только логических чтений (из памяти), но и физических (с диска). Возьмем, к примеру верхние строчки. Видим 20-30 млн. логических чтений, из которых почти столько же делается с диска. Т.е. запросу почти всегда не хватает буферного пула, и он обращается к диску. В целом 30 млн. логических чтений – это очень много и кэша, конечно, не хватит. Поэтому главный вектор улучшения производительности здесь должен лежать в плоскости оптимизации самих запросов, чтобы они выполнялись с меньшим количеством логических чтений и, как следствие, с меньшим количеством физических чтений или вообще без них. А фрагментация индекса – это дело даже не второго порядка. Под оптимизацией подразумевается не только переписывание запроса на стороне приложения, но и индексный тюнинг.
Если запрос оптимизации не подлежит (что вряд ли), также имеет смысл подумать в сторону увеличения памяти для расширения буферного пула. Но такая возможность не всегда имеется.
Еще один параметр, связанный с индексами, о котором еще не сказал – fill factor. Может устанавливаться как для всей СУБД, так и для каждого индекса. Именно эта настройка определяет, сколько свободного места для новых данных будет в индексе после перестроения. По умолчанию всем индексам параметр fill factor установлен в 0, то есть значение считывается из параметров базы данных. В параметрах сервера СУБД также установлено по умолчанию значение 0 (оно же 100%). Это означает, что после перестроения индекса на странице не остается свободного места для вставки новых данных. Если СУБД добавляет данные в такой индекс, ей требуется выделять место в другой секции файла данных. Другими словами, любое добавление или изменение данных моментально приводит к росту фрагментации по такой таблице и росту количества страниц при чтении.
Соответственно, если значение fill factor указывается менее 100%, например, 80%, то значит на каждой странице индекса остается 20% свободного пространства для вставки данных без разбиений страниц.
Чтобы понять нужно ли вам в вашей базе данных что-то с этим делать, можно обратиться к счетчику Количества разбиений страниц в секунду (page splits/sec) и сопоставить это с дисковыми очередями.
Если корреляция есть, как на рисунке ниже, то значит идет частое разбиение страниц с повышенной нагрузкой на диск. Надо попробовать снизить fill factor и оценить эффект.

Слишком низкое значение fill factor (<70-80%) приводит к уже значительному и не всегда оправданному росту места, занимаемого базой. Поэтому «золотой серединой» считаются значения из диапазона 80–90%. Но это не догма. Размер базы при этом увеличится не на много – процентов на 5-10% и остаётся место под вставки новых данных. Но если в какие-то таблицы данные вставляются большими пачками (массовые изменения), то эта настройка не поможет. Поэтому нужно смотреть по месту – конкретно для вашей ситуации, для ваших потоков, для ваших наборов индексов.
Выводы
В качестве выводов ранжирую механизмы улучшения производительности ИТ-системы как это видим мы, спустя 20+ лет практики в этой области.
В список не попали рекомендации по процессору и по другим ресурсам. Например, только нюансам с процессором мы посвятили аж две статьи (статья 1 и статья 2). Здесь только связка память-диск:
Оптимизация запросов, индексный тюнинг.
Как писал выше, цель этой операции – не только ускорить запрос, но и по возможности снизить количество логических чтений. Чем меньше логических чтений, тем меньше запрос обращается к оперативной памяти и тем меньше к физической (диску).Обслуживание статистик.
Вовремя пересчитанные статистики – это увеличение вероятности выбора правильного плана выполнения запроса, а значит запрос будет стабильно выполняться.Добавление оперативной памяти (увеличение buffer pool).
Это самая простая и, пожалуй, доступная рекомендация. Чем больше данных будет в кэше, тем меньше СУБД будет обращаться к диску.Выбор значения FILL FACTOR.
Цель – найти компромисс между скоростью фрагментации индексов и размером базы данных.-
Дефрагментация индексов (таблиц).
Под дефрагментацией я буду подразумевать перестроение индекса командой
ALTER INDEX ...REBUILD.
ИспользованиеREORGANIZEдля высоконагруженных систем не эффективно.Дефрагментацию делать нужно, даже если у вас SSD. Очень часто ее не делают потому, что база высоконагруженная, большая. Соответственно в тех. окно не успевают, а рабочее время – это блокировки. Кроме того, сильно фрагментированный индекс может занимать почти в 2 раза больше страниц, чем ему требуется. А значит снизится процент попадания в кэш (Buffer Cache Hit Ratio).
Особенно эффективна дефрагментация для баз данных на чтение, архивных, где данные меняются не интенсивно, но при этом пользователи часто работают с отчетами за огромные периоды, поэтому этих данных точно нет в кэше – читаются с диска. Если будем считать, что запросы выполняются оптимально по плану и по логике (выполнены пп.1 и 2 данного перечня), то в таких случаях должен быть хорошо заметен эффект от дефрагментации. Но это только 5-й шаг в оптимизации и ускорении вашей ИТ-системы!
Так что, периодичность перестроения индексов выбирайте самостоятельно. Это может быть и раз в неделю, и раз в месяц. У нас есть даже примеры, когда клиенты перестраивает индексы раз в год.
Улучшение скорости дискового хранилища.
Эта рекомендация на последнем месте, т.к. встречается проблема с дисками все реже и реже. Хотя, зачастую, многие администраторы начинают оптимизацию производительности с этого шестого (!) шага. И в большинстве случаев это, мягко говоря, является преждевременным.
Продолжение следует…
Ссылки на остальные части Записок оптимизатора 1С:
Записки оптимизатора 1С (ч.1). Странное поведение MS SQL Server 2019: длительные операции TRUNCATE
Записки оптимизатора 1С (ч.2). Полнотекстовый индекс или как быстро искать по подстроке
Записки оптимизатора 1С (ч.3). Распределенные взаимоблокировки в 1С системах
Записки оптимизатора 1С (ч.4). Параллелизм в 1С, настройки, ожидания CXPACKET
Записки оптимизатора 1С (ч.5). Ускорение RLS-запросов в 1С системах
Записки оптимизатора 1С (ч.6). Логические блокировки MS SQL Server в 1С: Предприятие
Записки оптимизатора 1С (ч.7). «Нелогичные» блокировки MS SQL для систем 1С предприятия
Записки оптимизатора 1С (ч.8). Нагрузка на диски сервера БД при работе с 1С. Пора ли делать апгрейд?
Записки оптимизатора 1С (ч.9). Влияние сетевых интерфейсов на производительность высоконагруженных ИТ-систем
Записки оптимизатора 1С (ч.10): Как понять, что процессор — основная боль на вашем сервере MS SQL Server?
Записки оптимизатора 1С (ч.11). Не всегда очевидные проблемы производительности на серверах 1С.
Записки оптимизатора 1С (ч.12). СрезПоследних в 1C:Предприятие на PostgreSQL. Почему же так долго?
Записки оптимизатора 1С (ч.13). Что не так в журнале регистрации 1С в формате SQLitе?
Записки оптимизатора 1С (ч.14.1). Любите свою базу данных и не забывайте обслуживать
Записки оптимизатора 1С (ч.14.2). Пересчет индексов на SSD-дисках. Делаем или игнорируем?
Комментарии (13)

Mur81
30.10.2025 10:16В очередной раз интересная и полезная статья, спасибо!
Такое замечание. Вы используете термин "дефрагментация индекса", в то время как в самом MS SQL сервере в планах обслуживания используются термины Reorganize index и Rebuild index (реорганизация индекса и перестроение индекса - в русской редакции), которые являются совершенно разными операциями.
С этими терминами вечная путаница, особенно у начинающих. Думаю хорошо бы на этом заострить внимание.

koloskovv Автор
30.10.2025 10:16Не планировал рассказывать про разницу между Reorganize и Rebuild. Это не совсем в тематике данной статьи. Информации в интернете много. Кому нужно, найдет.
Кратко. Мы в своих скриптах не используем Reorganize, т.к. работаем преимущественно с высоконагруженными БД и там это не эффективно. Основные недостатки перед Rebuild:
не обновляет статистику, не может изменить параметры индекса (например, тот же FILLFACTOR), не освобождает пустые страницы, медленнее работает с большими индексами, не справляется с высокой фрагментацией (>30).
Mur81
30.10.2025 10:16Я же не к тому, что надо рассказывать про разницу. Я к тому, что говоря "дефрагментация индекса" нужно однозначно указывать, что имеется в виду именно операция rebuild. Для тех, кто с этим работает (как Вы) это, понятно дело, само собой разумеется. Но вот для новичков это ни разу не очевидно. Информация в Интернете не помогает. Очень много где в статьях эти понятия перепутаны, т.к. на деле оказалось, что для авторов этих статей это тоже не очевидно. Либо же написано вот так: Rebuild это перестроение индекса, а Reorganize это просто дефрагментация (понимай как хочешь). Потом новичок читает какую-то статью, видит там про дефрагментацию индекса и делает операцию Reorganize, тогда как надо было делать именно Rebuild. Поэтому я сделал вывод, что в русскоязычных текстах желательно вообще избегать термина "дефрагментация индекса" и использовать или английские термины или так как они в локализованном SQL написаны, т.е. реорганизация и перестроение. Иначе велика вероятность, что кто-то поймёт не так.
А про операцию Reorganize достаточно написать одну фразу - знаешь Вовка, не нужна тебе такая операция, поверь мне на слово.

speshuric
30.10.2025 10:16А значит это то, что у ссылки нет автоинкремента, потому как GUID имеет условно случайную структуру – они так задумывались. Даже если использовать Time-Based GUIDs для первичных ключей, они получаются более сгруппированными (GUID’ы выдаются пулом по 32 штуки для каждого сеанса), но всё равно не последовательными. Некоторое ускорение при вставке в кластерный индекс Time-Based GUID даёт, но в целом индекс получается плохо кластеризован и как сказал сам Б.Нуралиев: «Механизм генерации ссылок обеспечивает только их уникальность. Возрастающая последовательность при их генерации не обеспечивается.».
Ссылка для СУБД в первую очередь это binary(16). SQL Server про её "гуидообразность" как бы не в курсе совсем. На стороне 1С своим (не очень обычным) преобразованием эта binary(16) преобразуется из/в GUID.
Из-за этого ссылаться на "GUID имеет условно случайную структуру – они так задумывались" некорректно. У нас в СУБД это не GUID, и 1С ссылки не генерит как GUID ОС, а пользуется своим движком.
В рамках одной ИБ, в рамках одного сервера ссылки, созданные по умолчанию, в виде binary(16) в целом близки (очень близки) к монотонным. Если их генерировать сторонними средствами и генерацией гуидов, то, конечно, это нарушается. Если генерировать в разных ИБ на разных серверах 1С:Предприятия, то это будут разные серии.
Time-Based GUID генерируемый ОС или SQL Server имеет не ту же структуру, что механизм "новая ссылка". И по умолчанию они друг к другу отношения не имеют.
Это замечание никак не противоречит выводам статьи, которые в целом, конечно, верны. Просто для уточнения, что на самом деле гуиды отдельно, ссылки отдельно.
vis_inet
Подскажите, где и как именно настраивается FILL FACTOR применительно к базе 1С ?
koloskovv Автор
fill factor настраивается не для 1С. Можно настроить целиком для всего сервера MS SQL и/или для каждого индекса в отдельности.
Для всего сервера можете воспользоваться, например, вот таким скриптом.
После очередного перестроения индекса(ов) степень их заполнения поменяется на 90%.
vis_inet
Если изменить FILL FACTOR для отдельного индекса, то, например, загрузка базы из .DT не изменит его?
koloskovv Автор
Ну а вы как думаете? Индексов в DT нет, значит они будут созданы с тем fill factor, что указан глобально.
vis_inet
Спасибо за пояснения.
Mur81
А хорошая ли это практика - менять это значение для всего сервера? Мне кажется я не сильно ошибусь если предположу, что в проде (особенно если это не крупный бизнес) наиболее частая ситуация это когда на одном сервере лежит много баз. Часть из них может изменяться интенсивно, часть - нет. Поменяем fill factor для всего сервера - получим бесцельное расходование дискового пространства для баз которые меняются не интенсивно.
С другой стороны - прописывание fill factor для отдельных индексов это точно непосильная задача для, скажем так, админа широкого профиля (который не специализируется конкретно на SQL и 1С).
Но если я правильно понимаю, то можно fill factor указать для конкретной базы, т.е. для всех индексов конкретной базы. Прямо в плане обслуживания, в задании rebuild index (перестроение индекса) выбираем не все базы скопом, а какую нам надо и там же указываем - change free space per page percentage to (изменить свободное место страницы на). Это ведь оно и есть? Или я заблуждаюсь?
koloskovv Автор
Прямых настроек fill factor для базы как бы нет, но при обслуживании конкретной базы в скрипте, перебирающем все/определенные индексы, можно указать параметр для ALTER INDEX .... REBUILD WITH (FILLFACTOR = 85)