Уже давно стали обыденными внедрения решений на платформе 1С:Предприятие на тысячу одновременных пользователей. Есть внедрения и более масштабные. И масштаб внедрений растёт. Поэтому мы решили убедиться, что платформа выдержит нагрузку нашего самого востребованного на крупных внедрениях решения 1C:ERP на 30 000 одновременно работающих пользователях.

Почему именно 30 000 пользователей, как мы измеряли производительность и как добились желаемой производительности – под катом.

Почему именно 30 000 пользователей?

Согласно требованиям крупнейших компаний России, представленных Национальным центром компетенций по информационным системам управления холдингом (АНО «НЦК ИСУ»), 30 000 пользователей соответствует размерности самых крупных российских организаций с сотнями тысяч сотрудников (далеко не все сотрудники целый день работают в ERP).

Нам нужно было убедиться, что в таких сценариях платформа 1С:Предприятие работает надёжно, качественно и производительно.

А что значит производительно?

Как мы измеряем производительность

В экосистеме 1С:Предприятия сложившийся стандарт оценки производительности — это Apdex (акроним Application Performance inDEX).

Как вычисляется Apdex.

В качестве опорной точки в Apdex выступает целевое (желаемое) время выполнения ключевых операций (это время назначается экспертным путём). Мы определили целевые времена для всех ключевых операций, участвующих в сценарии теста.

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

Если время укладывается в диапазон от целевого до четырёх целевых времён – то это «зона толерантности», где пользователю работать не очень комфортно, но он ещё готов терпеть.

А если мы не уложились в четыре целевых времени, то с точки зрения пользователя программа зависла и это неудовлетворительное время.

Индекс производительности Apdex вычисляется так:

  • Т — целевое время выполнения операции

  • N — общее количество выполнений операции

  • NS — количество выполнений, попавших в диапазон [0, T)

  • NT — количество выполнений, попавших в диапазон [Т, 4*Т)

  • Apdex = (NS + NT/2) / N

Индекс Apdex принимает значения от 0 до 1 и его значение истолковывается следующим образом:

Значение Apdex

Производительность

0,94 — 1,00

Отличная

0,85 — 0,93

Хорошая

0,70 — 0,84

Удовлетворительная

0,50 — 0,69

Плохая

0,00 — 0,49

Неприемлемая

На практике достаточно значения Apdex = 0,85 или выше — это устраивает подавляющее число заказчиков. Это означает, что у нас больше половины всех операций уложилось в целевое время и некоторая часть операций попала в зону толерантности.

Приведём пример. Предположим, что ровно половина операций попала в целевое время (оценка будет 0,5), а вторая половина операций уложилась в диапазон от целевого времени до целевого времени умножить на 4. Соответственно, эта часть операций внесёт вклад 0,25 и значение Apdex получится 0,75. Этого мало — нам требуется, чтобы большее количество операций было в диапазоне до целевого времени и допустимо, чтобы какая-то часть из него выбилась. И крайне нежелательно, чтобы какие-то операции не укладывались в четыре целевых времени.

Тестовая база данных

Поскольку целевая аудитория теста — это крупнейшие организации, были собраны требования крупных компаний по размеру и составу базы данных. Далее требования были усреднены до средней крупной компании со смещением в сторону более крупных. Получилась база размером около 1 Терабайта.

Характеристики базы
  • 28 000 000 договоров

  • 22 000 000 движений себестоимости

  • 20 000 000 бух. проводок

  • 2 500 000 контрагентов

  • 1 000 000 основных средств

  • 700 000 сотрудников

  • 25 000 пользователей

  • 1 800 подразделений

  • 56 организаций (с единой головной организацией)

И по документам в базе:

Документ

Количество

Регистратор расчётов

1 622 037

Заявка на расходование денежных средств

1 067 654

Списание безналичных денежных средств

1 067 386

Счет-фактура полученный

1 027 831

Заказ поставщику

778 551

Приобретение товаров и услуг

777 796

Заказ клиента

723 716

Счет-фактура выданный

567 056

Поступление безналичных денежных средств

537 417

Расходный ордер на товары

339 371

Реализация товаров и услуг

334 494

Приходный ордер на товары

285 622

Приобретение услуг и прочих активов

206 605

Внутреннее потребление

157 894

Прочее оприходование товаров

137 313

Реализация услуг и прочих активов

136 486

Принятие к учёту основных средств

129 407

ВСЕГО ДОКУМЕНТОВ

9 896 636

Тестовая инфраструктура

Архитектурно платформа 1С:Предприятие — это трехзвенная система, сердце которой — кластер серверов, где выполняется основная часть бизнес-логики.

Для работы с терабайтной базой мы смонтировали кластер из шести серверов: четыре рабочих сервера, один центральный и один — сервер лицензирования.

Спецификация серверов кластера:

Intel(R) Xeon(R) Gold 6426Y 2500 MHz L1/L2/L3 1280/32768/38400 KB
16 cores/32 threads, ОЗУ 256 Гб, 1 SAS SSD 1920 ГБ Samsung PM1643a

Для эмуляции пользовательской нагрузки мы использовали 32 сервера-нагрузчика, на каждом стояло по шесть виртуальных машин, для теста запускалось по 160 пользовательских сеансов на виртуальную машину.

Спецификация серверов-нагрузчиков:

Intel(R) Xeon(R) Gold 6226R 2900 MHz L1/L2/L3 1024/16384/22528 KB 16 cores/32 threads,
ОЗУ 256 Гб, 1 SAS SSD 1920 ГБ Samsung PM1643a

Статистика по потреблению памяти

Платформа активно использует сеансовые данные, которые представляет собой файлы, хранящиеся на диске и скопированные в оперативную память. Они могут не поместиться в память и считываться с диска, и тогда производительность системы может «просесть». Желательно, чтобы памяти под сеансовые данные хватало. Для этого нам в тестах требуется не менее 40 ГБ оперативной памяти.

Потребление памяти на серверах:

  • Управляющий сервис кластера ragent — 0,5 Гб

  • Сервис сеансовых данных — 3,5 Гб

  • Сервис журнала регистрации — 1,5 Гб

  • Главный менеджер кластера (сервис конфигурации кластера, сервис состояния кластера, сервис управления предметами отладки) — 1,5 Гб

  • Сервис получения списка сеансов — 0,5 Гб

  • Сервис заданий — 0,5 Гб

  • Сервис уведомлений клиента — 0,5 Гб

  • Остальные сервисы: < 0,3 Гб

  • Размер сеансовых данных — 25 Гб

Для СУБД PostgreSQL мы использовали сервер с 96-ядерным процессором и памятью 1 ТБ. Всё, что было возможно (данные, индексы, WAL, временные файлы), мы распределили по разным дискам, чтобы максимально распределить нагрузку.

Про сервер СУБД

ЦПУ: Ampere Altra Max M96-28, 2800 MHz 96 cores
Память: 1 Тб Samsung DDR4 64 Гб 3200 MT/s
Накопитель: SSD 7.7 Tb NVMe Samsung PM1733a

SAMSUNG MZWLR7T6HBLA	7T		/mnt/pg_data	Каталог кластера postgres
SAMSUNG MZWLR7T6HBLA	7T		/mnt/pg_tables	Табличное пространство v81c_data (данные)
SAMSUNG MZWLR7T6HBLA	7T		/mnt/pg_index	Табличное пространство v81c_index (индексы)
SAMSUNG MZWLR7T6HBLA	7T		/mnt/pg_wal		Журналы транзакций
SAMSUNG MZQL2960HCJR	894,3G	/mnt/pg_log		Логи postgres
SAMSUNG MZWLR7T6HBLA	7T		/mnt/pg_temp1	Табличное пространство временных таблиц
SAMSUNG MZWLR3T8HCLS	3,5T	/mnt/pg_temp2	Табличное пространство временных таблиц
SAMSUNG MZWLR3T8HCLS	3,5T	/mnt/pg_temp9	Табличное пространство временных таблиц
SAMSUNG MZQL2960HCJR	894,3G	[SWAP]

Параметры PostgreSQL:

temp_tablespaces = 'tmp1,tmp2,tmp9'

ssl = off

shared_buffers = 256GB
temp_buffers = 256MB
work_mem = 256MB
maintenance_work_mem = 1024MB
max_files_per_process = 8000

lc_messages = 'C'

client_min_messages = warning

logging_collector = on
log_directory = '/mnt/pg_log/files'
log_filename = 'postgresql-%Y-%m-%d_%H%M%S.log'
log_rotation_size = 1GB
log_temp_files = 4kB
log_checkpoints = on
log_connections = on
log_disconnections = on
log_lock_waits = on
log_timezone = 'Europe/Moscow'

wal_level = minimal
max_wal_senders = 0
synchronous_commit = off

max_locks_per_transaction = 1000
max_connections = 9000

max_parallel_workers_per_gather = 0
max_worker_processes = 2
max_parallel_workers = 1
max_logical_replication_workers = 0

listen_addresses = '*'
port = 5432

bgwriter_delay = 20ms
bgwriter_lru_maxpages = 500
bgwriter_lru_multiplier = 4.0

commit_delay = 1000
commit_siblings = 5

checkpoint_timeout = 20min
max_wal_size = 12GB
min_wal_size = 1GB
checkpoint_completion_target = 0.9

random_page_cost = 1.1
effective_cache_size = 512GB

from_collapse_limit = 20
join_collapse_limit = 20

autovacuum_max_workers = 80
autovacuum_naptime = 20s
autovacuum_vacuum_cost_delay = 10ms
autovacuum_vacuum_cost_limit = -1
autovacuum_vacuum_scale_factor = 0.01
autovacuum_vacuum_cost_limit = 0.005

jit = off
track_activity_query_size = 128 # Max 1048576
enable_temp_memory_catalog = on # ! PostgreSQL-1C

На всех серверах была установлена ОС Linux, СУБД PostgreSQL (от 1С) версии 16.4-49.1C.

Что мы оптимизировали в PostgreSQL

Как было сказано выше, в качестве СУБД в наших тестах мы использовали PostgreSQL от 1С. Кстати, тут — полный список СУБД, с которыми мы работаем.

Мы использовали версию PostgreSQL от 1С, потому что в ней мы можем поправить те узкие места, когда производительность системы начинает напрямую зависеть от сервера СУБД.

Про PostgreSQL от 1С

Особенности PostgreSQL от 1С:

  • Добавлены новые типы данных mchar и mvarchar

    • Точно воспроизводят поведение типов CHAR и VARCHAR у MS SQL Server

  • Расширения:

    • fasttruncate

    • fulleq

    • dbcopies_decoding

    • online_analyze

    • plantuner

  • Множество оптимизаций под сценарии, типичные для 1С

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

Часть наших оптимизаций связана с тем, что «ванильный» PostgreSQL одинаково работает со всеми таблицами — и с постоянно существующими в БД, и с временными. В некоторых ситуациях эта унифицированность избыточна.

Оптимизация отправки сообщений об инвалидации

В PostgreSQL для каждого соединения с БД (сессии) создается процесс (бэкенд), так что эти термины в известной степени можно считать взаимозаменяемыми.

В PostgreSQL есть несколько рабочих областей памяти. Есть области памяти, зарезервированные индивидуально под каждый бэкенд — они доступны только бэкенду, для которого выделены. И есть области памяти, которые доступны для всех бэкендов одновременно.

Этот довольно сложный механизм необходим для того, чтобы разные бэкенды могли одновременно работать с одними и теми же таблицами. Например, один бэкенд может выбирать данные из таблицы, а другой её обновлять. Чтобы у каждого бэкенда было своё «видение» этой таблицы, PostgreSQL использует этот механизм.

Чтобы совместная работа многих сессий не приводила к запутанным ситуациям, существует ещё один механизм, который позволяет синхронизировать всю эту конструкцию между различными бэкендами и с общей памятью. Это очередь сообщений. В эту очередь бэкендом помещается информация о том, когда и как этот бэкенд обновил данные в базе.

Другие бэкенды смотрят в эту очередь — а не поменялось ли там что‑то в интересующих их таблицах. Если что‑то поменялось — бэкенды инвалидируют свои данные, то есть выбрасывают невалидные данные из своей личной памяти и могут обратиться в общую память и взять оттуда свежие обновлённые порции данных. Как раз эта очередь служит механизмом синхронизации между различными бэкендами.

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

Эти сообщения благополучно доставлялись другим бэкендам; но так как у других бэкендов всё равно нет возможности получить доступ к данным «чужих» временных таблиц, то им незачем обрабатывать эти сообщения, да и отправлять эти сообщения не имеет смысла. Доработка заключалась в «вырезании» отправки сообщений, касающихся изменения данных временных таблиц.

Нет «лишних» сообщений — нет лишней нагрузки, нет обработки этих сообщений, нет расхода ресурсов и процессорного времени. Небольшая, но очень эффективная доработка.

Оптимизация блокировок временных таблиц

Следующая оптимизация также связана с временными таблицами, и она касается блокировок временных таблиц.

Блокировки таблиц — это механизм многоуровневых рекурсивных блокировок, который нужен, чтобы разграничить одновременно выполняемые из разных бэкендов действия над таблицами. Например, когда несколько сессий читают данные из одной и той же таблицы, то они могут делать это одновременно. Но если какая-то сессия хочет обновить таблицу, то другая сессия, которая хочет прочесть данные из этой таблицы, должна подождать конца обновления с учётом MVCC (multiversion concurrency control).

В «ванильном» PostgreSQL блокировки используются и для временных таблиц. Но поскольку доступ к временным таблицам возможен только из той сессии, в которой эти временные таблицы созданы — смысла в блокировках нет. Поэтому в нашей версии мы отключили блокировки временных таблиц и тем самым убрали ненужные нагрузку и потребление ресурсов.

Оптимизация блокировки SpinLock

Следующая доработка более техническая. Она заключается в том, что на определённых архитектурах (особенно это касается ARM-архитектуры с большим количеством ядер, особенно если ядер больше сотни) в коде было «горячее» место, сильно нагружающее процессор. Мы это место обнаружили и поправили. Проблема была в коде для синхронизации последовательного доступа к памяти использовалась блокировка spinlock.

Цитируем статью от Postgres Professional:

«Спин-блокировки или спинлоки (spinlock) предназначены для захвата на очень короткое время (несколько инструкций процессора) и защищают отдельные участки памяти от одновременного изменения.

Спин-блокировки реализуются на основе атомарных инструкций процессора, таких, как compare-and-swap. Если блокировка занята, ожидающий процесс выполняет активное ожидание — команда повторяется («крутится» в цикле, отсюда и название) до тех пор, пока не выполнится успешно. Это имеет смысл, поскольку спин-блокировки применяются в тех случаях, когда вероятность конфликта оценивается как очень низкая.»

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

Порядок выполнения операций важен для корректного функционирования упомянутой выше очереди сообщений.

Сначала сообщение помещается в буфер очереди, а потом обновляется информация о количестве сообщений в этом буфере. Этот порядок операций был очень важен, потому что если мы сначала обновим количество сообщений, а потом поместим сообщение в буфер, то между этими двумя операциями мог вклиниться какой-то поток (другой бэкенд) и, сначала увидев увеличившееся количество сообщений, начинал пытаться его читать. А по факту сообщения в очереди ещё нет, и это могло привести к неприятным ситуациям. Чтобы упорядочить эти действия использовался spinlock. Блокировка spinlock не позволяет нескольким потокам выполнить один и тот же код. Spinlock использовался как обёртка вокруг счётчика сообщений для того, чтобы упорядочить доступ к счётчику сообщений для того, чтобы потоки не конфликтовали между собой.

Но при большой нагрузке механизм spinlock давал о себе знать, потому что он реализован так, что в цикле проверяет переменную пока она не сбросится. При большом количестве ядер процессора это приводило к катастрофическим последствиям. Мы это обнаружили и заменили spinlock на конструкцию в виде барьера памяти.

Барьер памяти - это специальная инструкция процессора, которая говорит процессору о том что, необходимо завершить все операции  записи до того, как приступить к следующей операции. Тем самым достигается та же желаемая упорядоченность команд, но без использования spinlock.

Оптимизация операции ANALYZE для «широких» таблиц

Следующая доработка заключается в оптимизации операции ANALYZE для случаев, когда эта операция выполняется над «широкой» таблицей, в которой много столбцов и в которой присутствуют столбцы с переменной длиной.

Команда ANALYZE TABLE собирает статистику о содержимом таблицы. Эта статистика используется для определения наиболее эффективных планов выполнения запросов. Команда анализирует таблицы и индексы, собирает данные о распределении данных (например, количество уникальных значений в столбце, самые распространённые значения и т.п.) и сохраняет их в специальных системных таблицах.

ANALYZE работал по следующему алгоритму. Сначала выбирался некоторый ограниченный набор данных из таблицы. Далее для каждого столбца из этого набора данных по очереди считается статистика по содержимому данных в этом столбце.

Данные столбца записаны в память последовательно, первый столбец находится в начале выделенной для записи таблицы области памяти. Для второго столбца мы знаем, что длина первого столбца, например, 4 байта (для типа INT) — значит, второй столбец лежит в четырёх байтах от начала области памяти.

С появлением столбцов переменной длины (varchar, mchar, text и т. п.) чтобы узнать, где находится следующий столбец за столбцом с переменной длиной, нам надо узнать реальную длину данных, находящихся в столбце этой записи таблицы, и приплюсовать к предыдущему значению смещения. Если таких столбцов много, то, чтобы получить, например, доступ к данным 201-го столбца, нам нужно пройти по всем предыдущим двумстам столбцам, узнать длину данных в них, просуммировать, и только после этого мы попадём на 201-й столбец. И так нужно сделать для всей таблицы (или набора данных из неё). И для каждой следующей строчки операцию придётся выполнить заново. В результате операция ANALYZE занимала существенное время.

Чтобы ускорить операцию, мы поменяли алгоритм.

После того, как данные из набора данных таблицы загружены в память, мы один раз проходим по каждой строчке и по каждому столбцу строчки слева направо. Для каждой строки, на время выполнения команды ANALYZE, сохраняем указатели на её столбцы в отдельном массиве в оперативной памяти.

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

Благодаря таблице указателей можно быстро найти положение нужного столбца в памяти, не вычисляя длину предыдущих столбцов. Это позволяет алгоритму команде ANALYZE работать иногда в десятки раз быстрее.

Оптимизация типовых выражений LIKE для типа данных mchar

Эта оптимизация касается регулярных выражений для типа данных mchar. Регулярные выражения — это маски или определённые шаблоны, содержащие текстовые формулы, и искомый текст должен соответствовать этим формулам. Поиск по регулярным выражениям — достаточно сложная и ресурсоёмкая операция, потому что в общем случае это проверка строк с помощью формального языка.

Платформа 1С:Предприятие в ходе работы часто использует достаточно простую операцию текстового поиска внутри строки (s LIKE ‘%содержание%’), т.е. искомая строка должна содержать некоторый текст (подстроку). Эта операция значительно проще по вычислительным затратам, чем полноценный поиск на соответствие регулярному выражению.

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

Оптимизация функции fsync при использовании временных таблиц

Следующая оптимизация — это оптимизация использования функции fsync при использовании временных таблиц. fsync — это специальная операция ОС, которая позволяет принудительно записать данные из кэша файловой системы на диск. Когда мы программно пишем данные в файл — на самом деле сначала записываемые данные попадают в кэш файловой системы в ядре операционной системы, после чего в какой‑то момент операционная система данные из кэша записывает непосредственно на диск.

PostgreSQL устроен таким образом, что особо критичная информация, необходимая для восстановления базы данных после сбоя, которая записывается в журнал транзакций, принудительно сбрасывается при помощи операции fsync на диск так, чтобы в случае отказа питания или отказа сервера записанные транзакции не потерялись. Эта операция (запись на диск) требует некоторого времени, она может замедлить работу системы, она нагружает диск.

Наша оптимизация касается временных таблиц. Временные таблицы помимо того, что они живут только в одной сессии, они ещё и временные: они уничтожаются при завершении сессии, их данные никуда не записываются и нигде не сохраняются. То есть потеря данных временных таблиц не несёт никакой угрозы целостности базы данных.

Оптимизация заключается в том, что для тех транзакций, в которых не было записей в постоянные таблицы (т.е. были только записи во временные таблицы), не делается вызов fsync. Таким образом, мы экономим время на дисковых операциях и на вызовах процессора.

Оптимизация хранения информации о временных таблицах

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

Информация о таблицах в PostgreSQL хранится в системном каталоге PostgreSQL. Системный каталог — это также набор таблиц; есть таблица, содержащая информацию о таблицах в базе, есть таблица, содержащая информацию о столбцах таблиц, есть таблица для хранения информации о ключах таблиц, таблица, хранящая информацию об индексах и т. д. Все эти системные таблицы находятся в схеме pg_catalog.

В PostgreSQL вся информация о временных таблицах также сохраняется в системных таблицах.

Как вы уже наверное поняли, платформа 1С:Предприятие очень «любит» временные таблицы, активно создаёт и удаляет их в ходе работы. И каждое такое создание, и удаление временной таблицы сопровождается записью и изменением каталога системных таблиц. Системные же таблицы не временные, они общие для всех сессий, синхронизируется по всем правилам механизмами PostgreSQL. Поэтому операция создания и удаления таблицы (неважно, временной или нет) — достаточно сложная, «тяжёлая», и в определённые моменты нагрузка на сервер может стать существенной.

Наша оптимизация реализует «теневой» системный каталог, который размещён в оперативной памяти. Этот теневой системный каталог по структуре дублирует стандартный системный каталог. При этом если в системный каталог помещается запись, относящаяся к временной таблице, то эта запись перенаправляется из системного каталога в эту новую структуру — «теневой» системный каталог. А при чтении из системного каталога информация из этой новой структуры (хранящей информацию о временных таблицах) добавляется к результатам чтения.

Таким образом, мы сделали обходной путь вокруг системного каталога, позволяющий при создании и удалении временных таблиц обходиться без стандартного системного каталога, а работать исключительно в рамках памяти одной сессии. Это значительно разгрузило диск и буферы блокировки на операциях создания и удаления временных таблиц.

Это достаточно агрессивная оптимизация; она работает только при установленном у сервера СУБД специальном новом параметре enable_temp_memory_catalog (чтобы был простой способ её отключить и работать по стандартному алгоритму). Если при включённом параметре мы сделаем SELECT * из системного каталога, то мы не увидим там временных таблиц.

Благодаря этой оптимизации на некоторых операциях мы достигли ускорения в десятки, а иногда и в сотни раз.

Что мы оптимизировали в платформе 1С:Предприятие

Ряд оптимизаций для ускорения работы мы сделали и в платформе.

Размер пула временных таблиц

Несмотря на сделанные нами (и описанные выше) оптимизации, создание временной таблицы остается относительно ресурсоемкой операцией, особенно при большом количестве конкурирующих сеансов.

Работу с временными таблицами можно оптимизировать (помимо оптимизации исходного кода СУБД) и другими средствами.

Первая вещь, которую можно изменить — это размер пула временных таблиц.

Когда требуется временная таблица определённой структуры (выполняется команда CREATE TEMPORARY TABLE) — PostgreSQL создаёт временную таблицу, а после окончания её использования (после выполнения DROP TEMPORARY TABLE) таблица очищается и помещается в пул временных таблиц в памяти. Когда в следующий раз понадобится временная таблица с такой структурой — она будет извлечена из пула (если там есть подходящая таблица).

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

Мы провели ряд экспериментов и определили, что при нагрузках, характерных для крупных организаций, оптимальным будет размер пула около 2 000 временных таблиц на соединение.

На графиках мы видим, что после того, как мы увеличили размер пула, стало меньше операций, время выполнения которых равно «целевое время» * 4 или дольше. Зато выросло количество операций, которые уложились в целевое время. «Хвост» долгих операций (выделенный жёлтым) стал более плоским.

По оси X - интервал времени выполнения операции (в целевых временах), по оси Y - количество операций, уложившихся в интервал
По оси X - интервал времени выполнения операции (в целевых временах), по оси Y - количество операций, уложившихся в интервал

Закрытие соединений с БД

Одна из оптимизаций связана с операцией закрытия соединения с базой данных.

Ранее время жизни соединения с СУБД было фиксировано и составляло 30 минут. По истечении 30 минут соединение с СУБД закрывалось и при необходимости открывалось новое. При закрытии соединения PostgreSQL начинает очистку временных таблиц для этого соединения.

На практике это могло выглядеть так. На сервере стартует новый рабочий процесс 1С (исполняемый файл, обслуживающий клиентские приложения и взаимодействующий с сервером баз данных). В одном рабочем процессе может быть открыто много соединений с БД. Через 30 минут все соединения закрываются — и в этот момент мы видим и на графике производительности всплески, говорящие нам о замедлении выполнения ключевых операций. Сервер PostgreSQL занят — он чистит временные таблицы многих одновременно закрывающихся соединений с БД.

Чтобы избежать такой ситуации мы сделали время жизни соединения в случайном диапазоне от 30 до 60 минут. Результат на графике. Розовая линия — это время выполнения операций после этой оптимизации.

TRUNCATE пустых временных таблиц

В ходе теста мы обнаружили, что от 30% до 50% временных таблиц не содержит ни одной строки. При этом операция TRUNCATE для них всё равно выполняется при вызове её платформой. Несмотря на то, что платформа использует FAST TRUNCATE (который более оптимален, чем простой TRUNCATE), эта операция всё равно не бесплатная и хорошо видна при высоких нагрузках. А что, если не очищать таблицы, в которых нет ни одной строки?

Мы проверили это и обнаружили, что такая оптимизация неплохо улучшает производительность. Мы получили на наших тестах прирост производительности от 5% до 10%.

Как мы это сделали? Мы добавили ещё один запрос SELECT, выполняемый перед TRUNCATE. Если SELECT вернул ноль строк, то TRUNCATE не выполняется. Как показали замеры, влияние дополнительного запроса SELECT совершенно неощутимо на фоне выигрыша, который мы получили.

Было:

INSERT INTO pg_temp.tt10….
…",RowsAffected=0,Result=…
SELECT FASTTRUNCATE(‘pg_temp.tt10’);

Стало:

INSERT INTO pg_temp.tt10….
…",RowsAffected=0,Result=…
SELECT 1 FROM pg_temp.tt10 LIMIT 1;

Пакетная обработка запросов

Все вышеописанные изменения дали нам результат, очень близкий к целевому (напомним это Apdex = 0,85). Но чуть-чуть не хватало.

И тут мы вспомнили про задачу, которую делали вне связи с этим тестом – про пакетную обработку запросов.

Платформа исторически выполняет каждый запрос к СУБД по отдельными SQL командами, т.е. для каждой SQL команды платформа обращается к серверу СУБД.

Это привносит накладные расходы, при этом величина этих расходов очень сильно зависит от того, что за запрос выполняется. То есть если запрос выполняется минуту, то эти накладные расходы совершенно не ощутимы, они составляют доли процентов. Но если запрос маленький и быстрый, то round-trip до сервера СУБД и обратно занимает существенную долю времени –десятки процентов и даже больше (в зависимости от скорости сети, от того, где расположен сервер СУБД – на одном сервере с сервером 1С:Предприятия или на отдельном).

Чтобы решить эту проблему мы реализовали пакетирование некоторых запросов. PostgreSQL поддерживает пакетную обработку запросов, т.е. выполнение нескольких SQL команд (разделённых ';') за одно обращение к серверу СУБД. Использование пакетной обработки запросов позволяет сократить количество обращений платформы к серверу СУБД и, соответственно, суммарную сетевую задержку, а также общее время выполнения при обработке множества SQL команд, т.е. по итогу повысить производительность системы.

Платформа анализирует запросы к СУБД и те из них, которые могут быть выполнены в составе пакета, помещает в пакет.

Разумеется, не все запросы можно пакетировать. Но для пакетирования хорошо подходят, например, запросы, работающие с временными таблицами, когда результат запроса сам по себе нам не нужен, а нужен для использования следующим запросом. В реальных сценариях мы увидели цепочки до 100 запросов, помещённых в один пакет.

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

Итого

В совокупности все эти изменения со стороны платформы 1С:Предприятие и со стороны PostgreSQL позволили нам успешно пройти тест 1С:ERP на 30 000 одновременных конкурирующих пользователей в одной базе.

Тест успешно отработал 10 часов и показал производительность Apdex = 0.858, то есть цель теста была успешно достигнута.

Цифры:

  • 30 000 одновременно активных пользователей

  • Время работы теста: 10 часов

  • Количество выполненных операций: 885 000

Кластер серверов 1С: 5 серверов по 64 ядра CPU и 256 Гб RAM.

Структура базы (с т. з. бизнеса):

  • 54 филиала, выделенных на отдельные балансы

  • 1800 подразделений

  • 3500 складов

  • 80 000 номенклатурных позиций

  • 700 000 сотрудников

  • 90 000 партнеров

  • 2 500 000 контрагентов

  • 25 000 000 договоров

  • 1 000 000 основных средств

Наша статистика показывает, что 30 000 одновременно работающих пользователей соответствует организации с 250 000 сотрудников.

Тест показал, что платформа 1С:Предприятие готова для автоматизации крупнейших предприятий и организаций России.

Комментарии (52)


  1. lazy_val
    24.07.2025 09:02

    тест 1С:ERP на 30 000 одновременных конкурирующих пользователей

    Для эмуляции пользовательской нагрузки мы использовали 32 сервера-нагрузчика, на каждом стояло по шесть виртуальных машин, для теста запускалось по 160 пользовательских сеансов на сервер

    32 * 160 = 5'120

    Откуда 30'000? Или все-таки 160 пользовательских сеансов на виртуальную машину (а не на сервер)? Тогда получается 32 * 6 * 160 = 30'720


    1. PeterG Автор
      24.07.2025 09:02

      Вы правы, у нас опечатка.
      Поправили.
      Спасибо!


    1. PLAstic2
      24.07.2025 09:02

      В чём разница? Сервер 1С:Предприятия крутится на физическом или виртуальном сервере.


  1. usrsse2
    24.07.2025 09:02

    Вы изменили реализацию (заменили спинлок на барьер), но не обновили комментарий, там остался спинлок.


    1. PeterG Автор
      24.07.2025 09:02

      Так и задумано - чтобы читателю было видно, что раньше был спинлок.


  1. lazy_val
    24.07.2025 09:02

    кластер из шести серверов
    Спецификация серверов кластера:
    Intel(R) Xeon(R) Gold 6426Y 2500 MHz L1/L2/L3 1280/32768/38400 KB 16 cores/32 threads, ОЗУ 256 Гб, 1 SAS SSD 1920 ГБ Samsung PM1643a

    Впечатляет )) А для внедрений на 1000 пользователей, скажем, рекомендуемые параметры кластера можно где-то поглядеть в открытом доступе?

    Я к тому что SAP S/4HANA вполне себе бодренько крутится на кластере из двух серверов приложений c RAM по 64 гига каждый. И тысячу или около того пользователей успешно выдерживает.


    1. iNomad
      24.07.2025 09:02

      в HANA на сколько RAM для 1000пользователей?


      1. lazy_val
        24.07.2025 09:02

        Для кластера серверов приложений (они же ABAP Application Server) - как и указано выше:

        на кластере из двух серверов приложений c RAM по 64 гига каждый

        Требования к RAM для сервера БД зависят от ожидаемого количества данных во всех развертываемых модулях, поскольку вся БД in-memory. На практике для стандартного набора модулей (бухучет, управленческий учет, казначейство, закупки и склад, сбыт, производство, техобслуживание и ремонты) от 256 до 512 гигов, для особо тяжелых случаев 1 TB. Еще балансировщик нагрузки, но там копейки.


        1. iNomad
          24.07.2025 09:02

          т.е как и тут для 1С на PostgreSQL, так и для SAP S/4 на HANA будет достаточно сервера БД:
          ЦПУ: Ampere Altra Max M96-28, 2800 MHz 96 cores
          Память: 1 Тб Samsung DDR4 64 Гб 3200 MT/s
          Накопитель: SSD 7.7 Tb NVMe Samsung PM1733a


          1. lazy_val
            24.07.2025 09:02

            Хороший кстати вопрос - а зачем Postgres в этой конфигурации терабайт памяти? С HANA понятно, там вся БД в RAM должна помещаться. А Postgres почему столько требует?

            Может нам автор статьи пояснить сможет?


            1. DreamNik
              24.07.2025 09:02

              Память расходуется на shared_buffers, work_mem и файловый кэш ОС.
              Для запуска хватило бы и условных 16 ГБ, но при нагрузке диск захлебнётся. Такой сервак обычно ставят как раз для больших нагрузок - это десятки тысяч пользователей 1С, что после пулинга превращается в сотни активных сессий в PostgreSQL.


            1. jakobz
              24.07.2025 09:02

              Так и для postgres хорошо, когда вся БД в памяти, ну за вычетом всякого редко читаемого.


        1. krids
          24.07.2025 09:02

          Требования к RAM для сервера БД зависят от ожидаемого количества данных во всех развертываемых модулях, поскольку вся БД in-memory

          Начиная с S/4HANA 2020 можно использовать NSE (будет как классическая CУБД работать через буффер-кэш), если с памятью совсем напряг, но, естественно с некоторым оверхедом

           На практике для стандартного набора модулей (бухучет, управленческий учет, казначейство, закупки и склад, сбыт, производство, техобслуживание и ремонты) от 256 до 512 гигов, для особо тяжелых случаев 1 TB.

          Для S/4HANA на 1000 конкурентных юзеров 1TB RAM БД это скорее для особо легких случаев без тяжелого Z-кода (CDS, AMDP), что практически фантастика :)


          1. lazy_val
            24.07.2025 09:02

            для особо легких случаев без тяжелого Z-кода (CDS, AMDP), что практически фантастика :)

            Ну если разработки пилить на уровне SELECT * FROM ACDOCA то никаких терабайт не хватит ))


            1. krids
              24.07.2025 09:02

              А по факту оно примерно так не только лишь всегда через пару/тройку лет от go live :) Это в ECC 6.0 все было в абапе, а в S/4HANA теперь БД-монстры, выжирающие селектом по 500+GB оперативы. И в "стандарте" в том числе.


              1. lazy_val
                24.07.2025 09:02

                по 500+GB оперативы. И в "стандарте" в том числе

                До 2109 ни с чем таким не сталкивался. В части стандартной поставки, по крайней мере. Может, повезло

                В части разработок на проектах всегда был технический архитектор (часто не один) который такие фокусы отслеживал и отстреливал на взлете. Опять же может повезло, и не везде так

                через пару/тройку лет от go live :)

                Если заказчик после уходе интегратора нанимает студентов за доширак и сажает их пилить разработки, это не проблема инструмента, а проблема как его использовать. Сдуру все что угодно можно сломать.


                1. EvilBeaver
                  24.07.2025 09:02

                  Вы не обижайтесь, но в коде сап обычно так всегда. Код 1С-ников при всем своем разнообразии и близко не приближается к трешу, который выдают "интеграторы" и просят потом мульярд на железо, чтобы это говно крутить


  1. DikSoft
    24.07.2025 09:02

    Не могли бы Вы подробнее раскрыть, как с одной, пускай и "жирной" виртуалки эмулируется параллельная работа 160 пользователей?
    И есть ли разница в нагрузке с такого эмулятора с работой реальных 30K клиентов на разных IP адресах и с реальными скоростями CPU, а не с делёнными между собой тиками vCPU?
    Было бы интересно. Спасибо.


    1. PeterG Автор
      24.07.2025 09:02

      Запускаются тонкие клиенты, работают одновременно в одной терминальной сессии.

      Тесты по такой методике проводятся уже много лет на проектах ЦКТП (https://v8.1c.ru/tekhnologii/tekhnologii-krupnykh-vnedreniy/tsentry-korporativnoy-tekhnologicheskoy-podderzhki-tsktp/) и показывают хорошие результаты, принципиальной разницы с реальными клиентами нет.

      То, что ip адрес один и тот же - значения не имеет: с точки зрения сервера - клиенты разные, т.к. клиенты адресуются по паре ip-адрес + порт, а порты разные; с точки зрения клиента - на тестах упираемся не в производительность стека tcp/ip, и даже не в пропускную способность сети, а в первую очередь в память и cpu.


  1. NitroJunkie
    24.07.2025 09:02

    А что мешало просто сделать master-slave логическую репликацию, на мастер отправлять только транзакции записи, на slave все остальные (естественно проверяя что lsn слейва >= lsn последней транзакции данного пользователя)? Так сделано в lsFusion и так масштабируемость можно увеличить на порядок, а не заниматься экономией на спичках.

    Ну и не совсем понятно опять-таки зачем делать многие вещи на уровне PostgreSQL, а не на уровне платформы (тот же кэш временных таблиц, как опять-таки это сделано в lsFusion)? Разве что, если потом собираетесь это в основную ветку PostgreSQL залить, но что-то мне подсказывает, что вряд ли такие коммиты туда пройдут.


    1. PeterG Автор
      24.07.2025 09:02

      А что мешало просто сделать master-slave логическую репликацию

      Чтобы не усложнять настройку.

      зачем делать многие вещи на уровне PostgreSQL

      Чем "ниже" по цепочке клиент-сервер приложений-СУБД делаются оптимизации - тем оптимальнее (извините за тавтологию).
      Если можно сделать оптимизацию на финальном уровне трехзвенки - СУБД - лучше сделать её там, а не на уровне сервера приложений или тем более на уровне клиента. На мой взгляд, это очевидно.

      если потом собираетесь это в основную ветку PostgreSQL залить, но что-то мне подсказывает, что вряд ли такие коммиты туда пройдут.

      У нас своя версия PostgreSQL от 1С. Мы не планируем вливать наши правки в основную ветку PostgreSQL .


      1. NitroJunkie
        24.07.2025 09:02

        Чтобы не усложнять настройку.

        Настройку кем? Для администратора вся настройка master-slave кластера СУБД может делаться ровно двумя действиями: a) развернуть еще один instance postgres б) одной кнопкой (в Администрировании) или программно (одним http запросом, если нужно сделать кластер динамическим) добавить слейв с адресом базы postgres (скажем localhost:5425). Дальше всю работу может / должна взять на себя платформа: на master'е создается CREATE PUBLICATION FOR ALL TABLES, на slave'е (также как и на мастере) синхронизируется структура БД, создается CREATE SUBSCRIPTION, после чего после завершения реплик всех таблиц, slave добавляется в кластер (с проверкой lsn при переключении соединений как я писал выше). Грубоватое описание, но суть такая (при обновлении БД все немного сложнее, но это все прозрачно для администратора).

        Чем "ниже" по цепочке клиент-сервер приложений-СУБД делаются оптимизации - тем оптимальнее (извините за тавтологию).Если можно сделать оптимизацию на финальном уровне трехзвенки - СУБД - лучше сделать её там, а не на уровне сервера приложений или тем более на уровне клиента. На мой взгляд, это очевидно.

        Я почему спросил. Если бы была какая-то универсальная оптимизация PostgreSQL, которая существенно увеличивала бы производительность СУБД (а не экономила на спичках), она бы уже давно была бы в самом PostgreSQL. А если оптимизации platform-specific, то как правило требуют информацию от самой платформы и вы на взаимодействии "назад" потеряете больше. Плюс заставлять администратора устанавливать чужие сборки это мягко говоря неудобно (усложнение настройки как вы говорите) с точки зрения облаков, контейнеров, совместимости с другими инструментами / программами и т.п. (скажем помню недавно общался с кем-то, кто запускал lsFusion на вашей сборке и у него что-то, точно не помню, не заработало, в отличии от ванильной сборки PostgreSQL, и получается что вашу сборку нужно отдельно держать).


        1. ildarz
          24.07.2025 09:02

          Развертывание, настройка под хайлоад и администрирование кластера сложнее, тут даже обсуждать нечего. Другое дело, что в реальности 30К пользователей по-любому будут жить на чем-то более отказоустойчивом, чем один инстанс Postgres c одиночными SSD на каждый чих. Но это совсем отдельная история, и с точки зрения чисто демонстрации пиковой производительности не особо нужная. Хотя статью о том, у кого и на чем 30К клиентов 1С ЕРП крутятся в продакшене, я бы тоже почитал. :)


          1. NitroJunkie
            24.07.2025 09:02

            Ну базовую отказоустойчивость можно обеспечить как раз физической репликой на соседний сервак, с переключением на него при падении основного. А вот с масштабируемостью все сложнее и требует определенные вещи именно от платформы (обновление структур баз, переключение на лету соединений с контролем актуальности слейвов для этого соединения и т.п.)

            И да, синтетические тесты это прикольно, а вот на продакшне было бы куда интереснее посмотреть как можно организовать 30к одновременных пользователей без мастер-слэйв репликации.

            ЗЫ: Одновременными имеется ввиду полноценные бизнес-пользователи, которые за последние минуту имели хоть какую-то существенную активность.


    1. atshaman
      24.07.2025 09:02

      Тут как бы это сказать? Нюансы. Если делать синхронную репликацию - то она весь выигрыш съест, если асинхронную - то в случае, когда на основании результата выборки данных производится запись - возможны нехорошие варианты.

      Опять же, в оффлоад readonly-запросов на slave умеют не только лишь все...


      1. NitroJunkie
        24.07.2025 09:02

        Речь естественно об асинхронной.

        когда на основании результата выборки данных производится запись - возможны нехорошие варианты.

        Эта ситуация ничем не отличается от ситуации когда между результатом выборки данных и нажатием кнопки "сохранить" проходит какое-то время.

        Единственная разница это когда ты прочитал данные на мастере или более актуальном слейве, потом ушел на менее актуальный (но все еще допустимый слейв в соответствии с последним lsn для своего "требования актуальности") и там этих данных еще нет (получаются такие "мигающие" данные). Но опять-таки эта ситуация не сильно отличается от ситуации когда ты считал данные, а их параллельно удалили (изменили), а потом добавили назад. И с этим всем борятся при помощи уровней изоляции (то есть repeatable read / serializable, update conflict и вот это все).

        Опять же, в оффлоад readonly-запросов на slave умеют не только лишь все...

        Ну это да, но как раз платформы бизнес-приложений, где часто работает принцип "семь раз отмерь - один отрежь", должны по любому уметь это из коробки. Я поэтому и удивился, зачем такие танцы с бубнами, если можно просто master-slave настроить и не париться.


        1. atshaman
          24.07.2025 09:02

          Эта ситуация ничем не отличается от ситуации когда между результатом выборки данных и нажатием кнопки "сохранить" проходит какое-то время.

          не-не. В норме ты выборку + последующую запись в одну транзакцию упаковал и все у тебя норм - а с оффлоадом их две, стейт не консистентный и что там в итоге вышло - Ктулху Фтагн


          1. NitroJunkie
            24.07.2025 09:02

            Не совсем понял. Так транзакции записи естественно только на мастере делаются. Но это именно что "кнопка Сохранить". До нажатия этой кнопки вы же не будете упаковывать все в одну транзакцию (так как транзакции не кошерно держать открытыми на время любого внешнего взаимодействия, что с кластеризацией, что без, по сотне причин), а значит опять-таки что с кластеризацией, что без это нужно учитывать (и для этого есть уровни изоляции, update conflict'ы и все такое)


            1. atshaman
              24.07.2025 09:02

              Условно - увеличиваем количество позиций товара на 2 шт - делаем запрос на чтение того, что есть - RO запрос, идет на слейв, увеличиваем-записываем, ой - а слейв отстал! Что оказалось в базе?


              1. NitroJunkie
                24.07.2025 09:02

                Вы в одной транзакции это делаете? Если в одной то транзакция идет на мастер и все ок. Если не в одной, то между чтением и записью у вас может кто-то вклиниться и изменить количество и в итоге в базе будет абы что (поэтому в таких случаях все делают в одной транзакции с repeatable read и ловлей update conflict -> перестартом транзакции).


                1. atshaman
                  24.07.2025 09:02

                  "не-не. В норме ты выборку + последующую запись в одну транзакцию упаковал и все у тебя норм - а с оффлоадом их две, стейт не консистентный и что там в итоге вышло - Ктулху Фтагн"(C)

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

                  По личному опыту - вынос аналитики на слейв "да", разнесение обычных запросов на ro реплику - неа. Не стоит того.


    1. aru89
      24.07.2025 09:02

      Почему именно логическую?


      1. NitroJunkie
        24.07.2025 09:02

        Физическая временные таблицы не поддерживает. А без них в бизнес-приложениях со сложной логикой (и не только) очень тяжело.


  1. CrushBy
    24.07.2025 09:02

    Тестирование в попугаях это конечно же хорошо, но это сферический конь в вакууме. Важно же проверять разные типы нагрузки. Одно дело, когда создается 100 документов в минуту, а остальные 30К только читают. А другое - когда все 30К активно что-то пишут. А самые чудеса начнутся, когда пользователи будут править одно и то же и попадать в UPDATE CONFLICT. И вот тут самое интересное, как все себя поведет.

    30 000 одновременно активных пользователей

    Время работы теста: 10 часов

    Количество выполненных операций: 885 000

    А можете уточнить - это как ? Каждый "пользователь" выполнил всего 30 операций за 10 часов ? Это какие-то ленивые пользователи ? Или что подразумевается под операцией ?
    Сколько они документов провели ? Там у Вас 9 млн документов - это было до начала теста, или создалось за весь тест ?

    Далее требования были усреднены до средней крупной компании со смещением в сторону более крупных. Получилась база размером около 1 Терабайта.

    28 000 000 договоров
    22 000 000 движений себестоимости
    20 000 000 бух. проводок
    2 500 000 контрагентов
    1 000 000 основных средств
    700 000 сотрудников
    25 000 пользователей
    1 800 подразделений
    56 организаций (с единой головной организацией)

    Странные у Вас представления о "средней крупной компании". Для сравнения у нас в lsFusion ERP есть сейчас клиенты (с 2К одновременно работающих реальных, а не "виртуальных" пользователей) вот с таким количеством записей в таблицах (и там только документов приобретение товаров и услуг - где-то 15 млн):

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


    1. PeterG Автор
      24.07.2025 09:02

      Тестирование в попугаях это конечно же хорошо

      Мы тестируем не в попугаях, а в Apdex, Apdex – стандарт индустрии.

      Важно же проверять разные типы нагрузки.

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

      30 000 одновременно активных пользователей

      Время работы теста: 10 часов

      Количество выполненных операций: 885 000

       

      А можете уточнить - это как ? Каждый "пользователь" выполнил всего 30 операций за 10 часов ? Это какие-то ленивые пользователи ? Или что подразумевается под операцией ?

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

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

      Другие вводят много документов.

      Мы взяли текущие данные по количеству объектов (документов\справочников\...) в базе и посчитали, сколько их должно быть введено за 1 день.

      Примерно прикинули сколько отчетов\обработок запускают пользователи за день. Получились такие числа.

      Тест не жестко фиксированный, в нем можно менять интенсивность работы пользователей. Если хотите сделать более интенсивным - пожалуйста. Ссылка на базу теста доступна тем, кто купил 1С:ERP.

      Сколько они документов провели ? Там у Вас 9 млн документов - это было до начала теста, или создалось за весь тест ?

      9 млн документов – до начала теста.

      Проведено документов за тест 136 000

      Для сравнения у нас в lsFusion ERP есть сейчас клиенты (с 2К одновременно работающих реальных, а не "виртуальных" пользователей) вот с таким количеством записей в таблицах (и там только документов приобретение товаров и услуг - где-то 15 млн)

      Коллеги, мы очень рады, что у вашего продукта хорошая производительность на больших объемах.
      Приходите конкурировать!
      Будем улучшать наши продукты в ходе конкуренции.


      1. CrushBy
        24.07.2025 09:02

        Я к тому, что утверждение по 30К одновременных пользователей - это манипуляция. Да, я понимаю, что могут быть разные пользователи. Есть такие, кто будет к компьютеру подходить раз за день. В Вашем тесте получается, что пользователь в течение рабочего дня делает какое-то действие раз в 20 минут.

        Но в жизни обычно под пользователем подразумевают все-таки человека, у которого постоянная работа в программе так или иначе занимает большинство рабочего дня. И он делает значительно больше, чем 3 "операции" в час.

        Честнее было бы считать все-таки только таких пользователей, которые более менее постоянно работают в программе, и апроксимировать исходя из их статистики. И либо сократить количество пользователей, либо увеличить нагрузку. Иначе, это немного введение в заблуждение.


        1. atshaman
          24.07.2025 09:02

          Эммм... если что - то заказчик примерно так это и считает. Количество пользователей == количество активных УЗ в системе, затраты разносятся пропорционально, в результате в KPI СМ'а торчит аудит этих УЗ и "лишнего" там не то, чтобы много.

          Так, чтобы кто-то из заказчиков выполнял профилирование пользователей и\или опирался на это профилирование при составлении ТЗ - ну... околонаучная фантастика. Реально у тебя будет выкопировка из того же APDEX в разделе "требования к эрогономике и технической эстетике" ну и вот "количество одновременно работающих пользователей", шт. Причем что это за "пользователь" такой - зооказчег тебе не скажет, "хоть дерись".

          Я бы сказал - что проведенный тест вполне релевантен условиям задачи, как я их понимаю.


  1. alex7six
    24.07.2025 09:02

    Оптимизация "размер пула временных таблиц" относится к платформе 1С, а не к PG


  1. Mnemonic0
    24.07.2025 09:02

    А потом возникает процедура закрытия периода и оказывается, что за выполнения этой процедуры для этих миллионов/миллиардов документов требуется x10 ресурсов, но это же другая история. Правда?


  1. noknown
    24.07.2025 09:02

    1. Указанные оптимизации PostgreSQL были сделаны только для теста или это серийно поставляемый с 1С:ERP билд?

    2. Цитата: "В качестве опорной точки в Apdex выступает целевое (желаемое) время выполнения ключевых операций (это время назначается экспертным путём)" - можно ли уточнить: что ещё за "экспертный путь" такой и как это соотносится с реальными скоростями выполнения самых распространенных задач (проведение документов, создание отчётов)? Потому что, скажем, я понимаю, что количество запросов на одну пользовательскую задачу может быть достаточно большим, но вот конкретным пользователям "надо чтобы проводилось быстро", а не чтобы "среднее время ответа Postgres на запрос достигало целевых значений". Если этих запросов на проведение одного малюсенького документа - миллиард - то как будто бы все равно на взгляд обычного пользователя работать будет не быстро.

    3. Если спецификой работы 1С является создание большого количества временных таблиц (а именно эта особенность, как я понял, потребовала внесения части правок), то может имеет смысл рассмотреть возможность работы со временными таблицами в оперативной памяти сервера 1С? Без использования сетевого взаимодействия с сервером Postgres для этих временных таблиц.

    4. Планируется ли выкладывать где-то отдельно от 1С:ERP этот доработанный билд PostgreSQL? Как он лицензируется?


    1. DreamNik
      24.07.2025 09:02

      1 Все оптимизации включены в серийный дистрибутив PostgreSQL с патчем от 1С.

      3 Все временные таблицы участвуют в других SQL запросах, поэтому вынося их на уровень платформы придётся их каждый раз их передавать к СУБД, что лишь ухудшит ситуацию.

      4 Билд и исходный можно скачать всем у кого есть доступ к ИТС. Лицензируется по той же лицензии что и оригинальные исходные коды.


    1. PeterG Автор
      24.07.2025 09:02

      2.Цитата: "В качестве опорной точки в Apdex выступает целевое (желаемое) время выполнения ключевых операций (это время назначается экспертным путём)" - можно ли уточнить: что ещё за "экспертный путь" такой и как это соотносится с реальными скоростями выполнения самых распространенных задач (проведение документов, создание отчётов)?


      Могу ответить так
      Если у вас есть доступ к 1С.ИТС - то есть статья от 1С более подробная.


  1. mozg37
    24.07.2025 09:02

    Очень круто. Я ушел из 1с 8 лет назад, тогда я считал, что цифры, подобные вашим - фантастика. Но вот по номенклатуре и документам - мне кажется маловато для конторы с 30к юзеров.


  1. DonAlPAtino
    24.07.2025 09:02

    А объясните пожалуйста дураку как так получается. 1С c postgre работает уже лет 15. Лет 10 как нас гонят пинками на него. Все это время все понимают что половина затыков - из-за отсутствия в PG аналогов временных таблиц MS SQL. У 1С с деньгами проблем нет. Майнтейнеры PG активно зарабатывают в РФ на импортозамещении и продаже корп версий PG по цене Оракла.

    И НИ-ЧЕ-ГО.

    Максимум вот такие статьи из которых понятно что за 15 лет проблема никуда не делась, а решать ее предлагается ручной правкой кода PG. Ну или приглашением выскоополачиваемых спецов из тех же 1C и Postgres Professional , которые под вас PG пропатчать.

    В чем проблема то кроме жадности -то ?


    1. atshaman
      24.07.2025 09:02

      "Осталось апстрим уговорить", ага.

      Но если что - в последнюю пару-тройку лет тесты показывают, что на postgresql (Наконец-то!) 1с стала работать быстрее, чем на mssql. "В среднем", конечно же - по тому же apdex'у.


      1. DonAlPAtino
        24.07.2025 09:02

        Ну т.е. типа опять происки "злобного запада", "англичанка гадит", "пиндосы проклятые". 15 лет ведущие разработчики PG и 1С не могли протолкнуть в апстрим.. что? Я может пропустил, но я не видел никаких потуг даже. Так раз в пару месяцев статьи на тему "что мы тут в коде поправили чутька". Зато чуть реально не упал со стула когда мне Postgres Professional КП прислал на 16 ядер.

        в последнюю пару-тройку лет тесты показывают, что на postgresql (Наконец-то!) 1с стала работать быстрее, чем на mssql. "В среднем", конечно же - по тому же apdex'у.

        Но статьи на тему как мы переписали кучу запросов и упоролись в конфигах чтобы оно нормально заработало почему -то с habr и инфостарта не пропадают.


        1. NitroJunkie
          24.07.2025 09:02

          Но статьи на тему как мы переписали кучу запросов и упоролись в конфигах чтобы оно нормально заработало почему -то с habr и инфостарта не пропадают.

          Ну потому что ни postgresql, ни 1С почему-то так и не освоили такую базовую оптимизацию как predicate push down в обычные GROUP, не говоря уже о predicate push down в рекурсивные запросы, с window функциями, адаптивную материализацию подзапросов и т.п. (в отличии от MSSQL или lsFusion)

          https://habr.com/ru/companies/lsfusion/articles/468415/#opt

          https://habr.com/ru/companies/softpoint/articles/914956/

          И переложили все на разработчика. И если в типовых это можно решить руками (точнее деньгами), то что делать при доработках под конкретного заказчика - решительно непонянтно. Вот поэтому и столько статей про "мы переписали кучу запросов и упоролись в конфигах".


          1. alex7six
            24.07.2025 09:02

            В Tantor Postgres реализован JPPD и не только - https://habr.com/ru/companies/tantor/articles/924978/


        1. atshaman
          24.07.2025 09:02

          Ну т.е. типа опять происки "злобного запада", "англичанка гадит", "пиндосы проклятые". 

          Та ни, это вот - ваши персональные проЭкции. А тут - opensource, в котором НИКТО НИКОМУ НИЧЕГО НЕ ДОЛЖЕН(ТМ)

          Вот пошто в ванильке нет 64х битного счетчика транзаций, который нужен ВОТ ВООБЩЕ ВО ВСЕХ крупных инсталляциях? Набор патчей - есть. Реализация (На основе этого же патчсета, ага) во всех коммерческих дистрибутивах - есть. А в апстриме - не-а, нету. Тоже 1С должен, небось?

          А пошто 1с все еще свою сборку postgresql таскает, а не "влил в апстрим"? А апстриму - внезапно - на проблемы одноэсенных положить. У них свой "проджект вижн" и делать из слоника мысыкуель они - н-ннегодяи! не хотят. Даже вот за деньги и чужими руками.

          Вам надо - вы в своем приложении и. Вот 1С и. На удивление даже - небезуспешно.


          1. DonAlPAtino
            24.07.2025 09:02

            Хм.. вообще это сарказм был. Но без тега видимо уже никто не догадается. Вопрос этот наверное в первую очередь к сотрудникам 1С и PG типа Проф. Но им очевидно и так хорошо. Заодно можно и стомиллионную статью написать на тему как мы (В 1С или PGProf) в 100500 раз "оптимизировали" работу наших о..ных систем.


    1. alex7six
      24.07.2025 09:02

      из-за отсутствия в PG аналогов временных таблиц MS SQL

      В PG есть временные таблицы, платформа 1С работает также кроме нюанса с использованием механизма копий баз данных, когда на реплике нельзя создавать временные таблицы, но это решаемо.


  1. 1CUnlimited
    24.07.2025 09:02

    Я так понимаю изменен и код релиза платформы. Какой релиз платформы использовался? Я просто вижу что при записи в регистры analyze и fasttruncate по Временным! таблицам занимает большое время.

    И еще вопрос - сколько движений в регистры породили эти 885000 операций? Замечание Crash by про ленивых пользователей справедливо