DBA: Ночной Дозор

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

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

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

    Что-то мы сильно грузим диск...

    Вся база нашего сервиса "распилена" на суточные секции и реорганизована под максимальную пропускную способность. Подробнее об этом можно прочитать в статьях "Пишем в PostgreSQL на субсветовой: 1 host, 1 day, 1TB" и "Экономим копеечку на больших объемах в PostgreSQL".

    При этом мы стараемся писать в базу предельно сбалансированно (см. "Телепортация тонн данных в PostgreSQL"), но все равно наши "хотелки" превышают возможности дисковой подсистемы - занята она регулярно на все 100% и очереди доступа, небольшие, но есть всегда:

    "Чукча - не читатель, чукча - писатель!" Или нет?

    Как бы это и не удивительно, когда пишешь со скоростью до 200MB/s:

    ... и читаешь не меньше.

    Стоп - читаешь?.. Но у нас же нет тонн неэффективных запросов, которые бы столько читали! Но при этом больше половины всех дисковых операций - чтение!

    Как же так? Кто этот злодей, который столько читает? И почему это у нас в середине дня до 18 maintenance-процессов? К ним относятся разные VACUUM, ANALYZE, CREATE INDEX и т.п.

    Как и что мы мониторим в PostgreSQL, стоит прочитать в статье "Мониторим базу PostgreSQL — кто виноват, и что делать".

    "Вот те раз! - подумал Штирлиц"

    А ну-ка, воспользуемся функционалом анализа работы autoVACUUM/autoANALYZE:

    Оказывается, у нас в течение дня бегает множество autovacuum (to prevent wraparound) по секциям каких-то других дней - и все они "промахиваются" мимо кэша данных, и лезут в диск! То есть мы пишем в базу настолько много транзакций, что она регулярно начинает хотеть их "прибрать" по старым секциям, а они у нас до 150GB… Хм.

    "Вот те два, - подумал Мюллер, и бросил второй."

    Так, а что у нас делает autoANALYZE?..

    Кучу раз в течение дня делаются ANALYZE-пробежки по append-only табличкам! Большого смысла в этом тоже не сильно много, поскольку заведомо все записи тут уникальны и имеют PK.

    Ночной Дозор и не только

    Итак, начнем "с хвоста" - избавимся от autoanalyze. Для этого перенастроим соответствующие параметры сервера:

    ALTER SYSTEM SET autovacuum_analyze_scale_factor = 1;
    -- ждем для следующего сканирования, пока в табличку не запишут еще столько же (x2) записей
    
    ALTER SYSTEM SET autovacuum_analyze_threshold = 100000;
    -- ... но не меньше 100K

    Теперь внимательно посмотрим на "старые" секции. Устаревают они ровно тогда, когда мы перестаем в них писать - около полуночи, как раз когда нагрузка минимальна.

    Они у нас есть двух видов:

    • относительно небольшие с поддержкой INSERT ... ON CONFLICT UPDATE со всякими счетчиками

    • мега-большие append-only "факты"

    pg_repack

    В силу специфики MVCC, первый вид секций оказывается к концу дня "сильно-дырявым". Это замедляет чтение и приводит к занятости большего объема.

    Чтобы "схлопнуть" секцию, не блокируя доступ к ней, мы разработали ежесуточный ночной cron-скрипт, стартующий в 00:15, который последовательно применяет ко всем таким таблицам "предыдущего дня" pg_repack, "схлапывает" их и физически переупорядочивает записи в соответствии с наиболее используемым индексом:

    Модуль pgrepack — это расширение Postgres Pro Standard, которое позволяет ликвидировать пустоты в таблицах и индексах и может дополнительно восстанавливать физический порядок кластеризованных индексов. В отличие от CLUSTER и VACUUM FULL, оно выполняет эти операции «на ходу», обходясь без исключительных блокировок таблиц в ходе их обработки. К тому же pgrepack действует эффективно, демонстрируя производительность, сравнимую с непосредственным использованием CLUSTER.

    VACUUM FREEZE

    По всем остальным (append-only) таблицам - выполняем принудительно VACUUM FREEZE, убирая у базы напрочь желание делать это "внезапно" по ходу рабочего дня:

    VACUUM FREEZE rawdata_20190419;
    VACUUM FREEZE rawplan_20190419;
    ...

    Пруфы!

    Объем дискового чтения сократился в 20 раз, загрузка диска (disk busy) уменьшилась до ~60%:

    Тензор
    Разработчик системы СБИС

    Комментарии 18

      0

      Спасибо за пост. Что вы думаете о https://github.com/dataegret/pgcompacttable? pgcompacttable не сильно нагружает БД.

        0
        Мы [давно] сравнивали с pg_repack, он показался как-то попроще и поэффективнее тогда. Деталей, за давностью лет, уже не припомню.

        А для аналогичной по смыслу методики «апдейти хвост, отрезай пустое VACUUM'ом» есть самописные скрипты для тех серверов, куда контриб не хочется вкатывать.
        0

        С точки зрения метрик вы сделали офигенно. -60% нагрузки. С точки зрения понятности архитектуры: теперь есть cron скрипт, после смерти которого нагрузка вырастет на 166% и никто (без его пересоздания посредством повторения исследования из этого поста) ничего не сможет сделать. Т.е. это классический админский костыль на которых всё и держится. Они очень хорошо работают, но не очень хорошо сопровождаются.


        Каким образом и когда вы узнаете, что он сломался? И как человек, который "что-то заподозрит" поймёт, что дело в нём?

          +2
          Когда «что-то» ломается, то каждый сработавший триггер отсекает набор возможных причин. И если «админский костыль» (а чем он хуже программерского?) — часть инфраструктуры, то и мониториться он должен по аналогичным правилам.

          Навскидку, в данном примере, резкое увеличение количества одновременных автовакуумов в течение суток явно укажет на этот скрипт.
            0
            Слушайте, а почему у вас ротация сделана ночью, а не днем? Ну просто это вроде важная операция, которую неплохо бы иметь возможность отлаживать во вменяемом состоянии, если вдруг оно упадет?
              +1
              Если посмотреть на последнюю картинку, то там есть такой нехилый пик до 80% в 00:15-00:30 — это вот ровно этот скрипт. И это те самые +80%, которые в течение дня наблюдать совсем не хочется.
              А если оно «вдруг упадет», то система не станет мгновенно работать в разы хуже — то есть запас по времени на устранение последствий достаточен.
                0
                Странно, что простая вроде бы операция занимает 15 минут. Я подобный трюк проводил с мускулем лет 5 назад, там был банальный drop partition для достаточно старых partition-ов, и это вроде не было так уж дорого… Хотя конечно, как и сколько может тут занимать перестройка индексов — достойный ответа вопрос, так что похоже на ваших данных — вы правы.
                  0
                  Там идет обработка порядка 100-150GB данных за предыдущие сутки — с физическим переупорядочиванием и перестроением индексов.
              0

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


              Я к тому, что если у вас есть критическое улучшение инфраструктуры (рост утилизации ресурсов СУБД на 166% — это вполне близко к "критическое" при указанных нагрузках), то оно должно быть отражено в описании инфраструктуры. То есть есть два сервера: на одном поправлено, на другом нет. Чем они отличаются, кроме "последствий"?


              Ответ должен включать какую-то инструментацию: тесты, декларативное описание, доказательства работы (кроме быстрой производительности).


              Кстати, почему крон, а не таймер systemd? Вот я таймера я адекватную инфраструктуру проверки работы могу себе представить, а вот для крона — только если несколько слоёв баша намазать...

                +1
                В цепочке «процесс < — тот, кто контролирует процесс < — тот, кто контролирует процесс, который контролирует < — ...» где-то есть разумное ограничение, когда стоит остановиться. И инструкция, и описание, и «скрипт, проверяющий скрипт» — все это звенья этой цепи, а ее длина обычно определяется ценностью проверяемого процесса и его последствий.
                  0

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


                  Т.е. в нашем случае тесты являются финальным якорем для решений (и костылей). Что-то поменяли и сломали костыль? Оно просто не попадёт в мастер.

                    0
                    Мы же вроде не про CI и «сломали в версии»? А про мониторинг самой работоспособности этого скрипта — например, сам cron-демон по каким-то причинам сбойнул «на бою» и наш скрипт не запустил.

                    И если «админский костыль» (а чем он хуже программерского?) — часть инфраструктуры, то и мониториться он должен по аналогичным правилам.
                    В этом контексте под «мониториться» надо понимать и весь CI-цикл.
                      0

                      Я немного про другое. Если есть проверка, которая следит за таймером и соответствующим сервисом (не крон. пожалуйста, не крон!), то она работает на слепой вере. 99.9% времени она работает хорошо (сообщает, что проблемы нет), в оставшийся 0.1% она работает плохо (не сообщает о том, что проблема есть). Откуда вы знаете, что ваша проверка ловит проблему?


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

                        0
                        99.9% времени она работает хорошо (сообщает, что проблемы нет), в оставшийся 0.1% она работает плохо (не сообщает о том, что проблема есть). Откуда вы знаете, что ваша проверка ловит проблему?
                        А откуда мы знаем, что остальные 99.9% она работает действительно хорошо? Ах, это нам сообщил непогрешимый проверочный скрипт?.. Или таки проверочный скрипт тоже надо проверять?

                        Ровно про это я и говорю, что количество степеней проверки определяется ценностью проверяемой операции.
                          0

                          Ну, достоверно сломать проще, чем сделать достоверно работающее. В соответствующем скрипте у нас написано:


                          • проверить, что работает
                          • сломать
                          • проверить, что срабатывает
                          • починить
                          • проверить, что работает

                          Вот эта часть "сломать" — она чаще всего тривиальна, и в сочетании "проверить, что срабатывает" верифицируется в силу маловероятности синхронных двух одновременных багов в обоих проверках ("маловероятность" тут следует читать в одном ряду с "маловероятной коллизией рандомных uuid'ов").


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

              +2
              Никто не мешает подцепить метрику и алертинг на промежуточные сигналы типа того же autoANALYZE на таблички, помеченные как append-only. А к ним в документацию — ссылочку на пост-мортем от этого исследования, чтобы когда оно сломается и разбудит инженера все было под кончиками пальцев.
                0

                Я не знаю что у проекта "снизу" (как всё развёртывается/тестируется), так что не могу сказать достаточно этого или нет. Если просто добавлен руками алерт в мониторинг с ссылкой на статью, то это ещё один костыль, который подпирает предыдущий костыль.

          Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

          Самое читаемое