Топ ошибок со стороны разработки при работе с PostgreSQL
Intro
В компаниях любого размера бывают проблемы. Откуда они берутся?
Из фич. Начинаем использовать продвинутые фичи, утилиты и прочее. «Хочется взять дежурный пистолет, положить в ящик стола, иногда достать, застрелиться и работать дальше».
Из хранения данных. Когда оно усложняется, больше шансов написать кривой запрос.
Из жизненного цикла. Разработчики пилят, админы настраивают, а улучшать систему некому. База работает с дефолтными конфигами и когда-нибудь ломается.
Наводим порядок
Планирование и мониторинг
Пока проект новый, всё работает быстро. Потом приложение растёт, появляется больше клиентов — приложение тормозит. Где могут быть проблемы:
- Диск занят
pg_xlog
. Разработчик: «а давайте его удалим!». Не надо так. Там журнал транзакций. - Диск занят базами, больше всего в
history_log
. Пытаемся почистить базу запросом, но она не уменьшается, потому что постгрес не отдаёт автоматически место ОС. - Disk I/O. Некто составил запрос на M таблиц в N потоков X тысяч раз в секунду. Результат — сервер пятисотит.
- Тормоза фоновых процессов СУБД.
- Накладные расходы от виртуализации
- Хранилище от китайского noname
- Дефолтные конфиги
- …
Что делать? Мониторить и планировать!
- Сразу на SSD, не раздумывая.
- Схема данных: планировать заранее и писать только нужное. Например, не писать в логи JSON весом в несколько мегабайт.
- Партиционирование
Мониторинг:
- Storage: latency, utilization
- PostgreSQL: подключенные клиенты, ошибки, запросы (statements)
Масштабирование
Типичный разработчик видит БД как строчку конфига и не интересуется тем, как она работает. Это источник проблем.
OLTP-транзакции — быстрые, короткие, лёгкие. OLAP-транзакции — медленные, долгие и тяжёлые.
Те и другие нужно разносить. Вторые мешают первым. Как масштабировать PostgreSQL так, чтобы разнести нагрузку?
- Streaming replication — создаём реплики, в том числе каскадные, на разные реплики вешаем разные запросы. Например, аналитику на отдельную реплику.
- Logical publications, subscriptions. Конкретные наборы таблиц подключаем в соседние базы и их приложения могут ими пользоваться.
- Внешние таблицы (foreigh tables), декларативное партиционирование (declarative partitioning). Несколько баз с разными наборами таблиц. С помощью механизма внешних таблиц объединяем их в одной базе, приложение работает с ней, но на самом деле качает данные из тех нескольких баз.
С чего начать?
- С репликации. Разнести нагрузку на чтение и запись. Пишем в мастер, читаем из реплик. Для аналитических запросов делаем отдельную реплику.
- Балансировка: лучше на стороне приложения, можно DNS round robin, интересно Keepalived и Haproxy, совсем хорошо Patroni, DCS.
Приложения и СУБД-транзакции
Idle transactions приводят к снижению производительности, блокировкам и дедлокам, потом к пятисотым.
Откуда берутся такие транзакции?
Внешний источник:
- Приложение открывает транзакцию
- Потом идёт на какой-то другой источник, встречает там ошибку, падает.
- Profit, транзакция висит, пока её явно не убьют.
Нет обработки ошибок
Человеческий фактор — открыл и забыл.
Что делать:
- Алерты в мониторинге
pg_terminate_backend
- устранять ошибки в приложении
Избегайте пустых транзакций любой ценой!
Велосипедостроение
Фоновая обработка событий. Бизнес хочет необычного. Появляются самописные очереди. От долгих транзакций эти таблицы распухают. Растёт время их обработки, очередь перестаёт работать.
Что делать: использовать Skytools PgQ. Но и в нём есть проблемы:
- Мало документации
- Нужно применять джедайские техники
- Много нужных знаний находятся в почтовых рассылках (mailing lists)
Плюсы PgQ:
- Настроил и забыл.
- Дешевле отдельных брокеров.
Вывод: для каждой задачи сначала ищите инструменты, которые уже изобретены.
Автоматизация
Админы хотят:
- инстансы
- деплой
- конфиги
Разработчики хотят:
- деплой
- миграции катить без ручных вмешательств
Все хотят autofailover! Но всё не так просто:
- Случается split-brain. Потеряли связность сети, fencing’а нет, приложения пишут сразу в два мастера. Так было недавно у GitHub.
- Каскадный failover. Мастер падает, переключаем на второй, который не успел отреплицироваться, он тоже падает, остаётся один сервер, который складывается под нагрузкой.
Как делать failover?
Контейнеры и оркестрация
А что, если развернуть базу в k8s?
- Нужно где-то хранить. Решения есть (CEPH, GlusterFS, DRBD), но они медленные. И нужно поддерживать кластерную файловую систему. Это работает, пока база маленькая, нет требований к производительности и не страшно потерять данные. Т.е. это не работает.
Вывод: использовать для стейджингов и девелоперских машин, но не на проде. Если очень хочется — то использовать local volumes, streaming replication и операторы PostgreSQL.