Comments 10
Спасибо за дашборд!
В этом дашборде мне не хватает знаний в promql и postgresql.
Вот хорошая статья по promql — https://medium.com/@valyala/promql-tutorial-for-beginners-9ab455142085
Побуду адвокатом InfluxData, не согласен с некоторыми вашими претензиями:
- "Периодически выкатывают фичи непонятно зачем сделанные, например сделали ifql (Flux)" — флюкс есть естественная попытка реализовать язык более функциональный, чем изначально слабый и топорный IQL, к которому накопился просто вагон претензий. Получилось хорошо
- "или CQ" — CQ вполне себе хороший инструмент для контроля за устареванием метрик. Подходящее решение для случаев, когда нет необходимости детально исследовать данные полугодичной данности, но оценить весь период целиком нужно (рассчитать аптайм СЛА, к примеру)
- "или хронограф" — вендор старается поставлять самодостаточный стек, вот и всё. Да и хронограф позиционируется не столько как визуализатор, сколько как веб-гуй для админки стека целиком, так делает много кто. Не будете же вы выдвигать претензию в сторону ELK за то, что у них есть кибана?
- "в телеграфе ломали поддержку Прометея" — как и в любом софте, появился баг, баг пофиксили. Нет идеального ПО
- "невозможность оверрайдить RP в кафке" — вот тут могу ошибаться, так как не приходилось гонять метрики через кафку, но в вашей цепочке разве не конечный телеграф определяет RP и базу для записи?
- "частые Breaking Changes" — чисто субъективно, больше пяти лет с последнего — это нормально, до полноценного релиза InfluxDB 2.0 ещё как до луны, а первую версию никто не бросает в подвал для легаси
А вот с претензиями к производительности и стабильности соглашусь, это прям беда. По запросу "influxdb out of memory" гуглится issue, которому уже скоро годик должен исполниться, вроде, а воз и ныне там.
В общем, не будьте столь категоричны, TICK имеет право на жизнь.
InfluxDB хорош, но лучше все-таки использовать VictoriaMetrics. Она быстрее, жрет намного меньше памяти, меньше глючит и поддерживает нормальный язык запросов — MetricsQL, обратно совместимый с PromQL, который подходит намного лучше для типичных запросов по временным рядам, чем InfluxQL и Flux. Например, попробуйте на Flux создать запрос, складывающий два fields из разных measurements. Если что, то в PromQL это выглядит как a + b
.
но лучше все-таки использовать
Ну это ведь спорный вопрос. Нельзя сказать, что всем нужна именно VictoriaMetrics, так как каждый выбирает систему из своих каких-либо личных соображений, что больше всего подходит под его задачи, что он понимает.
нормальный язык запросов
Для этого критерия я бы выбрал PostgreSQL с расширением timescaledb. Во первых — потому что я специализируюсь по СУБД PostgreSQL, во вторых — это действительно SQL, в третьих — мне вполне достаточно того уровня сжатия который может предоставить timescaledb для секций.
Для этого критерия я бы выбрал PostgreSQL с расширением timescaledb. Во первых — потому что я специализируюсь по СУБД PostgreSQL, во вторых — это действительно SQL, в третьих — мне вполне достаточно того уровня сжатия который может предоставить timescaledb для секций.
TimescaleDB хороша в двух случаях:
- когда нужно делать запросы поверх временных рядов, попутно обогащая их реляционными данными из соседних таблиц;
- когда нужно делать сложные запросы, которые нельзя выразить с помощью PromQL или MetricsQL.
Оба случая встречаются достаточно редко. Для остальных случаев PromQL подходит намного лучше, т.к. длина и сложность типичного PromQL запроса намного меньше по сравнению с длиной и сложностью аналогичного запроса, построенного с помощью SQL. См. пример в начале этой статьи.
/* Схема, таблица, её размер в байтах */
WITH usertables AS
(SELECT nspname AS schemaname,
relname AS tablename,
pg_relation_size(C.oid) AS bytes
FROM pg_class C
LEFT JOIN pg_namespace N ON (N.oid = C.relnamespace)
WHERE C.relkind = 'r' /* r - "обычная" таблица */
AND C.relhasindex = TRUE /* у которой есть индексы */
AND nspname NOT IN ('pg_catalog',
'information_schema')/* не системная */ ),
/*Схема, таблица, название индекса, его размер в байтах */
userindexes AS
(SELECT schemaname,
tablename,
indexname,
pg_relation_size(C.oid) AS bytes
FROM pg_indexes I
INNER JOIN pg_class C ON I.indexname = C.relname
WHERE schemaname NOT IN ('pg_catalog',
'information_schema') )
SELECT schemaname,
tablename,
pg_size_pretty(S.bytes) AS table_size,
index_count,
pg_size_pretty(sum_bytes) AS sum_index_size,
round((sum_bytes / bytes)::numeric, 1) AS ratio /* коэффициент, на который общий объем всех индексов таблицы, отличается от её объема*/
FROM
(SELECT T.schemaname,
T.tablename,
T.bytes, /* размер таблицы */ count(I.indexname) AS index_count, /* кол-во индексов в таблице */ sum(I.bytes) AS sum_bytes /* их суммарный размер */
FROM usertables T
INNER JOIN userindexes I ON (T.schemaname = I.schemaname
AND T.tablename = I.tablename)
WHERE T.bytes > 10000000 /* желательный размер таблицы в байтах */ AND
I.bytes > 0 /* исключаем таблицы и индексы с размером 0 */
GROUP BY T.schemaname,
T.tablename,
T.bytes) S
WHERE (sum_bytes / bytes) > 2 /* желаемое значение коэффициента */
AND S.schemaname !~ 'temp' /* исключение схем */)
ORDER BY 6 DESC;
Там ограничения размера перенесены внутрь запроса списка БД
Но в место него лучше этот
WITH
btree_index_atts AS (
SELECT
pg_namespace.nspname,
indexclass.relname AS index_name,
indexclass.reltuples,
indexclass.relpages,
pg_index.indrelid,
pg_index.indexrelid,
indexclass.relam,
tableclass.relname AS tablename,
(regexp_split_to_table((pg_index.indkey) :: TEXT, ' ' :: TEXT)) :: SMALLINT AS attnum,
pg_index.indexrelid AS index_oid
FROM ((((pg_index
JOIN pg_class indexclass ON ((pg_index.indexrelid = indexclass.oid)))
JOIN pg_class tableclass ON ((pg_index.indrelid = tableclass.oid)))
JOIN pg_namespace ON ((pg_namespace.oid = indexclass.relnamespace)))
JOIN pg_am ON ((indexclass.relam = pg_am.oid)))
WHERE ((pg_am.amname = 'btree' :: NAME) AND (indexclass.relpages > 0))
),
index_item_sizes AS (
SELECT
ind_atts.nspname,
ind_atts.index_name,
ind_atts.reltuples,
ind_atts.relpages,
ind_atts.relam,
ind_atts.indrelid AS table_oid,
ind_atts.index_oid,
(current_setting('block_size' :: TEXT)) :: NUMERIC AS bs,
8 AS maxalign,
24 AS pagehdr,
CASE
WHEN (max(COALESCE(pg_stats.null_frac, (0) :: REAL)) = (0) :: DOUBLE PRECISION)
THEN 2
ELSE 6
END AS index_tuple_hdr,
sum((((1) :: DOUBLE PRECISION - COALESCE(pg_stats.null_frac, (0) :: REAL)) *
(COALESCE(pg_stats.avg_width, 1024)) :: DOUBLE PRECISION)) AS nulldatawidth
FROM ((pg_attribute
JOIN btree_index_atts ind_atts
ON (((pg_attribute.attrelid = ind_atts.indexrelid) AND (pg_attribute.attnum = ind_atts.attnum))))
JOIN pg_stats ON (((pg_stats.schemaname = ind_atts.nspname) AND (((pg_stats.tablename = ind_atts.tablename) AND
((pg_stats.attname) :: TEXT =
pg_get_indexdef(pg_attribute.attrelid,
(pg_attribute.attnum) :: INTEGER,
TRUE))) OR
((pg_stats.tablename = ind_atts.index_name) AND
(pg_stats.attname = pg_attribute.attname))))))
WHERE (pg_attribute.attnum > 0)
GROUP BY ind_atts.nspname, ind_atts.index_name, ind_atts.reltuples, ind_atts.relpages, ind_atts.relam,
ind_atts.indrelid, ind_atts.index_oid, (current_setting('block_size' :: TEXT)) :: NUMERIC, 8 :: INTEGER
),
index_aligned_est AS (
SELECT
index_item_sizes.maxalign,
index_item_sizes.bs,
index_item_sizes.nspname,
index_item_sizes.index_name,
index_item_sizes.reltuples,
index_item_sizes.relpages,
index_item_sizes.relam,
index_item_sizes.table_oid,
index_item_sizes.index_oid,
COALESCE(ceil((((index_item_sizes.reltuples * ((((((((6 + index_item_sizes.maxalign) -
CASE
WHEN ((index_item_sizes.index_tuple_hdr %
index_item_sizes.maxalign) = 0)
THEN index_item_sizes.maxalign
ELSE (index_item_sizes.index_tuple_hdr %
index_item_sizes.maxalign)
END)) :: DOUBLE PRECISION + index_item_sizes.nulldatawidth)
+ (index_item_sizes.maxalign) :: DOUBLE PRECISION) - (
CASE
WHEN (((index_item_sizes.nulldatawidth) :: INTEGER %
index_item_sizes.maxalign) = 0)
THEN index_item_sizes.maxalign
ELSE ((index_item_sizes.nulldatawidth) :: INTEGER %
index_item_sizes.maxalign)
END) :: DOUBLE PRECISION)) :: NUMERIC) :: DOUBLE PRECISION) /
((index_item_sizes.bs - (index_item_sizes.pagehdr) :: NUMERIC)) :: DOUBLE PRECISION) +
(1) :: DOUBLE PRECISION)), (0) :: DOUBLE PRECISION) AS expected
FROM index_item_sizes
),
raw_bloat AS (
SELECT
current_database() AS dbname,
index_aligned_est.nspname,
pg_class.relname AS table_name,
index_aligned_est.index_name,
(index_aligned_est.bs * ((index_aligned_est.relpages) :: BIGINT) :: NUMERIC) AS totalbytes,
index_aligned_est.expected,
CASE
WHEN ((index_aligned_est.relpages) :: DOUBLE PRECISION <= index_aligned_est.expected)
THEN (0) :: NUMERIC
ELSE (index_aligned_est.bs *
((((index_aligned_est.relpages) :: DOUBLE PRECISION - index_aligned_est.expected)) :: BIGINT) :: NUMERIC)
END AS wastedbytes,
CASE
WHEN ((index_aligned_est.relpages) :: DOUBLE PRECISION <= index_aligned_est.expected)
THEN (0) :: NUMERIC
ELSE (((index_aligned_est.bs * ((((index_aligned_est.relpages) :: DOUBLE PRECISION -
index_aligned_est.expected)) :: BIGINT) :: NUMERIC) * (100) :: NUMERIC) /
(index_aligned_est.bs * ((index_aligned_est.relpages) :: BIGINT) :: NUMERIC))
END AS realbloat,
pg_relation_size((index_aligned_est.table_oid) :: REGCLASS) AS table_bytes,
stat.idx_scan AS index_scans
FROM ((index_aligned_est
JOIN pg_class ON ((pg_class.oid = index_aligned_est.table_oid)))
JOIN pg_stat_user_indexes stat ON ((index_aligned_est.index_oid = stat.indexrelid)))
),
format_bloat AS (
SELECT
raw_bloat.dbname AS database_name,
raw_bloat.nspname AS schema_name,
raw_bloat.table_name,
raw_bloat.index_name,
round(
raw_bloat.realbloat) AS bloat_pct,
round((raw_bloat.wastedbytes / (((1024) :: DOUBLE PRECISION ^
(2) :: DOUBLE PRECISION)) :: NUMERIC)) AS bloat_mb,
round((raw_bloat.totalbytes / (((1024) :: DOUBLE PRECISION ^ (2) :: DOUBLE PRECISION)) :: NUMERIC),
3) AS index_mb,
round(
((raw_bloat.table_bytes) :: NUMERIC / (((1024) :: DOUBLE PRECISION ^ (2) :: DOUBLE PRECISION)) :: NUMERIC),
3) AS table_mb,
raw_bloat.index_scans
FROM raw_bloat
/* ограничение на размер таблиц */
WHERE raw_bloat.table_bytes > 10000000
)
SELECT
format_bloat.database_name,
format_bloat.schema_name,
format_bloat.table_name,
format_bloat.index_name,
format_bloat.index_scans,
format_bloat.bloat_pct,
format_bloat.bloat_mb,
format_bloat.index_mb,
format_bloat.table_mb
FROM format_bloat
WHERE
/* ограничение на размер bloat (более 10MB) */
format_bloat.bloat_mb > 10 AND
/* ограничение на процент bloat индекса */
format_bloat.bloat_pct > 10
ORDER BY format_bloat.bloat_mb DESC;
Выдаёт размер bloat индекса и процент по отношению к размеру таблицы.
Dashboard Postgresql Overview для postgres_exporter (Prometheus)