CREATE CONTINUOUS QUERY query_md5_10min ON telegraf_pg_demo
BEGINSELECTsum("duration") AS duration_sum,
sum(calls) AS calls_sum
INTO
telegraf_pg_demo.autogen.pg_stat_statements_query_md5_10min
FROM
telegraf_pg_demo."7d".pg_stat_statements_diff
WHEREtime>= now() -20m AND calls >0GROUPBY host, usename, datname, queryid, query_md5, time(10m)
END;
За счет Continious Query Influxdb cтатистика отдельно
# Отдельно поднять сервер баз данных
docker-compose up db
# Остановить поднятый сервер БД# <Ctrl+C> или командой
docker stop sql_monitor_postgres
# А теперь поднять все компоненты
docker-compose up
# Открыть Grafana
open 'http://localhost:3000'
Для чего и как выполнять трассировку SQL-запросов
Partial-индексы и вот это вот все
select
concat('{"m":"', message, '"}') body
from web_message
where
user_id ='0eaab87d-5977-4a3a-af9f-6f5f424140f5'and status ='NEW'and category in ('NOTIFICATION')
and channel ='WEB_POPUP'and ( expiration_time isnullor expiration_time >= now())
orderby created_at desc
limit 10;
Запрос в статистике
select
concat($1, message, $2) body
from ntn_web.web_message msg
where
user_id = $3and status = $4and category in ($5)
and channel = $6and ( expiration_time isnullor expiration_time >= $7)
orderby created_at desc
limit $8;
Первое предположение: добавить в индекс все
select
concat($1, message, $2) body
from ntn_web.web_message msg
where
user_id = $3-- в индексand status = $4-- в индексand category in ($5) -- в индексand channel = $6-- в индексand ( expiration_time isnullor expiration_time >= $7) -- в индексorderby created_at desc-- в индекс
limit $8;
Большой индекс, который не будет использован
select concat($1, message, $2) body from web_message
where user_id = $3and status = $4and category in ($5) and channel = $6and ( expiration_time isnullor expiration_time >= $7 )
orderby created_at desc limit $8;
------ В индекс включены все поляcreate index fix_web_message_idx_1 on web_message
using btree(user_id, status, category, channel,
expiration_time, created_at DESC);
Меньший индекс, который будет использован
select concat($1, message, $2) body from web_message
where user_id = $3and status = $4and category in ($5) and channel = $6and ( expiration_time isnullor expiration_time >= $7 )
orderby created_at desc limit $8;
------ Из индекса исключены неподходящие строкиcreate index fix_web_message_idx_2 on web_message
using btree(user_id, expiration_time,
created_at DESC)
where status ='NEW'and category in ('NOTIFICATION')
and channel ='WEB_POPUP';
Для заданных параметров фильтрации годится
select concat($1, message, $2) body from web_message
where user_id = $3and status ='NEW'and category in ('NOTIFICATION') and channel ='WEB_POPUP'and ( expiration_time isnullor expiration_time >= $7 )
orderby created_at desc limit $8;
------ Из индекса исключены неподходящие строкиcreate index fix_web_message_idx_2 on web_message
using btree(user_id, expiration_time,
created_at DESC)
where status ='NEW'and category in ('NOTIFICATION')
and channel ='WEB_POPUP';
Немного перепишем запрос
select concat($1, message, $2) body from web_message
where user_id = $3and status ='NEW'and category in ('NOTIFICATION') and channel ='WEB_POPUP'--and ( expiration_time is null or expiration_time >= $7 )and coalesce( expiration_time, '3000-01-01' ) >= now()
orderby created_at desc limit $8;
-- TODO: успеть актуализировать запрос до 3000-го года
Немного перепишем запрос
select concat($1, message, $2) body from web_message
where user_id = $3and status ='NEW'and category in ('NOTIFICATION') and channel ='WEB_POPUP'and coalesce( expiration_time, '3000-01-01' ) >= now()
orderby created_at desc limit $8;
-- TODO: успеть актуализировать запрос до 3000-го года
Код будет работать до 3000-го года корректно
А потом мы что-нибудь придумаем
Функцию coalesce тоже можно индексировать
select concat($1, message, $2) body from web_message
where user_id = $3and status ='NEW'and category in ('NOTIFICATION') and channel ='WEB_POPUP'and coalesce( expiration_time, '3000-01-01' ) >= now()
orderby created_at desc limit $8;
------ В индекс добавлен результат выполнения coalescecreate index fix_web_message_idx_3 on web_message
using btree(user_id, coalesce(expiration_time,'3000-01-01'),
created_at DESC)
where status ='NEW'and category in ('NOTIFICATION')
and channel ='WEB_POPUP';
Сделаем из индекса аналог View
select concat($1, message, $2) body from web_message
where user_id = $3and status ='NEW'and category in ('NOTIFICATION') and channel ='WEB_POPUP'and coalesce( expiration_time, '3000-01-01' ) >= now()
orderby created_at desc limit $8;
------ В индекс добавлена колонка messagecreate index fix_web_message_idx_4 on web_message
using btree(user_id, coalesce(expiration_time,'3000-01-01'),
message, created_at DESC)
where status ='NEW'and category in ('NOTIFICATION')
and channel ='WEB_POPUP';
Результат функции concat можно индексировать
select concat($1, message, $2) body from web_message
where user_id = $3and status ='NEW'and category in ('NOTIFICATION') and channel ='WEB_POPUP'and coalesce( expiration_time, '3000-01-01' ) >= now()
orderby created_at desc limit $8;
------ В индекс добавлен результат выполнения concatcreate index fix_web_message_idx_4 on web_message
using btree(user_id, coalesce(expiration_time,'3000-01-01'),
concat('{"m":"', message, '"}'), created_at DESC)
where status ='NEW'and category in ('NOTIFICATION')
and channel ='WEB_POPUP';
Все это стало возможно при знании параметров
select concat('{"m":"', message, '"}') body from web_message
where user_id = $3and status ='NEW'and category in ('NOTIFICATION') and channel ='WEB_POPUP'and coalesce( expiration_time, '3000-01-01' ) >= now()
orderby created_at desc limit $8;
------ В индекс добавлен результат выполнения concatcreate index fix_web_message_idx_4 on web_message
using btree(user_id, coalesce(expiration_time,'3000-01-01'),
concat('{"m":"', message, '"}'), created_at DESC)
where status ='NEW'and category in ('NOTIFICATION')
and channel ='WEB_POPUP';
История ускорения системы.
Про три месяца оформления и исправления дефектов большой системы,
которую можно услышать за тридцать минут и прожить за десять.
Как известно, инструмент подбирается под задачу.
Но для задачи визуализации статистики по SQL-запросам с точностью до минуты (или секунды, при желании) инструмента не было,
так чтобы инструмент работал в закрытой сети, работал под максимально высокой нагрузкой, не требовал модификаций PostgreSQL.
Инструмент был собран из открытых компонент: Telegraf, InfluxDB, Grafana. И отлично показал себя на проекте.
Позволив оформлять и исправлять дефекты в течение трех месяцев не отвлекаясь ни на что другое.
Что позволило достаточно быстро ускорить систему.
А если нужна детальная статистика с учетом значений параметров запроса, то это тоже возможно. За счет простого логирования и анализа лога.
Расскажу об опыте применения двух подходов к сбору и визуализации статистики,
о том какие есть альтернативные подходы, которые не дали результата,
о том какие есть альтернативные решения, которыми вдохновлялся,
о том какие новые и полезные решения есть и появляются сейчас.
Аудитория и уровень
Доклад будет интересен инженерам по производительности и мониторингу систем, имеющим дело с PostgreSQL. Тем, кто стремиться не только тестировать и мониторить, но и оптимизировать, как саму систему так и механизм мониторинга.
Опыт оптимизации не требуется, но представление о том, что такое Java, SQL, PostgreSQL и Time Series Database нужно иметь.
Повышаю качество более десяти лет. Занимаюсь системой дистанционного банковского обслуживания юридических лиц. Основной профиль моей работы — тестирование производительности. Развиваю сообщество инженеров по тестированию производительности, помогая коллегам в telegram чате «QA — Load & Performance».
* Выбор наиболее медленных SQL-запросов
* Анализ интенсивности SQL-запроса
* Среднее и максимальное время
* Другие важные метрики
* Выбор SQL-запросов в долгих транзакциях
* Поиск кода, вызывающего SQL-запрос
* Анализ причин зависания
* Выбор сервисов с неиспользуемыми подключениями
* Справедливое распределение подключений
* Настройки времени жизни подключений