Кеширование анонимных prepared statements
PgDoorman кеширует анонимные сообщения Parse в transaction pooling.
Многие драйверы отправляют короткие параметризованные запросы как
Parse с пустым именем. Без подмены PostgreSQL заново строит план на
каждом Bind, поэтому горячие OLTP-запросы каждый раз тратят CPU
планировщика.
PgDoorman прозрачно переписывает каждый анонимный Parse в
служебное имя DOORMAN_<N> на серверном соединении. План попадает в
реестр именованных prepared statements и переиспользуется между Bind
одного клиента и между разными клиентами одного пула. Главный эффект —
меньше CPU на планирование и меньше повторных Parse для одинаковых
форм запроса.
Подмена прозрачна для драйвера: клиент шлёт и получает пустые имена точно так же, как при работе с обычным PostgreSQL.
PgBouncer (1.21+) и Odyssey поддерживают prepared statements в
transaction mode, но только для именованных statements. Анонимный
Parse они передают без изменений. Отличие PgDoorman — подмена
анонимного Parse. Ограничения кеша, LRU, TTL и метрики нужны, чтобы
эта подмена не превращалась в рост памяти на динамическом SQL.
Что ускоряется
Кеш анонимного Parse убирает повторную работу на горячих
параметризованных запросах:
- бэкенд PostgreSQL не получает одинаковый
Parseпри каждом повторном обращении к уже известной форме запроса; - PostgreSQL может использовать prepared statement, который уже создан на этом серверном соединении;
- разные клиенты одного пула используют одну пуловую запись, а не прогревают одинаковый запрос независимо друг от друга;
- при попадании в кеш бэкенда PgDoorman синтезирует
ParseCompleteбез обращения к PostgreSQL.
Поэтому это в первую очередь оптимизация производительности для OLTP-нагрузок с повторяющимися формами запросов. Ограничение размеров кеша, TTL и интернер нужны затем, чтобы выигрыш не превращался в бесконтрольный рост памяти на пулере или бэкенде.
Базовая модель PostgreSQL
В сообщении Parse имя prepared statement задаёт его тип: пустое
имя соответствует анонимному statement, любое непустое —
именованному:
Время жизни в PG Кеширование плана
───────────────────── ───────────────── ─────────────────────
Анонимный (name="") До следующего Нет именованной
анонимного Parse записи; каждый новый
или конца сессии анонимный Parse
строит план заново
Именованный До Close / Сначала custom-планы;
(name="stmt_42") DEALLOCATE / после 5 custom-
конца сессии выполнений возможен
generic-план
Большинство современных драйверов по умолчанию используют
анонимные prepared для разовых параметризованных запросов:
lib/pq (Go), libpq PQexecParams (C), часть режимов в pgjdbc и
psycopg. Прикладной код выглядит как обычный параметризованный
запрос, но в wire protocol уходит пустое имя.
Почему это проблема в transaction pooling
В transaction pooling одно серверное соединение по очереди обслуживает разных
клиентов. Если пулер отправляет пустой Parse как есть, каждый
Bind клиента приходит на соединение, у которого плана для этого
запроса нет. Горячие OLTP-пути платят CPU планировщика на каждом
обращении.
Именованные prepared решают проблему производительности планирования, но перекладывают учёт на пулер:
- Пулер должен помнить именованные statements каждого клиента до его отключения, даже если общий кеш пула уже выселил запись.
- На каждом
Bindпулер должен проверить, знает ли текущее серверное соединение это имя, и при необходимости заново сделатьParse. - При отключении клиента пулер должен отправить
CloseилиDEALLOCATEна правильное серверное соединение. - Драйверы, которые генерируют отдельное имя
stmt_<seq>под каждый уникальный запрос, раздувают клиентский кеш: сотни записей на клиента, при тысячах подключений превращающиеся в миллионы записей в памяти.
Остаются два варианта: отказаться от кеширования плана для анонимных запросов или взять на себя полный учёт именованных. PgDoorman выбирает третий путь.
Что делает PgDoorman
На каждый анонимный Parse от клиента PgDoorman:
- Считает хеш по тексту запроса, OID типов параметров и короткому
отпечатку параметров планировщика, которые клиент закрепил при
подключении (
search_path,default_transaction_isolation,default_transaction_read_only,default_text_search_config,role). Поэтому два клиента с одинаковым запросом и OID параметров, но с разными значениямиsearch_pathвStartupMessage, получат разные записи кэша и разные планы PostgreSQL. Другие параметры, которые тоже могут влиять на план (TimeZone,DateStyle,plan_cache_mode,enable_*, настройки стоимости JIT), в этот отпечаток не входят. Перед тем как использовать один и тот же prepared-запрос с разными значениями таких параметров, посмотрите ограничения в справке поsync_server_parameters. - Ищет хеш в общем кеше пула. При промахе выделяет новое имя
DOORMAN_<counter>и регистрирует записьArc<Parse>. - Записывает в клиентский кеш ключ
Anonymous(hash), чтобы следующийBindнашёл тот жеDOORMAN_<N>. - Отправляет
Parseна бэкенд с переписанным именем. - На соответствующем
Bind(с пустым именем) переписывает имя statement вDOORMAN_<N>и проверяет, что текущий бэкенд уже держит запись; если нет, отправляетParseповторно.
Клиент никогда не видит DOORMAN_<N>: имя живёт только на участке
между PgDoorman и бэкендом. Когда нужный бэкенд уже держит запись,
PgDoorman синтезирует ParseComplete сам и не делает round-trip.
Пример wire-protocol
Go-приложение, выполняющее
db.Query("SELECT * FROM t WHERE name = $1", "vasya")
через lib/pq, отправляет такой обмен:
Клиент PgDoorman Backend
────── ───────── ───────
Parse("", q) ───►│ hash, miss → DOORMAN_42
│ pool_cache[hash] = Arc<Parse>
│ client_cache[Anon(hash)] = ...
│ Parse("DOORMAN_42") ─────►
│ ◄── ParseComplete
◄────│ ParseComplete
Bind("", "vasya") ───►│ rewrite "" → "DOORMAN_42"
│ Bind("DOORMAN_42") ──────►
│ Execute, Sync ───────────►
│ ◄── BindComplete, ...
│ ◄── ReadyForQuery
◄────│ BindComplete, ...
Второй клиент с тем же запросом в том же пуле попадает в кеш пула
и не отправляет Parse на бэкенд:
Клиент B PgDoorman Backend (тот же)
──────── ───────── ────────────────
Parse("", q) ───►│ hash hit → DOORMAN_42
│ server_cache содержит "DOORMAN_42"
◄────│ синтетический ParseComplete (на бэкенд ничего)
Bind("", v) ───►│ rewrite "" → "DOORMAN_42"
│ Bind("DOORMAN_42") ────►
│ ...
Слои кеша
PgDoorman держит состояние prepared statements на трёх уровнях:
Pool-level DashMap<hash, CacheEntry>
Один на пул. Хранит Arc<Parse> с именем DOORMAN_N.
Размер: prepared_statements_cache_size (по умолчанию 8192).
Выселение: приближённый LRU.
Client-level Named: AHashMap<String, CachedStatement>, без лимита.
Anonymous: LruCache<u64, CachedStatement> ограничен
client_anonymous_prepared_cache_size (если не задан,
наследует prepared_statements_cache_size),
или AHashMap при размере 0.
Выселение Anonymous локальное: Arc<Parse> отбрасывается,
DOORMAN_<N> на бэкенде остаётся.
Server-level LruCache<String, ()>, на серверное соединение.
Запоминает, какие DOORMAN_N этот бэкенд уже держит.
Точный LRU; при выселении отправляет Close на бэкенд.
При вытеснении записи из Anonymous LRU PgDoorman отбрасывает локальную
ссылку и не отправляет Close на бэкенд. Соответствующий
DOORMAN_<N> будет переиспользован server-level LRU или закроется по
server_lifetime (по умолчанию 20 минут) — что наступит раньше.
Текст запроса интернируется через Arc<str>: десять клиентов с
одним и тем же анонимным запросом делят одну аллокацию в памяти.
Когда подмена помогает
- API-нагрузки с малым набором горячих запросов. Десяток
уникальных форм
SELECT/INSERTна тысячи клиентов. Доля попаданий в кеш пула близка к 100 %, планировщик работает один раз на серверное соединение для каждой формы запроса, а следующие обращения идут черезBindк уже подготовленному statement. - Драйверы, привязанные к анонимным prepared.
lib/pq,libpqPQexecParams, pgjdbc до достиженияprepareThreshold. Без подмены они каждый раз перепланируют. - Смешанные пулы, где именованные и анонимные statements соседствуют. Анонимные получают тот же выигрыш от кеша планов, что и именованные, без роста клиентского кеша именованных statements.
Когда подмена не помогает
- Разовый / OLAP-трафик. Каждый запрос уникален. Когда кеш пула
заполнен, каждая новая форма запроса ищет старую запись для
вытеснения через O(N)-обход. Если инстанс обслуживает только такую
нагрузку, отключите remap через
prepared_statements: false. - Скрипты с одним statement. Цепочка connect →
Parse→ 1Bind→ disconnect не накапливает достаточно попаданий, чтобы окупить учёт. Накладные расходы наParse~700 нс — небольшие, но измеримые. - Асинхронные драйверы в режиме pipeline. Каждая сессия получает
уникальное имя
DOORMAN_async_<N>, чтобы избежать коллизий между одновременными незавершёнными операциями. Серверный кеш между сессиями не переиспользуется. Кеш пула по-прежнему делит текст запроса между сессиями; планировщик на бэкенде срабатывает один раз на сессию.
Эффективность этого ускорения измеряйте по
rate(pg_doorman_servers_prepared_hits_total[5m]) и
rate(pg_doorman_servers_prepared_misses_total[5m]). Устойчивая доля
промахов выше 30 % означает, что подмена расходует CPU и память, но
почти не даёт переиспользования планов. Тогда либо отключите её, либо
увеличьте prepared_statements_cache_size.
Как это устроено у других пулеров
| Пулер | Кеш Parse/плана для анонимного prepared statement |
|---|---|
| PgDoorman | Да: прозрачная подмена на DOORMAN_<N> |
| PgBouncer 1.21+ | Нет: только named, анонимный проходит as-is |
| Odyssey | Нет: только named, pool_reserve_prepared_statement |
| PgCat | Нет: только named |
В PgBouncer поддержка prepared statements появилась в 1.21, но
ограничена именованными: анонимный Parse проходит без
изменений, и каждый Bind запускает планировщик. Флаг
pool_reserve_prepared_statement в Odyssey требует именованных
statement; на анонимный трафик он не влияет. PgCat ведёт себя
так же.
Кешировать план анонимных prepared сегодня умеет только PgDoorman.
Конфигурация
| Параметр | По умолчанию | Эффект |
|---|---|---|
prepared_statements | true | Включает подмену и кеширование prepared statements. false отключает функцию. |
prepared_statements_cache_size | 8192 | Размер кеша пула в записях. Должен быть больше 0, пока prepared_statements = true. |
server_prepared_statements_cache_size | наследует prepared_statements_cache_size | Размер LRU на серверное соединение для имён DOORMAN_<N>. 0 отключает хранение на бэкенде, но не подмену на уровне пула. |
client_anonymous_prepared_cache_size | наследует prepared_statements_cache_size | Размер Anonymous LRU на клиента. 0 снимает ограничение. Named-часть всегда без лимита. |
Named-часть клиентского кеша всегда без лимита и не зависит от
client_anonymous_prepared_cache_size.
Полностью отключить подмену prepared statements (редко, для установок только с OLAP):
general:
prepared_statements: false
Отдельного переключателя только для анонимных statements нет. Не
используйте prepared_statements_cache_size: 0 как выключатель:
pg_doorman отклоняет такое общее значение, пока prepared_statements
включён.
Отличия от семантики PostgreSQL
Подмена меняет несколько протокольных деталей, на которые могут полагаться строгие приложения:
- Один и тот же анонимный
Parse, отправленный дважды, не стирает предыдущий. Каждая пара(query, param_types)живёт независимо в кеше пула под своимDOORMAN_<N>. Closeс пустым именем ничего не делает с кешами PgDoorman. СоответствующийDOORMAN_<N>живёт до выселения из LRU кеша пула или до закрытия пула.- Решение PostgreSQL между custom- и generic-планом становится общим
для всех клиентов, использующих один
DOORMAN_<N>. Именованный statement сначала получает custom-планы; после пяти custom-выполнений PostgreSQL может перейти на generic-план, если его оценочная стоимость достаточно близка. С PgDoorman эти выполнения могут прийти от разных клиентов, поэтому выбор generic-плана может отражать смешанное распределение параметров.
Приложения, которые опираются на PG-семантику "анонимный Parse
стирает предыдущий", должны переключиться на именованные statement
с явным Close.
Тюнинг
Размер кеша
Кеш prepared statements в PgDoorman состоит из трёх слоёв. Ими управляют три связанных параметра:
prepared_statements_cache_size(по умолчанию8192) задаёт размер общего кеша пула — одна хеш-таблица на пул, ключом служит хеш запроса. Это верхняя граница на число различных форм запроса, которые пул помнит сразу для всех клиентов. Приближённый LRU: вытеснение проходит за O(N) по всему кешу и не отправляетCloseна бэкенд (другие клиенты могут ещё держатьArc).server_prepared_statements_cache_size(по умолчанию наследуетprepared_statements_cache_size) задаёт размер серверного кеша — отдельный LRU на каждое серверное соединение, ключом служит имяDOORMAN_<N>. Это верхняя граница на число prepared statements, которое PgDoorman позволит держать одному бэкенду PostgreSQL. Точный LRU за O(1); при выселении в очередь бэкенда кладётсяClose, который отправляется ближайшим Sync или Flush — представлениеpg_prepared_statementsможет временно показывать больше строк, чем потолок, пока не придёт следующий Sync.client_anonymous_prepared_cache_size(по умолчанию наследуетprepared_statements_cache_size) задаёт размер Anonymous LRU на клиента.0отключает LRU и снимает потолок: кеш растёт без ограничения. Любое положительное число задаёт лимит независимо от размера кеша пула.
Пуловый и серверный лимиты можно переопределить на уровне пула:
general:
prepared_statements_cache_size: 8192
server_prepared_statements_cache_size: 1024 # потолок на бэкенд жёстче
pools:
oltp:
# наследует оба значения из general
pool_mode: "transaction"
reporting:
# у этого пула шире разнообразие запросов; пусть серверный кеш
# вмещает больше
server_prepared_statements_cache_size: 4096
pool_mode: "transaction"
prepared_statements: false отключает подмену целиком и заодно
обнуляет кеши на уровне пула и серверного соединения. Указать
server_prepared_statements_cache_size: 0 при положительном
размере кеша пула допустимо, но смысла мало: серверный кеш превратится
в pass-through, и каждое попадание на другой бэкенд приведёт к
повторному Parse.
Когда уменьшать server_prepared_statements_cache_size ниже размера
кеша пула:
- На бэкендах копится слишком много строк
DOORMAN_<N>(pg_prepared_statementsупирается в потолок, память планов растёт). - Хочется ускорить вытеснение через
Close, не урезая попадания в кеш пула.
Когда оставить значения равными (поведение по умолчанию):
- Нет измеренной проблемы с памятью на бэкендах. Достаточно наследования.
Размер client_anonymous_prepared_cache_size
Если параметр не задан, Anonymous LRU на клиента наследует
вычисленный prepared_statements_cache_size пула (по умолчанию 8192).
Явное значение перекрывает наследование: 0 отключает LRU, и кеш
растёт без ограничения; любое положительное число задаёт потолок
LRU.
Каждая запись хранит лёгкую структуру
(hash, async_name?, Arc<Parse>) — сам Arc<Parse> делится с
кешем пула, поэтому накладные расходы на клиента ≈ 80 байт
на запись. На 10 000 подключённых клиентов × 256 записей × ~80 байт
получаем около 200 МБ предсказуемого потолка на пулере.
Поднимайте лимит, когда:
- ORM или генератор SQL выдаёт
stmt_<seq>под каждый запрос и Anonymous LRU постоянно вытесняет записи (видно по устойчиво ненулевой скоростиpg_doorman_clients_prepared_anonymous_evictions_total). - Приложение заведомо имеет широкое рабочее множество в одной сессии и скорость вытеснений соответствует этой нагрузке.
Уменьшайте лимит при очень больших числах подключений (50 000+
клиентов): на таком масштабе clients × cache_size × 80 байт учётной
памяти на пулере может пересечь 1 ГБ, и урезание лимита уполовинивает
её. max_memory_usage не ограничивает служебную память кеша prepared
statements; этот параметр защищает буферы запросов в полёте.
Named всегда без лимита
Named-часть клиентского кеша не ограничена. PgDoorman держит
Arc<Parse> для каждого именованного statement, который создал
клиент, до его отключения или явного DEALLOCATE /
DEALLOCATE ALL. Это согласуется с собственным контрактом
PostgreSQL — именованные prepared живут до конца сессии — и
исключает сценарий, при котором вытеснение Named-записи под нагрузкой
ломает следующий Bind ошибкой prepared statement does not exist.
Обратная сторона: драйверы, генерирующие отдельное имя под каждый
запрос (часть режимов pgjdbc и Hibernate, отдельные конфигурации
.NET Npgsql), могут раздуть Named-часть без потолка. PgDoorman не
может ограничить её безопасно; ответственность за переиспользование
имён или явный DEALLOCATE лежит на приложении.
Сигнал давления есть только для Anonymous LRU — счётчик вытеснений
pg_doorman_clients_prepared_anonymous_evictions_total. Для Named
такого сигнала нет: следите за колонкой client_named_count в
SHOW POOLS_MEMORY и метрикой pg_doorman_clients_prepared_named_entries
на предмет неожиданного роста.
Окно роста памяти на бэкенде
При вытеснении записи из Anonymous LRU на стороне клиента PgDoorman
отбрасывает только локальный Arc<Parse>. Соответствующий
DOORMAN_<N> остаётся живым на каждом бэкенде, который когда-либо
его обслуживал. Очищают его два механизма:
- Server-level LRU. На каждом бэкенде ведётся свой
LruCache<String, ()>имёнDOORMAN_<N>, ограниченныйserver_prepared_statements_cache_size(илиprepared_statements_cache_size, если отдельный серверный лимит не задан). При достижении лимита бэкенд отправляетCloseна наименее давно использованное имя и освобождает план. - Ротация бэкенда. Бэкенд достигает
server_lifetime(по умолчанию 20 мин), и pg_doorman закрывает его; новый бэкенд стартует с пустым кешем планов.
Худший случай по памяти на одном бэкенде — это
server_prepared_statements_cache_size × средний размер плана
(8192 × ~100 КБ — около 800 МБ) на стороне PostgreSQL. Чтобы сжать
окно:
- Снизьте
server_prepared_statements_cache_size, чтобы серверный LRU быстрее вытесняла планы. - Снизьте
server_lifetime, чтобы бэкенды ротировались чаще.
Системное представление pg_prepared_statements в PostgreSQL
показывает имена, которые держит текущий бэкенд. Подсчёт строк там
показывает, насколько близко бэкенд подошёл к лимиту.
Мониторинг
Команды администратора:
-
SHOW PREPARED_STATEMENTS— pool, hash, name, query,count_used,kind. Топ записей поcount_usedпоказывает горячие запросы, на которых кеш окупается. Колонкаkind— последняя в наборе и принимает значенияnamed,anonymousилиmixedв зависимости от того, как клиенты использовали запись за её жизнь.Пример:
pool | hash | name | query | count_used | kind --------------+--------------------+-------------+-------------------+------------+----------- sharded.user | 1234567890123456 | DOORMAN_1 | SELECT * FROM t1 | 150234 | anonymous sharded.user | 2345678901234567 | DOORMAN_2 | INSERT INTO t2 .. | 87654 | named sharded.user | 3456789012345678 | DOORMAN_3 | SELECT * FROM t3 | 45678 | mixed -
SHOW POOLS_MEMORY—pool_prepared_count,client_prepared_count,pool_prepared_bytes,client_prepared_bytesплюс разбивка по kind:client_named_count,client_anonymous_count,client_anonymous_evictions_alive. Последняя колонка суммирует счётчики вытеснений только по подключённым сейчас клиентам — отключившиеся клиенты выпадают из суммы, поэтому колонка не монотонна. Для накопительного счётчика читайте Prometheus-метрикуpg_doorman_clients_prepared_anonymous_evictions_total.
Prometheus-метрики (полный список в Prometheus):
pg_doorman_pool_prepared_cache_entries{user, database}pg_doorman_pool_prepared_cache_bytespg_doorman_clients_prepared_cache_entriespg_doorman_clients_prepared_cache_bytespg_doorman_clients_prepared_named_entries{user, database}pg_doorman_clients_prepared_anonymous_entries{user, database}pg_doorman_clients_prepared_anonymous_evictions_total{user, database}pg_doorman_servers_prepared_hits{user, database}pg_doorman_servers_prepared_misses{user, database}pg_doorman_servers_prepared_hits_total{user, database}pg_doorman_servers_prepared_misses_total{user, database}pg_doorman_async_clients_count
Для rate() и алертов используйте метрики с суффиксом _total.
Метрики серверного кеша без _total — текущая сумма по живым
бэкендам; она может уменьшаться при ротации бэкендов.
Алертинг
Скорость вытеснений в Anonymous LRU
Устойчиво ненулевая скорость на счётчике вытеснений означает, что LRU вытесняет записи быстрее, чем приложение переиспользует их. Шаблон алерта:
rate(pg_doorman_clients_prepared_anonymous_evictions_total[5m]) > 10
for 10m
Порог в 10 вытеснений/с на пул — отправная точка, реальное значение
зависит от формы трафика и числа подключений. Срабатывание алерта
читайте как "лимит слишком мал или рабочее множество приложения
шире, чем ожидалось"; решение — либо поднять
client_anonymous_prepared_cache_size, либо разобраться, не
генерирует ли приложение уникальные запросы на горячем пути.
Интерпретация kind = mixed
Каждая запись кеша пула помнит, использовали ли её клиенты как
именованный statement, как анонимный, или обоими способами. kind = mixed
означает, что одна и та же пара (query, param_types) была
обработана хотя бы одним клиентом как named и хотя бы одним другим
как anonymous за её текущую жизнь. У большинства нагрузок строк
mixed нет; если в пуле их большинство, у клиентов разные драйверы
или разные конфигурации драйверов против одной БД, и эту разнородность
стоит проверить — иногда она задумана, иногда сигналит, что один из
клиентов настроен неверно.
Число prepared statements на бэкенде
PostgreSQL отдаёт pg_prepared_statements для текущего бэкенда.
Если память пулера в норме, но RSS бэкендов PostgreSQL растёт,
посчитайте строки на каждом бэкенде:
SELECT count(*) FROM pg_prepared_statements;
Цифры около server_prepared_statements_cache_size означают, что
серверный LRU работает на потолке, а ротация — второй способ
освободить память планов. Если серверный лимит наследует
prepared_statements_cache_size, используйте это значение как потолок.
Снижение серверного лимита или server_lifetime уменьшает давление на
память планов ценой более частых повторных Parse на бэкенде.
Ограниченный query interner
Глобальный интернер, в котором дедуплицируются тексты Parse, разделён на две независимые хеш-таблицы.
- NAMED — тексты именованных prepared. Запись жива, пока
пуловой или клиентский кеш держит ссылку через
Arc<str>. Сборщик мусора убирает запись, когда её перестали удерживать снаружи интернера; двухтактовая отсрочка защищает запись, на которую сослались между двумя циклами обхода. - ANON — тексты анонимных prepared. Запись истекает по
query_interner_anon_idle_ttl_secondsбездействия (60 секунд по умолчанию).0отключает TTL и возвращает поведение pg_doorman до 3.7 — оставлено для существующих установок.
Если anonymous Bind или Describe приходит после того, как
pg_doorman потерял соответствующее состояние anonymous prepared,
pg_doorman отвечает ERROR: unnamed prepared statement does not exist
(SQLSTATE 26000). Типичные причины: вытеснение из клиентского
Anonymous LRU, RESET INTERNER, TTL-вытеснение из interner или паттерн
драйвера, который переиспользует unnamed prepared statements между
пачками. Это та же ошибка, что PostgreSQL отдаёт напрямую в аналогичной
ситуации; стандартные драйверы реагируют повторным Parse.
Бинарное обновление (SIGUSR2) переносит и NAMED, и ANON в новый
процесс. Анонимные записи попадают в новый ANON-интернер со
свежим last_used, и TTL отсчитывается заново с момента
обновления.
Команды для оператора
SHOW INTERNER (через admin-сессию) выводит количество и объём
текста для каждой половины:
kind | entries | bytes
named | 420 | 87654
anonymous | 1337 | 234567
SHOW INTERNER N показывает N самых тяжёлых записей: hash,
kind, bytes, idle_ms (-1 для named — там нет
последнего-использования, вместо него отслеживается состояние
сборщика) и 120 первых символов текста запроса.
RESET INTERNER чистит обе половины. Активные клиенты заново
сделают Parse при следующем использовании. Команда
диагностическая.
Метрики Prometheus отражают SHOW INTERNER плюс гистограмму
длительности обхода и счётчик синтетических 26000. Увеличивайте
query_interner_anon_idle_ttl_seconds только когда synthetic misses
совпадают с TTL-вытеснениями anonymous-записей или с подтверждённым
паттерном cross-batch unnamed statements. Если промахи идут вместе с
pg_doorman_clients_prepared_anonymous_evictions_total, увеличивайте
client_anonymous_prepared_cache_size.
Справочник
- Режимы пула — transaction mode, где работает подмена prepared statements.
- Общие настройки —
prepared_statements_cache_size,server_prepared_statements_cache_size,client_anonymous_prepared_cache_size,query_interner_gc_interval_seconds,query_interner_anon_idle_ttl_seconds. - Команды администратора —
SHOW PREPARED_STATEMENTS,SHOW POOLS_MEMORY,SHOW INTERNER,RESET INTERNER. - Prometheus — полный список метрик.