Pull to refresh

Comments 10

Нет. Использую в связке с pgwatch2 на протяжении двух последних лет, для своих задач вполне не плох.

Побуду адвокатом 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 индекса и процент по отношению к размеру таблицы.
Sign up to leave a comment.

Articles