Записки оптимизатора 1С (часть 5). Ускорение RLS-запросов в 1С системах

Замахнемся сегодня на RLS. Обсуждать будем проблемы по нашему профилю, связанные с производительностью 1С:Предприятие. Но, в целом, этот материал может быть полезен и не только 1С-никам.

Немного о RLS

RLS (не РЛС) – это, напомню, ограничение доступа к данным на уровне записей – Row Level Security. То есть администратор открывает пользователю доступ к какому-либо объекту, но не ко всем его элементам (записям), а лишь к некоторым.

Если на уровне обычных ролей можно разрешить/запретить доступ ко всему объекту целиком, например, ко всем документам «Реализация товаров и услуг». То с механизмом RLS можно разрешить/запретить просматривать и изменять документы более тонко, например, только по определенным контрагентам, и только за определенный период. Вариаций бесконечное множество.

Инструмент призван решать как задачи безопасности данных – пользователь не сможет испортить данные, за которые он не отвечает, так и задачи конфиденциальности – пользователь не увидит то, что ему не положено видеть.

К сожалению, боли этот механизм доставляет очень много. И пользователям, и администраторам. Первые страдают от того, что у них все безбожно тормозит и работать иногда просто невозможно.  А вторые пребывают в шоке от поддержки всего этого хозяйства разграничения прав на плаву и попыток как-то ускорить работу пользователей. Чем больше пользователей, объектов системы, тем больше требуется шаблонов для правильной раздачи прав. И растёт это как снежный ком. Шаблоны и роли в какой-то момент начинают накладываться друг на друга, конфликтовать. Один и тот же отчет у одних пользователей работает, у вторых работает медленнее на порядки, а у третьих вообще не работает, и поддержка RLS превращается в сущий кошмар.

Это проблематика в двух словах. Копать дальше в руду не будем. Просто знаем, что есть расширенные настройки ролей 1С с собственным языком запросов и конструктором для настройки шаблонов ограничений, которые затем платформа 1С трансформирует в довольно громоздкие sql-запросы.

Почему запросы с RLS очень часто такие долгие?

Основная причина одна – неоптимальный план выполнения запросов. А вот почему он неоптимальный давайте разбираться.

Первое, на что следует обратить – это неактуальная статистика. Про это написано много статей. Статистика предоставляет информацию о том, какой объем данных нужно будет обработать при выполнении запроса. Один и тот же запрос на разных объемах данных будет использовать разные планы запросов. На маленьких объёмах – обработка попроще, а на миллионах строк будет совсем другой способ. Соответственно, неактуальные статистики могут привести к неправильной стоимостной оценки операций в запросе и к неправильному плану выполнения запроса и, в том числе, к неправильно используемым индексам и видам соединений.

Поэтому важно иметь всегда актуальные статистики, а значит должен быть правильно настроен регламент по их пересчету. Качественное и своевременное обновление статистики – условие, в принципе, необходимое для работы базы данных, и часто помогает. Но в случае RLS, по нашему опыту, бывает редко.

Куда более важный аспект, на который стоит обратить внимание – это неверный план запроса из-за нехватки времени на его поиск. По нашему опыту это одна из основных причин увеличения длительности запросов с RLS.

Ограничения, накладываемые в шаблоне, приводят к огромному количеству соединений между таблицами. На уровне СУБД это выглядит примерно так: 

Очень много левых соединений – внешних, внутренних, вложенных. Их может быть сотни и даже тысячи. Такие запросы могут стать просто монструозными и занимать тысячи строк кода. Мы в своей практике встречали запрос с текстом на 4 Мб(!). Выполнялся он крайне неторопливо.

Один и тот же запрос с RLS может выполняться в разное время по-разному. Иногда довольно быстро – за доли или единицы секунд, а иногда совсем приунывает, и выполняется минуты, десятки минут. Ситуация в таких громоздких запросах слабо предсказуемая и, как правило, связана с временем ожидания оптимизатора запроса SQL Server. Оптимизатор запросов выбирает план выполнения запроса с наименьшими затратами после создания и оценки нескольких планов запросов. Оптимизатор тратит на подбор подходящего плана ограниченное время, которое должно быть (и есть) сильно меньше длительности выполнения самого запроса. Оптимизатор перебирает различные комбинации выполнения частей запроса и должен выбрать самую оптимальную. Чем больше таблиц, и соединений между ними, тем больше комбинаций выполнения запроса будет в плане. Оптимизатор оперирует множеством факторов, среди которых:

  1. Использование индексов: кластеризованные, некластеризованные, просто таблицы.
  2. Метод поиска: Index Seek, Index Scan, Table Scan.
  3. Метод физического соединения: Nested Loops, Hash Join, Merge Join.
  4. Выполнение частей запроса параллельно или последовательно.

Соответственно, если оптимизатор не успевает перебрать все возможные комбинации, то выбранный план запроса может оказаться далеко не самым оптимальным. Именно этим и объясняется такой разброс по времени выполнении одного и того же запроса.

Посмотрите на рисунок ниже. На нем приведена трасса Reads из мониторинга Perfexpert (запросы с более 50 тыс. лог. чтений) по очень ненагруженной системе 1С, но в которой используется RLS.

Для примера возьмем запрос с хешем MD5 12391214314792349318. За четыре дня он попался в трассу 19 раз.

Это точно запрос с RLS. Вот его полный текст:

Из трассы видно, что его длительность прыгала от 0,6 сек. до почти 2 мин. Причем в половине случаев запрос выполнялся условно быстро за 1-2 сек., а в остальных случаях дольше минуты.

Обратите внимание на другие колонки трассы по отобранным 19-ти запросам — количество логических чтений и количество планов запросов.

Мониторинг Perfexpert помимо текста запросов собирает и планы запросов. Суммарное количество планов запросов, которые использовал SQL Server для нашего запроса в разные дни равняется семи (на самом деле их может быть больше, т.к. не все планы запросов попадают в Perfexpert). И только с помощью одного плана запрос выполнялся с ~80 тыс. логических чтений. С остальными же планами запрос вынужден был делать миллионные чтения, что сразу увеличивало его длительность на порядки:

С физикой процесса разобрались. Идем дальше, чтобы понять как с этим бороться.

Что делать

Альтернатив не много.

1. Использовать «Производительный режим работы RLS»

В отличии от стандартного режима, где ограничения прописываются на языке запросов 1С в ролях и добавляются к основному запросу в виде левых соединений, здесь все отборы и ограничения добавляются не в запрос, а в так называемые ключи доступа. И в итоговом запросе указываются именно ключи доступа. Ключи доступа – это один справочник «КлючиДоступа» и два регистра сведений: «КлючиДоступаКОбъектам» и «КлючиДоступаПользователей».

Предрассчитанные ключи доступа, с одной стороны, должны давать хороший прирост по скорости выполнения запроса, но, с другой, требуют правильного обслуживания и обновления данных в регистрах. Очень часто при входе пользователей в систему запускается обработка проверки актуальности ключей аналитики, которая удаляет неактуальные ключи и обновляет существующие – это серьезная нагрузка на всю систему и дополнительные ожидания пользователей.

Релевантной статистики по данному методу у нас пока нет, поэтому ни хвалить, ни ругать его я не могу. Но точно есть подводные камни, надо уметь их видеть и обходить.

2. Отказаться от RLS

Это самое, казалось бы, простое решение. Но совсем отказаться от RLS нельзя, т.к. некоторые виды бизнеса просто не могут себе этого позволить: есть чувствительные данные, да и конфиденциальность никто не отменял. А значит нужно прописывать ограничения вручную на уровне запросов в коде 1С. Что будет трудозатратно, не факт, что будет работать быстрее, да еще повлечет за собой вагон функциональных ошибок.

3. Оптимизация РЛС (упрощение)

Этот вариант выглядит более реалистичным. Подходит тоже далеко не всем. Если вы когда-нибудь видели список ролей в крупной ИТ-системе (1000+, из которых больше половины использует RLS), то понимаете, что провести их аудит задача крайне непростая, претендующая на отдельный проект.

4. Помочь оптимизатору запросов выбрать правильный план.

Этот вариант, на первый взгляд, может показаться нереальным, но нет, решение есть.

Использование QProcessing для ускорения RLS в 1С-системах

Как мы помним, в тексте 1С-запросов нельзя вставлять подсказки (хинты) для sql-запросов. А бывает, что очень нужно. Поэтому этот момент обходится при помощи нашего решения Softpoint QProcessing, представляющего из себя программный прокси-сервер, устанавливается между сервером приложений 1С и сервером баз данных MS SQL Server. Позволяет, не изменяя код приложения 1С, задавать и применять правила модификации определенных sql-запросов.

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

Ниже представлен рисунок, демонстрирующий сравнение неэффективного плана с 4 млн. логических чтений и длительностью около 1 минуты, и оптимального плана, при котором запрос выполняется за 8 секунд, выполнив лишь 64 тыс. логических чтений.

Видно, что плохой план использует в качестве метода левого соединения вложенные циклы (Nested Loops) и это не очень здорово. Если в тексте запроса заменить все условия «LEFT OUTER JOIN» на «LEFT OUTER HASH JOIN», явно указав оптимизатору SQL использовать хэширование в качестве типа соединения, то запрос выполняет на два порядка меньше логических чтений и на порядок сокращается его длительность. Это очень хороший результат. И такой эффект наблюдается почти для всех запросов с RLS.

Немного цифр для понимания эффекта ДО и ПОСЛЕ применения QProcessing.

Для анализа в продуктивной базе взял два периода длительностью одна календарная неделя каждый со схожим профилем нагрузки (количество пакетов в секунду, транзакций, пользователей, sql-сессий). Первая неделя – без использования QProcessing, вторая — с QProcessing.

1) Пример 1. Только один вид запросов

По хешу запросов отобрал один с RLS, у которого длительность значительно скачет на первой неделе и сравнил статистику по нему на двух неделях.

 Кол-во запросовСреднее кол-во лог. чтенийСумм. кол-во лог. чтенийСредняя длит-тьСумм.
длит-ть
Максим.
длит-ть
Неделя ДО532 867 882151 997 78953,22сек47м 0сек2м 45сек
Неделя ПОСЛЕ54117 6546 353 3530,99сек53,66сек1,84сек

 Эффект очевиден.

2) Пример 2. Статистика по всем запросам из трассы DURATION

Поскольку один запрос – не очень показательный пример, то взял всю трассу Duration (запросы с длительностью > 5 сек.) за первую и вторую неделю.

На рисунках ниже представлен скриншот трассы DURATION по каждой неделе. Запросы сгруппированы по видам, для каждого вида показано количество запросов, средние и максимальные длительности выполнения. На диаграмме показаны количественные метрики из трассы, разбитые по трём группам длительностей выполнения запросов: «< 10 сек.», «10 .. 60 сек.» и «> 60 cек.».

Неделя 1 (без QProcessing)
Неделя 1 (без QProcessing)
Неделя 2 (с QProcessing)
Неделя 2 (с QProcessing)

За вторую неделю с включенными правилами QProcessing в системе зарегистрировано тяжелых sql-запросов длительностью более пяти секунд всего 250 шт. То есть, их количество сократилось с 907 до 250 – в 3,5 раза. Причем запросов длительностью более 1 мин. в трассе не осталось вообще, а запросов длительностью более 10 секунд стало в разы меньше и осталось 122 против 455. Еще раз акцентирую внимание, что профиль нагрузки на обеих неделях идентичный. Соответственно, эти цифры не говорят, что запросов в системе стало меньше. Это значит, что стало меньше запросов длительностью более 5 секунд, а остальные просто мониторинг Perfexpert не собирает в трассу.

Итого

Поддержка RLS в 1С-системах не самый простой процесс, но зато отнимающий очень много сил у поддержки и нервов у пользователей. В статье я показал, что есть достаточно простой способ ускорить запросы с RLS с уже настроенными ограничениями, который:

  1. Не требует переписывания кода 1С и переписывания ограничений в шаблонах.
  2. Исключает наличие функциональных ошибок, которые обязательно будут, если проводить действия из п.1
  3. Не требует ресурсов со стороны разработчиков и поддержки.

Надеюсь, статья с очередным практическим(!) кейсом использования программы QProcessing вам понравилась.

Для подготовки материала я использовал, как обычно, данные, собранные системой мониторинга Perfexpert. Почитать про его возможности можно на нашем сайте https://perfexpert.ru. А практические кейсы с ним и QProcessing мы описываем почти во всех наших статьях: https://habr.com/ru/companies/softpoint/articles/.

Ну а кто решился и готов попробовать QProcessing (бесплатно!) и оценить эффект, милости просим сюда:
https://qp-rls.softpoint.ru.

Ссылки на остальные части Записок оптимизатора1С:

1. Записки оптимизатора 1С (часть 1). Странное поведение MS SQL Server 2019: длительные операции TRUNCATE.

2. Записки оптимизатора 1С (часть 2). Полнотекстовый индекс или как быстро искать по подстроке.

3. Записки оптимизатора 1С (часть 3). Распределенные взаимоблокировки в 1С системах.

4. Записки оптимизатора 1С (часть 4). Параллелизм в 1С, настройки, ожидания CXPACKET.

Меню

Что будем искать? Например,репликация