Как стать автором
Поиск
Написать публикацию
Обновить

Записки оптимизатора 1С (ч.12).  СрезПоследних в 1C: Предприятие на PostgreSQL. Почему же так долго?

Уровень сложностиСредний
Время на прочтение11 мин
Количество просмотров4.7K
Всего голосов 9: ↑9 и ↓0+10
Комментарии14

Комментарии 14

По хорошему надо бы в MS SQL параметр max_dop (max degree of parallelism) выставить в 1 и посмотреть план без параллелизма, он должен быть более понятный и логичный. Параллельные планы иногда тормозят, когда база на hdd, а не на ssd

Торможение на параллелизме напрямую никак не связанно с производительностью дисков.

Если вы про синтетический тест, то использовался ssd диск. Запрос выполняется мгновенно, к параллелизму претензий нет )).

А что мешает самой 1С формировать оптимальный текст запроса, в зависимости от используемой СУБД?

Ну это риторический вопрос.

Поэтому пока вендоры размышляют мы нашли альтернативный вариант.

Но это же сумасшествие! Тул в середине меняет запрос! А как потом разбираться если оно бахнет что-то не то в таблице?! ИМХО - это слишком опасный паттерн, особенно основанный на регулярках!

Я вас понимаю - без должного инструментария не очень понятно и даже боязно идти в эту историю. Но мы там уже более 10 лет. Покажу примерный ход внедрения QProcessing (QP).

  1. Мы всегда внедряем QProcessing в связке с мониторингом Perfexpert (PE), который позволяет собирать трассы разных тяжелых запросов, а также трассу ВСЕХ запросов, которые были модифицированы QP'ом. Поэтому всегда можно посмотреть и проверить результат. Без PE никак. Даже если вам не нужен мониторинг, на период внедрения, он у вас будет установлен.

  2. Интерфейс QP позволяет прогнать вручную запросы через правило, убедиться, что они модифицируются правильно или не модифицируются вообще. Дальше модифицированные запросы нужно просто выполнить в Studio на тесте и убедиться, что результат запроса один и тот же, а время сократилось. Да, тестовый стенд также необходим, как и мониторинг.

  3. Только после многократного прогона можно переходить в продуктив.

  4. Еще важно! Если вдруг запрос после модификации стал выполняться на проде с ошибкой (exception, например, из-за синтаксиса), то QP повторно отправляет на SQL Server запрос, но в исходном виде. То есть, пользователь ничего не заметит, кроме того, что запрос будет выполняться опять долго. Ну а разработчики/администраторы в логах QP быстро увидят эту ситуацию.

Как-то так.

Что юристы Софтпоинта скажут про целостность лицензионного соглашения 1С с этим инструментом, меняющим внутренние запросым1С?

Там вот у 1С ещё приписка есть...

Данное ограничение необходимо для обеспечения стабильности работы механизмов системы, осуществления поддержки и возможности перехода на новые версии «1С:Предприятия».

Если выполняется данное условие, то какие проблемы?

Но ведь 1С и так уже использует собственный патч к PgSQL для повышения производительности. Не лучше ли подкрутить исходники этого патча? Он же опенсорсный.

Программисты платформы "ниасилили" особенностей СУБД, которые работают в режиме версионирования записей. Вместо этого они выпустили патч, который превращает ванильный PostgreSQL в подобие "блокировочника" а-ля MS SQL (чтобы накладывать блокировки не на уровне записей, а на уровне таблиц), потому что с ванильным PostgreSQL 1С не работает от слова "совсем". Хотя спонсирование российских команд разработчиков PostgreSQL позволило протолкнуть некоторые решения этого патча в основную ветку (то ли с 13, то ли с 14 версии).

Но суть статьи не в этом (не в патче). Суть в том, что это сама платформа 1с генерирует "кривые" запросы при использовании PostgreSQL И никакими патчами к СУБД это не исправить. Тут или падишах умрет, или ишак: или текущая команда таки выучит PL/pgSQL, или их поменяют на тех, кто знает этот диалект.

Скрипт синтетичсекого теста долго заполняет таблицу. Более быстрое заполнение тестовой таблицы, если кто захочет попробовать:

\timing on \\
drop table if exists test_gr;
create table test_gr (col1 integer, col2 integer, per integer);
insert into test_gr select 100000*random(),100000*random(),10*random() from generate_series(1,100000000);
create index ind_1 on test_gr (col1,per);

INSERT 0 100000000
Time: 212985.280 ms (03:32.985)
CREATE INDEX
Time: 125360.633 ms (02:05.361)

Размер таблицы  test_gr 4224 MB , индекса ind_1 676 MB.

Тестовый запрос из статьи выполняется 56 секунд. План:

Hash Join  (cost=3610113.19..3612772.26 rows=12010 width=8) (actual rows=1 loops=1)
 Hash Cond: ((t4.col1 = t6.col1) AND ((max(t4.per)) = t6.per) AND ((max(t5.col2)) = t6.col2))
  Buffers: shared hit=55484996 read=630228 written=1, temp read=4765 written=8117
  ->  Finalize GroupAggregate  (cost=3606132.40..3608680.50 rows=9863 width=12) (actual rows=100001 loops=1)
   Group Key: t4.col1, (max(t4.per))
    Buffers: shared hit=55483943 read=630222 written=1, temp read=4765 written=8117
    ->  Gather Merge  (cost=3606132.40..3608433.93 rows=19726 width=12) (actual rows=300003 loops=1)
      Workers Planned: 2
      Workers Launched: 2
      Buffers: shared hit=55483943 read=630222 written=1, temp read=4765 written=8117
      ->  Sort  (cost=3605132.38..3605157.04 rows=9863 width=12) (actual rows=100001 loops=3)
        Sort Key: t4.col1, (max(t4.per))
        Sort Method: external merge  Disk: 2160kB
        Buffers: shared hit=55483943 read=630222 written=1, temp read=4765 written=8117
        Worker 0:  Sort Method: external merge  Disk: 2160kB
        Worker 1:  Sort Method: external merge  Disk: 2160kB
        ->  Partial HashAggregate  (cost=3603950.89..3604049.52 rows=9863 width=12) (actual rows=100001 loops=3)
         Group Key: t4.col1, (max(t4.per))
         Batches: 5  Memory Usage: 8241kB  Disk Usage: 11712kB
         Buffers: shared hit=55483929 read=630222 written=1, temp read=3955 written=7304
         Worker 0:  Batches: 5  Memory Usage: 8241kB  Disk Usage: 11496kB
         Worker 1:  Batches: 5  Memory Usage: 8241kB  Disk Usage: 11696kB
         ->  Hash Join  (cost=2348705.38..3524655.16 rows=10572764 width=12) (actual rows=1666645 loops=3)
           Hash Cond: ((t5.col1 = t4.col1) AND (t5.per = (max(t4.per))))
           Buffers: shared hit=55483929 read=630222 written=1
           ->  Parallel Seq Scan on test_gr t5  (cost=0.00..957201.63 rows=41666063 width=12) (actual rows=33333333 l>
             Buffers: shared hit=1 read=540540
             ->  Hash  (cost=2347225.92..2347225.92 rows=98631 width=8) (actual rows=100001 loops=3)
               Buckets: 131072  Batches: 1  Memory Usage: 4931kB
Time: 56498.999 ms (00:56.499)

если увеличить work_mem, чтобы Batches: 1 , то выполнится за 74 секунды.

дополнение, забыл команду создания второй таблицы. Она не обязательно должна быть временной, с постоянной удобнее:

--Создание и заполнение таблицы фильтра
create table Cols (col integer);
insert into Cols values(11111); -- Реальное значение
Зарегистрируйтесь на Хабре, чтобы оставить комментарий