При проектировании высоконагруженных систем на платформе 1С:Предприятие одним из ключевых факторов производительности является правильная работа с индексами СУБД. Регистры сведений — это один из наиболее часто используемых прикладных объектов для хранения нетехнологических данных (цены, курсы валют, настройки, штрихкоды и т.д.). Понимание того, как именно платформа транслирует наши метаданные в физические индексы на уровне базы данных (MS SQL Server, PostgreSQL, IBM DB2 или Oracle), позволяет избежать критических проблем с производительностью и блокировками. Рассмотрим подробно этот механизм.
Когда мы создаем новый непериодический регистр сведений с независимым режимом записи, платформа автоматически создает первичный ключ на уровне СУБД. Этому ключу соответствует уникальный кластерный индекс. Давайте разберем, по какому принципу он строится.
В состав этого кластерного индекса по умолчанию входят абсолютно все измерения регистра. Порядок полей в индексе в точности соответствует порядку следования измерений в дереве метаданных конфигуратора. Например, если в регистре созданы измерения Организация, Номенклатура и Склад, физический кластерный индекс СУБД будет построен по цепочке:
Организация + Номенклатура + Склад
Этот индекс является уникальным. СУБД на физическом уровне гарантирует, что в таблице не может существовать двух записей с одинаковым набором этих измерений.
Теперь проанализируем, как влияют различные флаги в свойствах измерений на создание дополнительных (некластерных) индексов:
ВедущееИзмерение + ОстальныеИзмерения.... Наличие такого индекса позволяет платформе мгновенно находить и очищать движения при удалении элементов ведущего объекта (например, при удалении номенклатуры удаляются связанные записи в регистре).ИзмерениеСОтбором + ОстальныеИзмерения.... Это критически важно для быстрого выполнения запросов, где данное измерение выступает в качестве единственного или первого условия фильтрации.Рассмотрим, как меняется физическая структура базы данных при включении периодичности регистра сведений. Это один из важнейших аспект оптимизации работы с временными срезами.
Если регистр сведений становится периодическим (например, в пределах секунды, дня или месяца), платформа кардинально перестраивает структуру основного кластерного индекса:
Период автоматически добавляется в самое начало кластерного индекса.Период + Измерение1 + Измерение2 + ....Такой подход обеспечивает максимальную скорость работы с временными интервалами и получение актуальных срезов данных. Однако здесь кроется серьезная ловушка для разработчика: если вы выполняете запрос с отбором по Измерению1, но не указываете условие по времени (Период), СУБД не сможет эффективно использовать данный кластерный индекс. Произойдет сканирование индекса (Index Scan) вместо быстрого поиска (Index Seek), что приведет к падению производительности на больших таблицах. В таких случаях обязательно нужно настраивать дополнительное индексирование для измерений.
Давайте разберем фундаментальное правило работы составных индексов в реляционных базах данных — правило левого префикса. Индекс по полям А + Б + В эффективно работает только тогда, когда в условиях запроса («ГДЕ» или в параметрах виртуальной таблицы) используются:
А;А + Б;А + Б + В.Если мы пишем запрос, в котором отбор идет только по полю Б или по связке Б + В, СУБД проигнорирует этот индекс (либо его эффективность будет крайне низкой).
Представим конфигурацию, где в периодическом регистре сведений измерения расположены в следующем порядке:
КонтрагентДоговорКластерный индекс выглядит как: Период + Контрагент + Договор.
Если мы часто выполняем запросы с поиском по конкретному договору без указания контрагента, система будет "тормозить". Чтобы решить эту проблему, нам необходимо либо поменять измерения местами в дереве метаданных (если это логично для бизнес-архитектуры), либо установить для измерения Договор свойство "Индексировать", чтобы платформа создала под него отдельный физический некластерный индекс.
Проанализируем ситуацию, когда у регистра сведений установлен режим записи "Подчинение регистратору". В этом случае логика работы с индексами полностью меняется, поскольку данные жестко привязываются к документам, которые их записывают.
Платформа 1С отказывается от стандартного кластерного индекса по измерениям и строит его по совершенно иному принципу:
Регистратор + НомерСтроки.Это технологически необходимо платформе для того, чтобы при перепроведении или отмене проведения документа СУБД могла моментально найти, заблокировать и удалить все движения, относящиеся к данному конкретному Регистратору.
Что же происходит с измерениями в таком режиме? Чтобы отчеты и запросы по измерениям не выполнялись полным сканированием всей таблицы, платформа автоматически генерирует дополнительные некластерные индексы для всех измерений, у которых в конфигураторе взведен флаг "Основной отбор".
Разберем, как платформа работает с виртуальными таблицами СрезПоследних и СрезПервых. Начинающие разработчики часто думают, что для этих таблиц СУБД создает какие-то отдельные скрытые индексы. Это не так.
При обращении к срезам данных платформа генерирует сложный SQL-запрос с подзапросами, который полностью опирается на существующую структуру метаданных. В СУБД этот запрос задействует основной кластерный индекс регистра.
Рассмотрим типичный пример запроса получения среза последних цен:
ВЫБРАТЬ
ЦеныНоменклатурыСрезПоследних.Номенклатура,
ЦеныНоменклатурыСрезПоследних.Цена
ИЗ
РегистрСведений.ЦеныНоменклатуры.СрезПоследних(&Период, Номенклатура = &Номенклатура) КАК ЦеныНоменклатурыСрезПоследних
Чтобы этот запрос отработал мгновенно, СУБД должна быстро найти максимальный Период для переданной Номенклатуры. Если кластерный индекс выглядит как Период + Номенклатура, СУБД придется анализировать весь диапазон дат. Оптимальным решением для быстрого получения среза последних является использование структуры индекса, где измерение стоит раньше периода. Платформа 1С решает это созданием дополнительных некластерных индексов при включении индексации у измерений.
Если наш регистр сведений включен в состав хотя бы одного Плана обмена и для него активирована авторегистрация изменений, платформа создает в базе данных дополнительную служебную таблицу регистрации изменений. На уровне СУБД она обычно имеет префикс _InfoRgChngX (где X — уникальный идентификатор таблицы).
Для этой таблицы платформа автоматически создает свои собственные индексы:
Необходимо учитывать, что при любой записи в регистр сведений платформа будет выполнять сопутствующую запись и обновление индексов в этой служебной таблице. Это может существенно снизить общую скорость записи данных (нагрузка на операции дискового ввода-вывода), поэтому включать регистры в планы обмена следует обдуманно.
Давайте проанализируем, в каких ситуациях имеет смысл устанавливать индексирование для ресурсов и реквизитов регистра сведений, а когда это является грубой ошибкой проектирования.
Для реквизитов регистра свойство "Индексировать" может быть очень полезно, если данный реквизит часто используется в условиях запросов (в секции ГДЕ). Например, если у нас есть реквизит АвторИзменения и нам периодически нужно быстро выбирать записи, измененные конкретным пользователем.
Что касается ресурсов — их индексация в 95% случаев является архитектурной ошибкой. Ресурсы предназначены исключительно для хранения целевых значений (сумм, цен, количеств, текстовых описаний). Если вам регулярно приходится накладывать жесткие условия фильтрации на значения ресурса в запросах, это явный сигнал о том, что данное поле спроектировано неверно и должно быть переведено в разряд измерений регистра.
Для закрепления материала сведем все возможные варианты генерации индексов платформой 1С в удобную структуру:
Период (если периодический) + Измерение1 + Измерение2 + ... (или Регистратор + НомерСтроки для подчиненных регистров).Ведущее.Основной отбор или включено свойство Индексировать.Используя эти правила при проектировании метаданных, мы сможем создавать максимально быстрые, масштабируемые и устойчивые к блокировкам регистры сведений в наших конфигурациях.