Живой митап #RuPostgres: вопросы и ответы с экспертами Avito. Расшифровка прямого эфира
Около месяца назад мои коллеги из DBA-команды приняли участие в живом митапе на youtube-канале #RuPostgres Live, где отвечали на вопросы Николая Самохвалова и зрителей, которые присылали их в форму и подключились к трансляции. Получилась интересная и содержательная беседа про PostgreSQL, опыт работы с разными версиями и задачами. Поэтому мы решили сделать текстовую расшифровку этой встречи, обогатив её полезными ссылками. В комментариях задавайте вопросы, если они возникнут — постараемся на них ответить!
Николай Самохвалов: Привет, уважаемые Youtube-зрители. Меня зовут Николай Самохвалов. Это #RuPostgres Live #2: вопросы и ответы с экспертами Avito, и со мной сегодня Константин Евтеев, Дмитрий Вагин и Сергей Бурладян. Это далеко не все Postgres-эксперты в компании Avito, я так понимаю. Сколько у вас людей в команде, которая знает Postgres?
Константин Евтеев: Postgres-команда — это, по факту, OLTP-юнит — состоит из 13 человек, которые в свою очередь делятся: часть занимается платформой, часть — продуктовой разработкой.
Николай Самохвалов: Женщины есть?
Константин Евтеев: Да.
Николай Самохвалов: Прямо сейчас в чатике Youtube можно писать вопросы. У нас есть и заранее подготовленные вопросы, в том числе — от меня лично. Поговорим, как в компании Avito устроена «готовка» Postgres. И первый вопрос: правда ли, что все ещё 9.2?
Константин Евтеев: Нет. 9.2 уже нет. Мы используем все версии Postgres, которые официально поддерживаются комьюнити.
Николай Самохвалов: Там end-of-life был совсем недавно…
Константин Евтеев: В сентябре. Мы к этому подошли, и с 9.2 мы проапгрейдились на разные версии, в том числе 9.4, 9.5, 9.6.
Николай Самохвалов: 10…?
Константин Евтеев: Пока ещё нет. 10 у нас есть в тесте.
Николай Самохвалов: С чем связана такая разношёрстная картина?
Константин Евтеев: Разношёрстная картина связана прежде всего, с тем, что перед апгрейдом мы сначала тестируем версию, и далее проводим апгрейд. Смотрим, как она себя показала, какой показывает перформанс, и после этого (для следующего инстанса) мы можем тестировать другую версию, апгрейдиться на 9.4–9.5. Потому что любой апгрейд связан с даунтаймом, и апгрейда ради апгрейда как такового нет. Мы делаем его ради получения необходимой функциональности и производительности.
Николай Самохвалов: Получается, у вас есть четыре разных версии?
Константин Евтеев: 9.3 у нас нет. Есть 9.4, 9.5, 9.6. 10 в тесте.
Николай Самохвалов: Если у кого-то из вас 9.3, срочно апгрейдьтесь. Был один из вопросов в Youtube: «Стоит ли мне из 9.3 сразу на 9.6 апгрейдиться, или мне нужна промежуточная версия?». Я бы сказал, что апгрейдиться нужно сразу на десятку.
Сергей Бурладян: Можно пропустить промежуточную версию.
Николай Самохвалов: Я немного не понимаю логику этого вопроса. Люди, наверное, думают, что эти версии более сырые. Но 9.6 существует уже год.
Сергей Бурладян: Кого-то может беспокоить, что, допустим, формат поменялся, индексы пересоздавать. Но pg_upgrade поддерживает пропуск версии, можно быть спокойным.
Николай Самохвалов: На 10 ты бы стал перетаскивать свой проект?
Сергей Бурладян: Пока нет. У нас такая идея, что мы апгрейдимся где-то на четвертый минорный релиз. На 10,4 мы бы уже смогли, наверное, перейти.
Николай Самохвалов: Это прямо официальная политика?
Сергей Бурладян: Полуофициальная.
Константин Евтеев: Но при этом у нас есть dev- и test-среды, там мы проводим тестирование самых новых версий. В том числе 10 версию мы тестировали начиная с альфы и написали несколько багрепортов: 1, 2.
Николай Самохвалов: Как это делаете?
Константин Евтеев: У нас есть семплирование, с одной стороны, с другой стороны — фикстуры для того, чтобы проводить тестирование нашего приложения. После семплирования данных мы готовим image, который деплоим в наше облако. Помимо того, что поднимается база, dev- и test-среда — это еще и большое количество инфраструктуры. Там поднимаем внешние индексы, различные кэши, очереди и другие элементы инфраструктуры. После деплоя нужно проинициализировать внешние индексы. У нас очень активно используется логическая репликация. На семпловых данных нужно поднять всю связанную инфраструктуру. Таким образом, накат семпловых данных приводит к инициализации около десятка баз данных, через механизм очередей PgQ, наполняются наши копии матвьюх, осуществляется доставка данных во внешние индексы и т. д.
Николай Самохвалов: Подходим к вопросу: какой у вас объем данных в Postgres хранится?
Константин Евтеев: Общий объем наших баз данных — более 15 ТБ. Работают они на десятках серверов. Типовая рабочая нагрузка на них — около 10 тыс. транзакций в секунду.
Николай Самохвалов: Очень интересный вопрос недавно проскакивал на Facebook. Есть продакшн-данные, которые можно было бы потестировать в тест-среде, а, может быть, даже в dev. Это идеальный случай: ты можешь посмотреть, где что тормозит. Ясно, что во-первых, не всем разработчикам нужно показывать все данные. Во-вторых, огромное количество данных размером в 15 ТБ мы легко на dev-среду не заберем. Как готовите эти среды, как забираете данные?
Константин Евтеев: Как нарезать семпл? Бывают данные связанные, бывают несвязанные. У нас на проекте есть две основные сущности — пользователь и объявление. Мы берем какие-то выборки пользователей, собираем все их объявления, далее идет выборка по остальным таблицам. Вероятнее всего, нужно ограниченное количество наборов — мы задаем лимиты в нашем скрипте.
Николай Самохвалов: Сколько процентов?
Константин Евтеев: Зависит от параметров и задачи, которую мы решаем в тесте. В большинстве случаев большой объём не нужен.
Дмитрий Вагин: Я немного расскажу весь механизм. Каждое утро у нас запускается скрипт, который забирает определенных пользователей и их объявления: рандомный набор плюс заранее зашитые тестовые юзеры. Плюс куча зависимостей с небольшими ограничениями. Вся сборка с продакшн-баз занимает около получаса. Затем на этом строятся docker-образы и пушатся в registry.
Николай Самохвалов: Как выбирается рандом? Table simple или что-то своё?
Дмитрий Вагин: Нет. У нас есть счетчики, сколько у каких пользователей активных объявлений, рандомно берется пачка пользователей. Критерий такой: чтобы выдача в этом тестовом образе на сайте была 2 страницы в Москве, чтобы можно было тестировать более-менее наглядно. Простые условия.
Николай Самохвалов: Это не много, получается, данных.
Дмитрий Вагин: Там куча справочников, куча зависимых данных.
Николай Самохвалов: Но самих объявлений получается не много. Если вы что-то новенькое делаете, например, индекс забыли, поймете об этом только на продакшене? Или есть что-то в тестовом окружении?
Дмитрий Вагин: Это мы увидим на тестировании.
Константин Евтеев: «Не много» и «забыли индекс» — это все зависит от объёма данных. Если есть несколько гигабайт, индекс будет работать, и будет заметно. А мы говорим, что там несколько гигабайт. А если разработчикам нужно нагрузочное тестирование, то можно поднять необходимую базу из бэкапа на dev-кластер. Мы иногда можем его делать. Главное, что я хотел упомянуть — при сборке этих сред в том числе идет обфускация, все данные анонимизированы.
Николай Самохвалов: Написали программисты специально?
Константин Евтеев: Чтобы не видеть настоящих пользовательских данных.
Николай Самохвалов: Это круто. Но подразумевается, что если схема меняется, мы должны переписывать. Требует дополнительного времени.
Константин Евтеев: В том числе. В случае, если работаете с семплом данных, это неизбежно, поднимая dev-среды. Перед тем, как вести разработку в прод, нужно делать двойную работу: сначала делать разработку в dev- и test-средах. Поэтому когда появляются новые проекты, мы всей командой агитируем за то, чтобы использовать не семплирование данных, а чтобы писали фикстуры, чтобы не иметь единую точку отказа везде.
Николай Самохвалов: В основном что за языки используются? Ruby или PHP?
Константин Евтеев: Фикстуры можно делать на различных языках.
Николай Самохвалов: У вас какие?
Константин Евтеев: PHP.
Николай Самохвалов: Вы стремитесь от семплирования уйти в сторону синтетических данных, чтобы на тестировании использовать?
Константин Евтеев: Да, во-первых, будут сначала тесты писаться, с другой стороны, не будет необходимости отдельно поддерживать семплинг данных. И решать в том числе вопросы аварий семплера. Он же может ломаться, если кто-то поправил структуру, но не написал миграцию, не добавил в семпл…
Николай Самохвалов: Как говорил один известный постгрессоид, жизнь богаче. Живое распределение данных — оно другое. Правильно? Может быть, продакшн-данные живые тоже есть смысл брать? Я просто услышал мысль что круто — фикстуры, но мне кажется, что нужно посмотреть живое распределение.
Константин Евтеев: В этом случае как раз стейджинг. Необходимость нагрузочного тестирования, и есть набор серверов, куда можно развернуть полный бэкап прод-базы.
Николай Самохвалов: Про мониторинг интересно спросить. Как понимаете, что там индексов не хватает? Что используете? Знаю, у вас есть доклад про мониторинг, Дмитрий делал. Можно пару слов: какие инструменты?
Дмитрий Вагин: Идея простая. Берете все, что есть, и кидаете в Graphite или куда-то еще. А потом строите дэшборды. Когда где-то на очень загруженной табличке не хватает индекса, обычно это сразу видно в том, что на сайте ничего не работает, или видно это в топе запросов где-то в pg_stat_activity и прочем. Более сложные кейсы приходится разбирать по графикам в дэшбордах. Допустим, тут чтения с диска слишком много на этой табличке. Смотришь, где какие там запросы идут, и где-то, и возможно, надо индекс чуть другой сделать.
Николай Самохвалов: Почему не взять что-то существующее?
Дмитрий Вагин: Можно, но когда берешь что-то существующее, приходится разбираться, как это существующее берет эти метрики и что ты в итоге видишь. Приходится разбираться, что оно берет, как обрабатывает, куда складывает и что ты в итоге видишь. И вот я не могу так просто взять и доверять какой-то циферке, которую говорит какая-нибудь утилита, например munin. Но в плагин munin можно залезть и посмотреть, что и как там…
Николай Самохвалов: В Zabbix тоже ведь можно увидеть, как там всё работает.
Дмитрий Вагин: Zabbix просто не вывезет на наших объемах.
Николай Самохвалов: Интересно.
Дмитрий Вагин: У нас Graphite-то не всегда всё вывозит.
Николай Самохвалов: Вопрос из чата: как вы делаете vacuum full огромной таблицы более 100 ГБ?
Дмитрий Вагин: Есть два варианта. Первый — не делать.
Николай Самохвалов: Плохой вариант.
Дмитрий Вагин: Ну… Зачем vacuum full?
Николай Самохвалов: Кстати, правильно. Я не сразу понял этот вопрос. Зачёркиваем «full». Никто не делает vacuum full просто так, не нужно это делать.
Дмитрий Вагин: Vacuum работает.
Николай Самохвалов: Ок. Мы перед трансляцией мы обсуждали, что размер 15 ТБ даже уменьшается. За счет чего?
Константин Евтеев: Мы это назвали «хвост». Есть данные, которые активные, горячие объявления. Потом есть, допустим, удаленные, заблокированные, а есть удаленные навсегда, то есть когда ты их удаляешь и больше не видишь их в интерфейсе. Но мы обязаны всю эту информацию хранить. Но это можно делать уже в другом месте — тоже в Postgres.
Николай Самохвалов: В 9.2?
Константин Евтеев: (Смеётся). Ну, например, да. Мы написали скрипт, который собирает все старые данные и переносит их в другую базу, доступную только для бэкофиса. У нас была высокая скорость роста объема основной базы данных. После запуска скрипта, переноса в данных в «хвост» и последующего реиндекса объем уменьшился и скорость роста замедлилась, почти остановилась. Периодически мы делаем реиндекс.
Николай Самохвалов: А всякие pg_repack не используете, или аналоги?
Константин Евтеев: В том числе используем. Сейчас Дима расскажет случай.
Дмитрий Вагин: Я один раз выключил специально на одной табличке автовакуум. И забыл его включить. Через две недели увидел, что табличка вместо 20 ГБ стала 300 ГБ. Думаю: «Ладно, надо исправить». Автовакуум отработал честно, она наполовину пустая, и надо ее как-то ужать. Вариант какой? Сделать даунтайм, выключить продакшн-нагрузку, сделать vacuum full — не очень. Второй вариант — использовать скрипт PG Compactor, который написал Сергей. Это старая всем известная идея — апдейтить с конца таблицы странички…
Николай Самохвалов: Я тут вступлю. Это пример epic fail опенсорса, когда Hubert depesz Lubaczewski поднимает какую-то тему, потом ребята легируют, потом Сергей Коноплёв включается, потом Максим Богук включается, и у всех своя версия. Они вот такие расфорканные три форка, а теперь я слышу про четвертый форк! Не знаю, может в NDA что-то такое, что каждый свое пишет, не могут договориться. Я пытался использовать все это, но для себя лично остановил выбор на pg_repack. Как раз в тот момент, когда я об этом задумался, Amazon добавил официальную поддержку pg_repack. Это практически индустриальный стандарт становится. А вы говорите, что используете апдейты. Для тех, кто не знает, pg_repack делает отдельную таблицу, триггерами следит за изменениями. Почему не pg_repack? Второй вопрос: почему всё-таки 4-я версия?
Сергей Бурладян: pg_repack редактирует служебный каталог Postgres. Нам это кажется страшным.
Николай Самохвалов: Напрямую?
Сергей Бурладян: Да.
Николай Самохвалов: Amazon не кажется.
Сергей Бурладян: Да. И потом, он использует триггеры, это дополнительная нагрузка во время продакшна. Занимает место, чуть ли не двукратный объем. Поэтому мы не хотели использовать pg_repack. И подход с постоянным апдейтом этих строчек, чтобы они сдвигались в свободное место, нам кажется более подходящим.
Николай Самохвалов: Похоже на дефрагментацию Windows.
Сергей Бурладян: Да.
Николай Самохвалов: Почему своя версия?
Сергей Бурладян: Просто нам хотелось сделать простую версию, чтобы было понятно, как она работает. Там нет фишек, которые есть в тех версиях, о которых ты говорил. У нас там, грубо говоря, вызов апдейтов и вызов вакуума. Тоже написан на Perl. Сейчас буду, наверное, на Python. Эта штука используется редко, но иногда используется.
Николай Самохвалов: Давайте немного вернемся назад. Как понять, что таблица распухла? Есть одна компания, они не показывают график блоттинга, объясняя это тем, что чтобы нормально показать, нужно использовать расширения, которые создают большую нагрузку. То есть чтобы нормально понять блоттинг, нужно влезть в кишки, потрясти их, и это очень заметно. А как вы это делаете?
Дмитрий Вагин: Мы не смотрим на блоат. Вакуум работает нормально. Достаточно не выключать автовакуум, и все будет отлично. Он чуть более агрессивен, чем дефолтный.
Николай Самохвалов: 1%, 2%? Там есть threshold, который говорит, когда analyze делать, и когда делать сам вакуум в зависимости от того, сколько в таблице грязного.
Сергей Бурладян: autovacuum_vacuum_scale_factor = 0.0014. По умолчанию vacuum_cost_limit, по-моему, стоит цифра 200, у нас 700. Это тоже у нас выкручено, чтобы он чаще срабатывал.
Николай Самохвалов: Самое интересное — это сколько процентов.
Сергей Бурладян: Пока есть место на дисках, то можно не задумываться над этим. Можно задуматься, если видно, что запросы тормозят, допустим.
Николай Самохвалов: Мониторинг?
Сергей Бурладян: Да.
Николай Самохвалов: Был вопрос в другую сторону: про безопасность. Храните ли вы ACL в Postgres?
Сергей Бурладян: ACL не используем. Row level security не используем.
Николай Самохвалов: Какие инструменты используете для файловер резервной репликации больших данных?
Сергей Бурладян: Свои инструменты, готовых не используем. Есть набор скриптов для файловера. До сих пор используем Londiste, причем наш, патченный, который позволяет после файловера автоматически восстановиться.
Николай Самохвалов: Londiste сейчас у вас основной инструмент?
Сергей Бурладян: Да, основной для логической репликаци.
Николай Самохвалов: PL/pgSQL код есть у вас?
Константин Евтеев: Есть, много.
Николай Самохвалов: Нравится?
Константин Евтеев: Зависит от случая. Но вообще нравится.
Николай Самохвалов: Расскажите, как готовите, тестируете, дебажите.
Константин Евтеев: Я больше расскажу про версионирование PL/pgSQL кода. Есть вопрос: как версионировать код и схему базу данных вместе с кодом приложения? Ведь когда у вас много серверов приложений, и они выкатываются постепенно, необходимо поддержать старую и новые версии PL/pgSQL кода.
Если мы говорим про схему базы данных, то мы это делаем через миграции. Все они должны быть обратно совместимыми, потому что в бою одновременно может находится как уже новые апликейшены, так и старые. Что же делать с кодом? Мы пришли к двум подходам, и сейчас в процессе перехода с одного на другой.
Первый подход — когда мы клали все наши хранимые процедуры на PL/pgSQL вместе с кодом апликейшена. Далее написали обвязку, которая читает каталог, каждая процедура хранится в отдельном файле, читает от него чек-сумму, обращается к базе, непосредственно в базе хранится контрольная сумма, то есть сравнивает, есть ли такая версия процедуры или нет. Она создает новую хранимую процедуру с припиской определенной версии, регистрирует в таблице. Например, создать пользователя, версия 1. В таблице хранится просто 1, а потом из этой таблички билдится словарик, который хранится вместе с приложением, и приложение, если видит обвязку «создать пользователя», у него есть словарик в котором написано, что этот суффикс должен быть 1, 2 или 3.
Есть недостатки.
- Когда появляется много проектов, всё это нужно в каждый проект внести и поддерживать.
Второй недостаток — что в данном случае те процедуры, которые вызывают другие процедуры, уже так не получится версионировать. Соответственно, пришли ко второму подходу. - Второй подход: когда мы просто держим некий пул пользователей, например. Перед деплоем приложения пользователь ротируется по кругу, у одного из пользователей полностью пересоздается схема с хранимыми процедурами. Новый деплой приложения происходит вместе с новым пользователем и схемой.
Николай Самохвалов: Один раз при создании. Прикольно. Видел в презентации, что вы достаточно много используете переменные сессий в Postgres — GUC Variable (Grand Unified Configuration).
Константин Евтеев: Да. Расскажу. Бояться их не нужно. Этот кейс мы изучили. В каком случае он у нас возник? Мы делали доставку в нашу аналитическую подсистему. Мы ее делаем с помощью очередей PgQ и deferred-триггера. Вешаем на таблицу deferred-триггер на первое срабатывание. Если запись меняется несколько раз, нам нужно поймать именно первое изменение. В момент первого изменения мы по старым данным видим все записи, а новые данные можем выбрать селектом непосредственно из базы.
Дальше у нас возникла следующая задача. Что, если у нас много связанных таблиц, а нам хочется непосредственно вместе с изменением этой таблицы еще прокинуть сигнал о том, что менялся объект в соседней таблице? В данном случае, когда меняем данные в соседней таблице, мы берем и выставляем сессионные переменные. Например, выставляем ключ: user 5 сделал какое-то действие. Только в текущей сессии. И в эту переменную делаем key value пары: вдруг несколько будет таких пользователей. И далее уже непосредственно в deferred-триггере проверяем эти ключи, сессионные переменные, и если что, подмешиваем некие сигналы. Таким образом у нас получается производная от данных: в таком виде на источнике их нет, а на приемник уже придет совершенно другой набор. Мы все это потестировали под нагрузкой, поняли, что никакого overhead при выставлении и использовании сессионных переменных не идет. (Во всяком случае, мы его не увидели).
Николай Самохвалов: А JSON-B или JSON используете?
Константин Евтеев: В большинстве случаев нет. В некоторых местах начинаем использовать, но мы не используем функции для работы с JSON-B. Почему? Как выяснилось, мы CPU bound. Сам формат JSON-B удобен для хранения и для работы, но когда мы CPU bound, то эта вся работа ведется на уровне приложения. Но, кстати, при этом мы активно используем Hstore. Он исторически раньше появился, но там все те же проблемы с CPU.
Николай Самохвалов: Видел в твоих слайдах про это. Когда ты говоришь про key value, это про hstore?
Константин Евтеев: Да.
Николай Самохвалов: А индексы? Они хранятся, hstore? GIN? Вообще GIN, GIST используются как-нибудь?
Константин Евтеев: В одном случае использовали. С ним есть некая история. Нужно внимательно читать документацию. В частности, у нас был кейс, когда надо выставлять fast update off. Потому что он до определенного размера очень хорошо работал, запись была достаточно активная в эту таблицу. А потом начало замирать при апдейтах.
Сергей Бурладян: Это написано в документации Postgres.
Дмитрий Вагин: Когда идут изменения по колонке, на которой есть GIN-индекс, то, чтобы делать изменения быстро, он строит у себя в небольшом поле маленькое дерево, и потом при чтении обращается к основному дереву и к изменениям в памяти. В какой-то момент место заканчивается, ему надо все это размазать — перенести изменения в большой индекс. В этот момент у нас все вставало на две минуты, и все упиралось в это.
Николай Самохвалов: Это означает, что в единицу времени было слишком много апдейтов?
Дмитрий Вагин: Апдейтов всегда много, потому что продакшен у нас ого-го.
Николай Самохвалов: 10000 транзакций, или сколько…
Дмитрий Вагин: Да-да-да. И просто нет момента, когда он может тихонько взять и сбросить. В итоге он говорит нам: «Я уперся в лимит, мне надо все сбросить. Подождите». И мы ждём. В итоге мы отключили fast update, и всё стало хорошо.
Николай Самохвалов: В целом каждый апдейт стал медленнее?
Дмитрий Вагин: Не заметили.
Николай Самохвалов: Из этих 10000 транзакций сколько модифицирующих?
Константин Евтеев: 1500. 90 000 в минуту. На разных базах, кстати, по-разному. Этот пример — в контексте одной из них. На других базах везде примерно 10 тысяч транзакций в секунду. Где-то запись, грубо говоря, 500 транзакций в секунду, а все остальное — чтение. А есть базы, где при общей нагрузке 8 тысяч транзакций в секунду запись 4 тысячи.
Николай Самохвалов: Разные версии, базы — это разные сервисы, у них разные команды?
Константин Евтеев: Есть разные сервисы, там где микросервисная архитектура, один сервис — одна база. Есть еще legacy — это монолит, у которого есть несколько баз, часть связана, в том числе и Londiste и event streaming, мы события через PgQ гоним. Часть из них связывается на уровне приложения.
Николай Самохвалов: Используете ли PostgreSQL в качестве аналитической базы данных?
Константин Евтеев: Раньше использовали, сейчас нет. У нас Vertica, и мы очень довольны.
Николай Самохвалов: Как часто из Postgres пуляете данные?
Константин Евтеев: В реальном времени с минимальной задержкой. Гоним через PgQ, а Vertica забирает. У нас для этого есть один выделенный сервер. Дальше команда Vertica забирает их оттуда в определенном порядке. За счет того, что у нас есть PgQ, на sequence все данные упорядочены.
Николай Самохвалов: Если что-то удаляется, обновляется, в Vertica тоже обновляется, или вы складируете новую версию, старую тоже храните? Строчка, допустим, обновилась.
Константин Евтеев: В Vertica хранятся все версии. Есть очень хороший доклад Николая Голова и статья на Хабре, как непосредственно реализовано хранение в Vertica.
Николай Самохвалов: Что с другими БД? Что c SQLite?
Константин Евтеев: В целом у нас такой подход в компании — когда команда начинает делать новый проект, она вольна выбирать из технического радара набор технологий, который ей удобней и какой более оптимален для решения ее задач.
Николай Самохвалов: ClickHouse нет в радаре?
Константин Евтеев: Есть. ClickHouse используем для хранения метрик.
Николай Самохвалов: CockroachDB?
Константин Евтеев: Нет. Для одного из решений хорошо подошел SQLite. Он уже в продакшене. В том числе для этого хорошо подходил и Postgres… Но определенное количество TPS выдал SQLite, мы его выбрали.
Николай Самохвалов: Вы помогаете всем командам по Postgres?
Константин Евтеев: Да. Делаем DBaaS, предоставляем базу данных под определенные требования. Если необходимо, выступаем в качестве некого SWAT, который помогает реализовывать бизнес-логику тем или иным командам. Можем научить или реализовать определенную бизнес-фичу.
Николай Самохвалов: Нет такого, чтобы использовать не только Postgres? Хотите — MongoDB, хотите — Tarantool, например?
Константин Евтеев: Для определенных задач есть и Tarantool, и MongoDB.
Николай Самохвалов: А MySQL, MariaDB используете?
Константин Евтеев: MySQL в продакшене нет. Может быть, есть на внутренних проектах.
Николай Самохвалов: Еще вопросы в чате. Что можете посоветовать для онлайн-миграции с 9.3 на 9.5 или 9.6? Londiste, Bucardo или что-то ещё? Сергей, может, лучше знает?
Сергей Бурладян: Мы просто не рассматривали такие процессы миграции, потому что, скорее всего, на первый взгляд, они просто не справятся с нашим трафиком. Ни Londiste, ни Bucardo не смогут нам логически реплицировать всю базу.
Николай Самохвалов: Давайте представим ситуацию (я знаю, что это не так), что у вас всё на 9.3. Ну, допустим, не повезло, и вам нужно что-то более свежее. Ваши действия?
Сергей Бурладян: Мы сделаем даунтайм и используем pg_upgrade.
Николай Самохвалов: На сколько ляжет сайт Avito?
Сергей Бурладян: Мы используем pg_upgrade, штатную утилиту Postgres, а у нее есть замечательный режим работы с хардлинками. Благодаря этому режиму даунтайм займет приблизительно 3–5 минут. 10 — максимум.
Николай Самохвалов: То есть берем документацию, изучаем, все штатными средствами, с таким подходом с хардлинками pg_upgrade. Вот вам ответ.
Сергей Бурладян: Да.
Николай Самохвалов: Как реплику ввести?
Сергей Бурладян: pg_upgrade может проапгрейдить только master, потому что ему нужно писать в сервер. А standby проапгрейдить не может. У нас есть в бою standby. Мастера нам не хватает, часть нагрузки идет на читающий standby. Мы уже не можем запустить просто один master без standby. (Раньше мы так и делали: апгрейдили мастер, потом спокойно пересоздавали standby, и все). К счастью, авторы Postgres написали в документации специальный алгоритм, который с использованием утилиты rsync позволяет после pg_upgrade проапгрейдить ещё и standby. Его хитрость заключается в том, что просто используется возможность rsync скопировать хардлинки. Грубо говоря, он воссоздает на standby такую же структуру хардлинков, как и на мастере. На standby есть те же все данные, что и на мастере. Если создать там такие же хардлинки во время даунтайма, получится… При запуске rsync мастер должен быть выключен.
Николай Самохвалов: Если есть несколько реплик, даунтайм увеличится, нужно ещё за этим последить.
Сергей Бурладян: Да.
Николай Самохвалов: Делаете вручную или автоматизируете?
Сергей Бурладян: Подготавливаемся к задаче, готовим набор команд, которые выполнить надо, и выполняем их. Вручную.
Николай Самохвалов: Скажите о каких-то подводных камнях, с которыми столкнулись при работе с Postgres, вещи, которые хотелось бы улучшить, проблемы, баги, недавно найденные.
Сергей Бурладян: Находим баги периодически в Postgres, потому что у нас нагрузка серьезная. Пару дней назад выяснили, что, оказывается, планы в триггере не инвалидируются при изменении таблицы. Есть триггер, сессия, она работает, потом мы делаем alter table, меняем колонку, а триггер не видит изменений, начинает падать с ошибками. Приходится после alter колонки в той же транзакции альтерить хранимку, чтобы сбросила свой кэш. Еще находили баги в вакууме. Про кэш не репортили.
Николай Самохвалов: Может быть, не как баг, а как…
Сергей Бурладян: Можно об этом написать в рассылку.
Николай Самохвалов: Нужно.
Сергей Бурладян: Согласен.
Николай Самохвалов: Бывают ли у вас дедлоки, как боретесь?
Сергей Бурладян: Бывают. Смотрим места, в которых появляются, пытаемся решить их.
Николай Самохвалов: Мониторинг об этом докладывает?
Сергей Бурладян: Да, в логах видно, что произошел дедлок.
Николай Самохвалов: Но логи же не будешь каждый день читать в таком количестве. Скажи, ну вот например, за последнюю минуту 10 дедлоков, смс получишь?
Константин Евтеев: Нет, но выявятся сразу. Как с ними бороться? Дедлоки, которые выявляет Postgres, один из запросов будет убит. Причем какой — неизвестно. Бороться с ними — брать блокировки в одном и том же порядке. Сортировку при взятии блокировок нужно взять. Или идти от более частного объекта к общим отсортированным.
Но более страшные дедлоки — когда их Postgres не может детектить. Если у вас, допустим, микросервисная архитектура, вы открываете транзакцию, берете lock на ресурс, и дальше приложение делает запрос в другой сервис, а он неявно обратится к этой же базе и попробует взять lock на этот же ресурс. В таком случае это будет вечный дедлок, который никак не задетектится. Дальше только по таймауту, возможно, отвалятся.
Дмитрий Вагин: Еще по поводу смс с десятью дедлоками. Если Deadlock возникает, одна транзакция отваливается, если это как-то аффектит прод, там вывалится исключение, ошибка, и в sentry создастся ошибка, она придет потом на почту. В принципе, все видится.
Николай Самохвалов: Придут последствия. Есть вопрос, который хотелось подробнее осветить. Как делаете бэкапы? Какие инструменты используете, как проверяете, в каком объеме хранится, насколько можете взять назад бэкап и восстановиться?
Сергей Бурладян: Делаем их с помощью pg_basebackup, вокруг него есть наша обвязка в виде скриптика. Используем Point-in-Time Recovery (PITR) у нас идет постоянное архивирование WAL-файлов, и мы можем восстановиться на любую точку из прошлого. Дополнительно используется задержка проигрывания WAL на одном из standby на 12 часов.
Николай Самохвалов: Был доклад на эту тему в прошлом году.
Сергей Бурладян: Да.
Николай Самохвалов: Интересующиеся могут найти видео, PDF, статью. Мы сможем восстановить бэкап месячной давности?
Константин Евтеев: Да.
Сергей Бурладян: Костя имеет в виду, что наш бэкап баз, который мы делаем, еще бэкапит дополнительно отдел DevOps, складывают у себя в хранилище. Они бэкапят с помощью Bareos, раньше был Bacula. Они берут наши бэкапы и складывают у себя отдельно.
Николай Самохвалов: Сколько места занимают?
Сергей Бурладян: Наши, по-моему, около 60 ТБ. Сколько занимают потом эти бэкапы у DevOps, не знаю.
Николай Самохвалов: Утилиты типа wal-e рассматривали?
Сергей Бурладян: Нет, у нас собственный скрипт. Знаем, что он делает, он простой, просто архивирует валы к нам в архив.
Николай Самохвалов: Пришла просьба выложить твою версию скрипта, этого компактора. Можешь это сделать?
Сергей Бурладян: Думаю, что могу. Мы планируем выложить до HighLoad нашу пропатченую версию Londiste на GitHub, и, возможно, этот скриптик тоже.
Николай Самохвалов: Присылай мне, я тоже твитну.
Теперь тема, которую невозможно обойти стороной — это поиск. Как у вас всё устроено, в том числе фуллтекст, что используете? Почему опять слово «зоопарк» тут вылезает?
Константин Евтеев: Полнотекстовый поиск в Postgres на текущий момент мы не используем. До недавнего времени мы использовали его, но в связи с тем, что достаточно большие объемы данных и большое количество запросов, соответственно, мы упираемся в CPU, мы CPU bound. Но у нас есть отличная команда «Sphinx».
Николай Самохвалов: Если CPU bound, почему бы не раскидать по нескольким машинкам, зашардить?
Константин Евтеев: Мы использовали его для очень узкоспецифичной задачи, когда там был очень небольшой объем данных. Мы поняли, что объем будет расти, после этого конкретно эту задачу перенесли на сторону Sphinx, который у нас был давно.
Николай Самохвалов: На сколько шард?
Константин Евтеев: Если не ошибаюсь… Лучше не буду говорить цифру.
Николай Самохвалов: Примерно.
Константин Евтеев: Есть статьи, и расшифровки, в том числе на Хабре, и видео с HighLoad, лучше посмотреть там. Я знаю, как мы доставляем данные в эти поисковые подсистемы.
Данные, которые необходимо доставить в Sphinx, если мы говорим про выдачу активных объявлений на сайте, мы материализуем и с помощью Londiste отправляем на отдельную машину, где в свою очередь, эти данные мы, наоборот, берем и раскладываем на много кусочков, то есть, грубо говоря, партицирование идет. Далее во много пото