При проектировании учетных систем периодически возникает задача хранения и обработки гигантских массивов нормативно-справочной информации (каталоги автозапчастей, базы материалов, глобальные классификаторы, базы штрихкодов), объем которых составляет десятки и даже сотни миллионов строк. В среде разработчиков существует опасение, что платформа 1С:Предприятие не способна стабильно работать с таблицами свыше 2–3 миллионов записей. Однако под капотом 1С работает полноценная реляционная СУБД (MS SQL Server, PostgreSQL и др.), которая изначально рассчитана на терабайтные массивы. Главная задача разработчика — не допустить неоптимальных конструкций и операций полного сканирования таблицы (Table Scan). Разберем по шагам, как правильно спроектировать работу с такими объемами данных в 1С.
Для того чтобы система работала стабильно на объемах в 30–100 миллионов записей, выясним причину возможных замедлений и проанализируем критические точки:
В ИЕРАРХИИ в запросах строится через рекурсивные алгоритмы или создание временных таблиц. На миллионных выборках это приводит к резкой деградации планов запросов в СУБД. Рекомендуется использовать плоскую структуру справочника, а классификацию выносить в отдельные связующие таблицы.Проанализируем специфику построения индексов в платформе 1С. В справочниках платформа создает кластерный индекс по ссылке, а также отдельные некластерные индексы по реквизитам, у которых включено свойство Индексировать. Однако создать составной пользовательский индекс по произвольному набору реквизитов в справочнике штатными средствами метаданных невозможно.
Если для бизнес-логики не требуются ссылочные связи внутри других документов, а задача сводится к хранению параметров и быстрому поиску соответствий, отличной альтернативой справочнику является непериодический регистр сведений. В регистрах сведений платформа формирует составной кластерный индекс по измерениям в порядке их следования. Если расположить измерения от наиболее селективного к наименее селективному, СУБД сможет выполнять мгновенный поиск сразу по комбинации ключевых полей (Index Seek).
Если справочник все же необходим и требуется производить регулярный быстрый отбор по комбинации 2–3 реквизитов (например, артикул + бренд + код производителя), наиболее эффективным архитектурным приемом является добавление индексированного реквизита-хэша. Рассмотрим пример реализации:
КлючПоиска фиксированной длины или переменной длины с установленным признаком Индексировать.ПередЗаписью модуля объекта формируем очищенную нормализованную строку (перевод в верхний регистр, удаление пробелов, дефисов и спецсимволов) или числовую контрольную сумму (хэш).Посмотрим на пример заполнения реквизита в модуле объекта справочника:
Процедура ПередЗаписью(Отказ)
// Нормализуем исходные значения
ОчищенныйАртикул = ВРег(СокрЛП(СтрЗаменить(Артикул, "-", "")));
ОчищенныйБренд = ВРег(СокрЛП(Бренд));
// Формируем составной суррогатный ключ
КлючПоиска = ОчищенныйБренд + "/" + ОчищенныйАртикул;
КонецПроцедуры
При выборке данных из таблиц объемом более 30 миллионов записей малейшая ошибка в тексте запроса заставит СУБД читать всю таблицу с диска в оперативную память. Разберем правила составления эффективных запросов:
ГДЕ только строгие проверки Равно или конструкцию ПОДОБНО с поиском по началу строки (например, &Образец + "%"). Поиск с символом процента в начале строки ("%...") делает использование B-Tree индекса невозможным.ПОДСТРОКА, ВЫРАЗИТЬ, функции приведения типов) над полями таблицы в блоке ГДЕ, так как они приводят к вычислению функции для каждой строки таблицы.ВЫБРАТЬ ПЕРВЫЕ N), чтобы ограничить сканирование индекса при совпадении критериев.Рассмотрим пример быстрого запроса для поиска информации по коду и индексному реквизиту:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ ПЕРВЫЕ 100
| Материалы.Ссылка КАК Ссылка,
| Материалы.Код КАК Код,
| Материалы.Наименование КАК Наименование,
| Материалы.Артикул КАК Артикул
|ИЗ
| Справочник.Материалы КАК Материалы
|ГДЕ
| Материалы.КлючПоиска = &КлючПоиска";
Запрос.УстановитьПараметр("КлючПоиска", "BOSCH/0250202022");
Результат = Запрос.Выполнить().Выгрузить();
Для ускорения сортировки и отбора в стандартных формах списков и запросах платформа 1С предоставляет режим реквизита Индексировать с дополнительным упорядочиванием. При включении этого режима СУБД создает составной индекс, содержащий значение самого реквизита и ссылку на элемент. Это позволяет выполнять быстрый отбор по реквизиту с одновременным упорядочиванием по наименованию или коду без дорогостоящих операций сортировки в tempdb.
Если бизнес-требования диктуют поиск по неполным совпадениям, подстрокам в середине слов, с учетом опечаток и морфологии, удерживать такие алгоритмы внутри транзакционной СУБД на 30 миллионах записей нецелесообразно. Рассмотрим альтернативные архитектурные пути:
Таблицы размером от 30 миллионов записей крайне чувствительны к состоянию статистики распределения данных. Если статистика устаревает, оптимизатор СУБД ошибочно выбирает неэффективные планы (например, сканирование таблицы вместо поиска по индексу). Для таких справочников необходимо настроить регулярное обновление статистики СУБД (желательно с полным сканированием для ключевых индексов) и периодическую дефрагментацию индексов.