PostgreSQL проти Percona Server: операції — що простіше запускати о 3:00

Було корисно?

03:07. Тріпер сповістив. Додаток «падає», канал виконання «працює», і хтось у всіх великих літерах друкує
«ми втратили дані?». Тут архітектурні бали не рахуються.
Важливі бали — за відновлення сервісу і розуміння, чому можна довіряти.

Це практичне порівняння експлуатації PostgreSQL і Percona Server (сумісного з MySQL) на випадок, коли ви втомлені,
під тиском і радіус ураження реальний. Не про те, що «краще». А про те, що простіше оперувати о 3:00,
з тими ручками, логами і шляхами відновлення, які реально існують у продакшені.

Що насправді означає «простішe о 3:00»

«Простіше» — це не менше функцій. Це менше невідомих у шляху відмови. О пів на четверту вам потрібно:
(1) чітка видимість того, що зламалося, (2) одна-дві надійні ручки відновлення,
(3) передбачувана поведінка під навантаженням, і (4) резервні копії, які відновлюються з першого разу.

PostgreSQL і Percona Server обидва можна експлуатувати добре. Але вони схильні карати за різні помилки.
PostgreSQL карає за недогляд (autovacuum, пухкі таблиці, погані запити, що виллються на диск).
Percona Server карає за розмиті припущення щодо реплікації та настановний «це ж просто MySQL» підхід, коли підсистема InnoDB
тихо волає.

Моя упередженість, на базі інцидентів: PostgreSQL часто простіший для правильного відновлення, бо семантика
стійкості явна, а реплікація узгоджена. Percona Server часто простіший для швидкого масштабування, бо його
екосистема (плюс інструменти Percona) робить стандартні MySQL-патерни простими — доки ви не натрапите на тонкий дрейф реплікації,
плутанину з GTID або фантазії про multi-writer.

Ще одна заслужена упередженість: база даних, для якої у вас вже є хороші runbook’и, простіша. «Знайоме» перемагає «краще»
в моменті, тому операційна зрілість важить більше, ніж бенчмарки.

Один короткий жарт, як того вимагають правила: О 3:00 кожна база даних — розподілена система, бо ваші колеги розподілені по часових поясах і думках.

Цікаві факти й історичний контекст (частини, що пояснюють шрами)

  • Походження PostgreSQL: PostgreSQL виросла з проєкту POSTGRES в UC Berkeley у 1980-х, і її ДНК «робити правильно» видно в семантиці транзакцій і розширюваності.
  • Рання «багажність» MySQL за замовчуванням: раніше MySQL поставлявся з нетранзакційними налаштуваннями за замовчуванням (наприклад, MyISAM), що навчило покоління ставитись до стійкості як до опціональної. InnoDB змінив правила гри, але культурні відлуння лишилися.
  • Історія появи Percona Server: Percona створила дистрибутив навколо MySQL з інструментами моніторингу та покращення продуктивності, бо операторам потрібна була краща видимість, ніж дав upstream.
  • WAL проти binlog: write-ahead log (WAL) PostgreSQL фундаментальний для безпеки після збою й реплікації. Бінлог MySQL (binlog) одночасно — паливо для реплікації і логічна історія — потужна річ, але вона може стати джерелом дрейфу, якщо ставитись до неї легковажно.
  • MVCC скрізь, але не однакове: обидві системи використовують ідеї MVCC, але autovacuum у PostgreSQL — це операційний обов’язок першого класу, тоді як поведінка undo/redo і purge в InnoDB проявляється як «history list length» або проблеми з undo tablespace.
  • Еволюція реплікації: реплікація MySQL починалася як statement-based, потім row-based, потім mixed. Та історія важлива, бо звички зі statement-based ще просідають в припущеннях про детермінізм.
  • Зрілість фізичної реплікації PostgreSQL: streaming replication і replication slots зробили безперервне архівування і репліки більш надійними, але ввели новий footgun: слоти можуть зберігати WAL вічно, якщо ви їх забули.
  • Вплив інструментів резервного копіювання Percona: XtraBackup став другом оператора для гарячих фізичних бекапів у MySQL-світі, особливо коли логічні дампи були занадто повільні. Але він також створив розрив «бекап пройшов, а відновлення… можливо», якщо вправи з відновлення не практикуються.
  • Поміркованість конфігурацій за замовчуванням: значення за замовчуванням у PostgreSQL навмисно консервативні; треба налаштувати пам’ять і поведінку checkpoint для реальних навантажень. Значення MySQL теж покращилися, але ви все ще побачите продакшн-системи з небезпечно великими InnoDB buffer і ризиковими налаштуваннями flush, бо хтось гнався за графіком.

Операційні ментальні моделі: як кожна база даних ламається

PostgreSQL: «усе добре, доки autovacuum теж добре»

PostgreSQL зазвичай ламається в способи, які можна діагностувати: велике навантаження, довгі запити, конфлікти блокувань, I/O wait,
сплески checkpoint або блоут, що робить усе повільнішим. Коли воно падає, відновлення зазвичай детерміністичне:
відтворити WAL — і повернутися. Складність не в аварійному відновленні; складність — це залишатися поза «повільною спіраллю»,
де блоут і погані плани призводять до більшого I/O, що дає довші запити, що дає більше мертвих кортежів і гірший vacuum.

Ручки на 3AM в PostgreSQL зазвичай: скасувати збіг запросів, зменшити конфлікти блокувань, виправити погані індекси/плани,
налаштувати пам’ять і checkpoint, і перевірити здоров’я WAL-архівування. Це система, що винагороджує
нудну операційну гігієну.

Percona Server: «реплікація проста, доки не стає складною»

Percona Server успадкував операційну модель MySQL: один primary (або «source»), репліки («replica»/«slave»),
асинхронна реплікація і набір опцій. Percona додає видимість (розширення performance schema,
сумісність з Percona Toolkit і операційні патчі залежно від версії).

О о 3:00 ваш найгірший ворог не завжди «база даних впала». Це «база дaних працює, але неконсистентна»,
або «репліки відстають», або «ми зробили failover і тепер записи йдуть не туди».
InnoDB також може застрягти в станах, коли він живий, але фактично заблокований на I/O, під тиском redo log або через довгі транзакції, що перешкоджають purge.

Одна цитата (парафраз ідеї), на вимогу: парафраз ідеї: у reliability engineering надія — не стратегія; системам потрібні зворотні петлі і протестоване відновлення. — натхненна практикою SRE.

План швидкої діагностики (перший/другий/третій)

Перший: це CPU, пам’ять чи I/O?

  • Перевірте завантаження і насичення: високе load не означає, що CPU — проблема. Це може бути I/O wait або хаос у черзі runnable.
  • Подивіться на I/O wait: якщо диск насичений, тонування запитів не допоможе, поки ви не зупините кровотечу (тротлінг, kill зловмисників, додати ресурс, зменшити checkpoint/flush сплески).
  • Перевірте тиск пам’яті: свопінг на хості бази даних — це повільна відмова.

Другий: чи заблоковані ми замками або чекаємо на зберігання?

  • PostgreSQL: знайдіть blocking PIDs, довгі транзакції і autovacuum, що застрягли за блоками. Якщо є реплікація, перевірте WAL sender/receiver і backlog слотів.
  • Percona Server: перевірте активні транзакції, deadlocks, InnoDB row lock waits і стан реплікаційних ниток. Переконайтеся, що ви пишете на очікуваний primary.

Третій: чи система розходиться (реплікація, корупція або часткова відмова)?

  • Затримка реплікації змінює вашу реакцію на інцидент. Якщо репліка відстає на 30 хвилин, failover може призвести до втрати даних.
  • Перевірте журнали помилок на CRC помилки, fsync failures, повний диск або помилки архівування redo/WAL. Це не «пізні» проблеми.
  • Підтвердіть, що резервні копії придатні перед тим, як робити руйнівні зміни. В обох екосистемах надто легко робити припущення.

Практичні завдання для операцій з командами (і що вирішувати за результатами)

Це ті команди, які ви справді запускаєте, коли світ горить. Кожне завдання включає:
команду, що означає вивід, і яке рішення ви приймаєте далі.

Завдання 1 (хост): підтвердити I/O wait проти насичення CPU

cr0x@server:~$ iostat -xz 1 3
Linux 6.5.0 (db01)  12/30/2025  _x86_64_  (16 CPU)

avg-cpu:  %user   %nice %system %iowait  %steal   %idle
          12.31    0.00    4.88   35.22    0.00   47.59

Device            r/s     w/s   rkB/s   wkB/s  await  aqu-sz  %util
nvme0n1         220.0   780.0  8800.0 54000.0   18.4    7.12   98.7

Значення: %iowait високий і пристрій практично на 100% завантажений. Ви I/O-bound, а не CPU-bound.
Рішення: припиніть створювати більше I/O: вбийте найгірші запити, призупиніть батчові роботи, зменшіть тиск checkpoint (Postgres) або flush (InnoDB), і перевірте на disk-full та помилки RAID/NVMe.

Завдання 2 (хост): виявити своп і тиск пам’яті

cr0x@server:~$ free -m
               total        used        free      shared  buff/cache   available
Mem:           64000       54000        1200         600        8800        6200
Swap:           8192        3900        4292

Значення: swap використовується. На хості бази даних це зазвичай самонанесена шкода.
Рішення: зменшіть споживачів пам’яті зараз (шторм підключень, занадто великі work_mem/sort буфери, надто великий buffer pool). Якщо не можете — перемістіть навантаження з вузла або додайте RAM. Свопінг плюс високий I/O wait — класичний коктейль для відмови.

Завдання 3 (PostgreSQL): побачити, що виконується і що чекає

cr0x@server:~$ psql -XAtc "select pid, usename, state, wait_event_type, wait_event, now()-query_start as age, left(query,80) from pg_stat_activity where state <> 'idle' order by age desc limit 10;"
9231|app|active|Lock|transactionid|00:12:33.18291|update orders set status='paid' where id=$1
8120|app|active|IO|DataFileRead|00:09:10.09121|select * from order_items where order_id=$1
...

Значення: у вас є очікування блокування (transactionid) і I/O очікування (DataFileRead). Найстаріший запит ймовірно блокує інших.
Рішення: знайдіть блокер і вирішіть, чи скасовувати/завершувати його. Також перевірте, чи I/O wait системний (див. iostat) або це один поганий запит/індекс.

Завдання 4 (PostgreSQL): швидко знайти блокери

cr0x@server:~$ psql -XAtc "select blocked.pid as blocked_pid, blocker.pid as blocker_pid, now()-blocker.query_start as blocker_age, left(blocker.query,80) as blocker_query from pg_locks blocked join pg_locks blocker on blocker.locktype=blocked.locktype and blocker.database is not distinct from blocked.database and blocker.relation is not distinct from blocked.relation and blocker.page is not distinct from blocked.page and blocker.tuple is not distinct from blocked.tuple and blocker.virtualxid is not distinct from blocked.virtualxid and blocker.transactionid is not distinct from blocked.transactionid and blocker.classid is not distinct from blocked.classid and blocker.objid is not distinct from blocked.objid and blocker.objsubid is not distinct from blocked.objsubid and blocker.pid <> blocked.pid join pg_stat_activity blocked_act on blocked_act.pid=blocked.pid join pg_stat_activity blocker on blocker.pid=blocker.pid where not blocked.granted and blocker.granted limit 5;"
9231|7011|00:48:02.01123|alter table orders add column foo text

Значення: ALTER TABLE утримує блоки 48 хвилин. Це може заморозити ваш додаток.
Рішення: завершити DDL, якщо це небезпечно, а потім встановити політику: online schema changes, lock timeouts і вікна для DDL.

Завдання 5 (PostgreSQL): перевірити затримку реплікації і backlog WAL

cr0x@server:~$ psql -XAtc "select application_name, state, sync_state, pg_size_pretty(pg_wal_lsn_diff(sent_lsn, replay_lsn)) as byte_lag from pg_stat_replication;"
replica01|streaming|async|512 MB

Значення: репліка відстає приблизно на 512MB WAL. Це може бути секундами або хвилинами залежно від швидкості записів.
Рішення: не робіть failover наосліп. Перевірте, чи затримка зменшується. Якщо зростає — проблема вниз по ланцюгу (мережа, диск, replay).

Завдання 6 (PostgreSQL): перевірити слоти реплікації на утримання WAL

cr0x@server:~$ psql -XAtc "select slot_name, active, pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(), restart_lsn)) as retained from pg_replication_slots;"
logical_slot_1|f|97 GB

Значення: неактивний logical slot утримує 97GB WAL. Disk-full тепер запланований інцидент.
Рішення: або знову включити споживача, або видалити слот, якщо він справді мертвий. Додайте моніторинг/алерти на утримання слотів.

Завдання 7 (PostgreSQL): перевірити тиск checkpoint

cr0x@server:~$ psql -XAtc "select checkpoints_timed, checkpoints_req, round(checkpoint_write_time/1000.0) as write_s, round(checkpoint_sync_time/1000.0) as sync_s from pg_stat_bgwriter;"
120|980|8420|3110

Значення: checkpoints_req (980) набагато більше за timed (120). Система змушує часті контрольні точки, зазвичай через обсяг WAL і малий max_wal_size.
Рішення: збільшити max_wal_size і налаштувати checkpoint_completion_target; потім перевірити, чи сховище витримає стабільний writeback. Не «виправляйте» це вимкненням fsync, якщо не плануєте змінити кар’єру.

Завдання 8 (Percona Server): ідентифікувати primary і стан реплікації

cr0x@server:~$ mysql -NBe "SHOW SLAVE STATUS\G" | egrep "Slave_IO_Running|Slave_SQL_Running|Seconds_Behind_Master|Last_SQL_Error"
Slave_IO_Running: Yes
Slave_SQL_Running: No
Seconds_Behind_Master: NULL
Last_SQL_Error: Error 'Duplicate entry' on query. Default database: 'app'. Query: 'INSERT INTO ...'

Значення: IO thread працює, SQL thread зупинено, lag невідомий. Репліка зламана, не просто відстає.
Рішення: не просувайте цю репліку. Виправте реплікацію (skip/repair з великою обережністю) або відновіть заново. Також діагностуйте, чому виникла дублікатна помилка — часто це неправильно налаштований multi-writer або недетерміновані запити.

Завдання 9 (Percona Server): підтвердити режим GTID і безпеку фейловера

cr0x@server:~$ mysql -NBe "SHOW VARIABLES LIKE 'gtid_mode'; SHOW VARIABLES LIKE 'enforce_gtid_consistency';"
gtid_mode	ON
enforce_gtid_consistency	ON

Значення: GTID увімкнено і enforce_gtid_consistency увімкнено. Це робить інструменти фейловера і перенацелення реплік чистішими.
Рішення: якщо ви робите запланований/незапланований фейловер, віддавайте перевагу топологіям з GTID. Якщо вимкнено, очікуйте ручних позицій binlog і більшого ризику о 3AM.

Завдання 10 (Percona Server): прочитати стан двигуна InnoDB щодо блокувань і purge

cr0x@server:~$ mysql -NBe "SHOW ENGINE INNODB STATUS\G" | sed -n '1,120p'
...
TRANSACTIONS
------------
Trx id counter 123456789
Purge done for trx's n:o < 123450000 undo n:o < 0 state: running but idle
History list length 987654
LIST OF TRANSACTIONS FOR EACH SESSION:
---TRANSACTION 123456111, ACTIVE 1865 sec
...

Значення: history list length величезний; purge не встигає, часто через довгі транзакції.
Рішення: знайдіть і завершіть довгі транзакції (або виправте поведінку додатку). Інакше undo росте, продуктивність деградує, і ви закінчите «оптимізацією» через перезапуск — тобто вимиканням і ввімкненням з додатковими кроками.

Завдання 11 (Percona Server): виявити дедлоки і вирішити, що вбивати

cr0x@server:~$ mysql -NBe "SHOW ENGINE INNODB STATUS\G" | egrep -n "LATEST DETECTED DEADLOCK|TRANSACTION|WAITING FOR THIS LOCK"
2345:LATEST DETECTED DEADLOCK
2361:*** (1) TRANSACTION:
2388:*** (1) WAITING FOR THIS LOCK TO BE GRANTED:
2410:*** (2) TRANSACTION:

Значення: дедлоки відбуваються. InnoDB зазвичай вирішує їх, відкотивши одну транзакцію.
Рішення: якщо дедлоки раптово зросли, шукайте нові шляхи коду або відсутні індекси. Вбивати випадкові потоки — рідко вирішення; виправлення патернів доступу — краще.

Завдання 12 (MySQL/Percona): підтвердити тиск на buffer pool і hit rate

cr0x@server:~$ mysql -NBe "SHOW GLOBAL STATUS LIKE 'Innodb_buffer_pool_reads'; SHOW GLOBAL STATUS LIKE 'Innodb_buffer_pool_read_requests';"
Innodb_buffer_pool_reads	9876543
Innodb_buffer_pool_read_requests	1234567890

Значення: buffer pool reads значні; порівнюйте темпи зростання в часі. Якщо читання з диска швидко зростають, кеш не тримає робочий набір.
Рішення: або збільшіть buffer pool (якщо RAM дозволяє), зменшіть робочий набір (індекси, виправлення запитів), або перестаньте вважати диски «добре, бо переважно читається».

Завдання 13 (PostgreSQL): знайти топ-запити за загальним часом (pg_stat_statements)

cr0x@server:~$ psql -XAtc "select round(total_exec_time) as total_ms, calls, round(mean_exec_time,2) as mean_ms, left(query,90) from pg_stat_statements order by total_exec_time desc limit 5;"
983412|1203|817.33|select * from events where user_id=$1 order by created_at desc limit 50
...

Значення: топ-запит за total-time частий і досить повільний. Це ваш ROI.
Рішення: додайте потрібний індекс, виправте сортування/пагінацію або змініть форму запиту. Потім перевірте знову. Не налаштовуйте kernel-параметри, поки не виправите очевидний запит.

Завдання 14 (PostgreSQL): перевірити сигнали блоуту (мертві кортежі) та autovacuum

cr0x@server:~$ psql -XAtc "select relname, n_live_tup, n_dead_tup, round(100.0*n_dead_tup/nullif(n_live_tup+n_dead_tup,0),2) as dead_pct from pg_stat_user_tables order by n_dead_tup desc limit 5;"
orders|12000000|4800000|28.57
events|90000000|11000000|10.89

Значення: велика кількість мертвих кортежів. Якщо autovacuum не встигає, продуктивність запитів деградує, а індекси роздуваються.
Рішення: налаштуйте autovacuum для гарячих таблиць, розгляньте ручний VACUUM (або VACUUM FULL у контрольовані вікна) і виправте патерни транзакцій, що перешкоджають vacuum (довгі транзакції).

Завдання 15 (Percona Server): побачити поточні запити і їх час

cr0x@server:~$ mysql -NBe "SHOW FULL PROCESSLIST;" | head
12345	app	10.0.2.15:44210	appdb	Query	35	Sending data	SELECT ... FROM big_table ...
12346	app	10.0.2.16:55122	appdb	Sleep	1200		NULL

Значення: запит виконується 35 секунд і знаходиться в стані «Sending data» (часто сканування або сортування). Є також довгі сплячі підключення.
Рішення: вбити або оптимізувати порушників, обмежити max connections або використовувати пулінг, і дослідити, чому підключення лишаються сплячими (витоки в додатку, неправильний пулінг).

Завдання 16 (хост): підтвердити повний диск перед тим, як ганяти привидів

cr0x@server:~$ df -h /var/lib
Filesystem      Size  Used Avail Use% Mounted on
/dev/nvme0n1p3  900G  890G   10G  99% /var/lib

Значення: фактично у вас закінчується диск. І PostgreSQL, і InnoDB поводяться погано тут, тільки різними діалектами болю.
Рішення: звільніть місце негайно (старі логи, старі бекапи, обережно ротовані WAL/binlogs), розширіть том, а потім розберіться з політиками ретенції.

Резервні копії та відновлення: 3AM-правда

PostgreSQL: резервні копії прості, відновлення — де ви заробляєте довіру

PostgreSQL дає два основні режими бекапу:
логічний (pg_dump) і фізичний (base backup + WAL). Логічні бекапи портативні,
повільні в масштабі і чудові для міграцій. Фізичні бекапи швидші для відновлення і підходять для DR,
але потребують архівування WAL (або безперервного стримінгу) для можливості відновлення в певний момент часу.

Операційна простота в Postgres концептуальна: якщо ви розумієте WAL, ви розумієте аварійне відновлення,
реплікацію і PITR. Це одна ментальна модель з різними інструментами.

Операційні footgun’и також послідовні: якщо архівування WAL ламається — PITR ламається. Якщо replication slots утримують WAL,
диски заповнюються. Якщо ви не практикуєте відновлення — ваш «бекап» буде лише заспокійливою папкою брехні.

Percona Server: XtraBackup чудовий, але поважайте pipeline відновлення

Оператори Percona Server зазвичай використовують:
логічні дампи (mysqldump/mysqlpump) і фізичні гарячі бекапи (XtraBackup).
XtraBackup швидкий і зазвичай правильна відповідь для великих датасетів. Він також вводить операційну реальність:
фізичний бекап — це не «один файл». Це дерево директорій, метадані і крок prepare/apply-log.
Якщо шлях відновлення не скриптований і не відрепетирований, він підведе у момент, коли потрібен.

PITR в MySQL/Percona часто покладається на binlogs плюс базовий бекап. Це потужно, але операційно більш хитко:
ви маєте відстежувати ретенцію binlog, формат binlog, режим GTID і послідовність застосування.

Хто простіший о 3:00 для відновлення?

Якщо ваша організація дисциплінована щодо архівації WAL і ви тестуєте відновлення, то відновлення PostgreSQL зазвичай простіше і передбачуваніше.
Якщо ваша організація вже глибоко в MySQL-світі і у вас відлагоджена автоматизація відновлення XtraBackup, Percona може бути надзвичайно гладким.
Рішучий фактор — не наявність інструменту. Рішучий фактор — чи є у вас відрепетировані, документовані runbook’и відновлення і тестові відновлення.

Реплікація й фейловер: що ламається і як голосно

Реплікація PostgreSQL: менше режимів, менше дивних сюрпризів

Streaming replication у PostgreSQL — фізична: репліки відтворюють WAL. Це робить її узгодженою з тим, як primary
фіксує зміни. Логічна реплікація існує, але більшість операційних топологій фейловера покладаються на фізичну реплікацію.

Операційно це хороша новина: менше форматів реплікації, менше проблем із «недетермінованими» інструкціями.
Вам все одно треба керувати затримкою реплікації, розривами мережі і інструментами промоції. Але типовий 3AM-режим відмови
очевидний: «репліка відстає» або «репліка не встигає через повільний диск».

Реплікація Percona Server: гнучка, зріла і легко неправильно зрозуміти

Реплікація MySQL значно покращилася за останні роки: GTID, row-based replication, опції semi-sync,
багатопотокові applier’и. Percona Server відстежує цю екосистему і часто дає більше інструментів видимості.

Небезпека о 3:00 — людська: люди вважають, що асинхронна реплікація «майже синхронна», або що вони отримають read-after-write
консистентність через репліки, або що фейловер — просто зміна DNS. Потім вони дізнаються, що затримка реплікації — це не пропозиція; це фізика.

Якщо ви використовуєте Percona Server, станьте релігійними щодо:
row-based binlog формату, GTID у всьому стеку, і автоматизованого фейловера, який також фейнсує старий primary.
Частина фейнсингу — як уникнути split-brain записів — один з найдорожчих класів інцидентів.

Налаштування продуктивності: безпечні зміни проти жалю

Налаштування PostgreSQL, що реально допомагає о 3:00

Інциденти продуктивності в PostgreSQL часто пов’язані з планом виконання запиту і I/O. Безпечні ручки:
обмежити кількість підключень (пулінг), виправити відсутні індекси, і налаштувати checkpoint/WAL, щоб уникнути періодичних хвиль записів.
Параметри пам’яті як work_mem можуть допомогти, але вони також частий фактор самонашкодження: це на одну сортування, на один хеш, на сесію.

Найбільш «безпечні о 3:00» зміни — це ті, що зменшують навантаження без зміни коректності: скасувати запит, додати відсутній індекс
(обережно, можливо concurrent), налаштувати statement timeouts і зменшити конкурентність.

Налаштування Percona Server, що реально допомагає о 3:00

InnoDB зазвичай в центрі уваги. Безпечні ручки:
правильно розмірити buffer pool, тримати innodb_flush_log_at_trx_commit і sync_binlog у надійних налаштуваннях, якщо у вас немає письмового прийняття ризику,
і переконатися, що ви не задихаєтесь через занадто багато підключень або overhead планувальника потоків.

Багато «виграшів продуктивності» в MySQL — насправді торгівля стійкістю. Виглядає гарно до того моменту, як буде відключення живлення або kernel panic.
Якщо ви мусите змінювати налаштування стійкості — робіть це як усвідомлене бізнес-рішення, а не як північний експеримент.

Другий короткий жарт, як потрібно: Вимкнути fsync — це як зняти датчик диму, бо він голосний — короткочасний спокій, а потім пізнавальний досвід.

Зберігання та файлові системи: I/O, стійкість і несподіванки

Обидві бази даних — це механізми зберігання з власними уподобаннями. Якщо ваше сховище бреше, вони вірно зафіксують цю брехню.
Питання простоти о 3:00 часто зводиться до «як база поводиться під I/O-болем?»

PostgreSQL під I/O-болем

  • Сплески checkpoint можуть створювати періодичні латентні бурі, якщо не налаштовані.
  • Autovacuum може стати або героєм (запобігаючи блоуту), або лиходієм (якщо він конкурує з робочим навантаженням на повільних дисках).
  • WAL архівування — жорстка залежність для PITR; зламаний архів — це інцидент, а не попередження.

InnoDB під I/O-болем

  • Тиск redo log і поведінка флешу можуть викликати зависання, якщо лог-підсистема обмежена.
  • Довгі транзакції перешкоджають purge і створюють undo/історію, що потім викликає більше I/O.
  • Флашинг брудних сторінок і чекпойнтинг можуть викликати крах пропускної здатності при насиченні дисків.

Порада оператору: обирайте битви зі сховищем

Якщо ви на хмарному блочному сховищі — тестуйте ваші найгірші IOPS, а не середні. Якщо на локальному NVMe —
плануйте відмову пристрою і поведінку відновлення. І в будь-якому випадку: моніторьте затримку диска, глибину черги і заповненість файлової системи.
Інциденти «диск повний» стидно поширені, бо проблеми зі сховищем тихі, поки не стануть гучними.

Три корпоративні міні-історії (реалістично, анонімізовано, технічно точно)

1) Інцидент спричинений неправильним припущенням: «читання з реплік завжди свіжі»

Середня SaaS-компанія використовувала Percona Server з одним primary і двома репліками. В архітектурному документі було:
«Читання — до реплік, записи — до primary.» Балансувальник навантаження сумлінно дотримувався правила.
Ніхто не записав друге правило: «Деякі читання мають бути узгоджені з останнім записом користувача.»

Запуск продукту збільшив записи. Затримка реплікації піднялась з «зазвичай незначної» до «помітної».
Стали приходити тікети: користувачі зберігали налаштування, оновлювали сторінку, і налаштування поверталися. Додаток не втратив дані.
Він читав старі дані з реплік. З точки зору користувача це було не відрізнити від втрати даних.

О о 3:00 on-call робив звичні кроки: перезапустити pod’и додатку, збільшити підключення до БД, масштабувати репліки.
Це все погіршило ситуацію. Більше підключень збільшило конфлікт записів на primary, що підвищило lag і невідповідність.
Система поводилась точно як запроектовано — просто не як припускали.

Виправлення було нудним і ефективним. Вони проксували «read-after-write» шляхи на primary (або використали session stickiness),
додали маршрутизацію, чутливу до lag (не надсилати запити на репліку вище порога), і чітко визначили у коді:
«eventual consistency тут прийнятна, а там — ні.» Також впровадили фейнсинг на основі GTID для фейловера,
бо інцидент відкрив, наскільки байдуже ставлення до фейловера.

Операційний урок: реплікація Percona може бути надійною, але вона не врятує вас від трактування async реплікації як магії.
У Postgres та сама фізика, але команди частіше проєктують навколо неї раніше, бо затримка streaming replication часто моніториться як «WAL bytes behind», що відчувається конкретніше, ніж «секунд за відставанням master».

2) Оптимізація, що обернулась проти: «більше пам’яті для швидших сортувань»

Команда платформи даних використовувала PostgreSQL для аналітичних навантажень на спільному кластері. Було багато повільних запитів
з великими сортами і хешами. Хтось запропонував значно підвищити work_mem. В тестах стало краще.
Графіки посміхнулися. Зміни розгорнули.

Через дві години primary почав свопити. Латентність вибухнула. Autovacuum відстав.
Потім з’явився lag реплікації, бо WAL replay на репліці відставав через конкуренцію диска.
Канали інцидентів заповнились звичними підозрами: «мережа?», «помилка ядра?», «нас DDoS-лять?»

Що сталося просто: work_mem — це на операцію. При конкурентності велике work_mem множиться
у реальне споживання пам’яті. База не «використовує більше пам’яті для одного запиту». Вона використовує більше пам’яті для сотень.
Як тільки система почала свопити, вона провела ніч у трясці, витрачаючи дорогоцінний I/O на підтримку overcommit пам’яті.

Відкат виправив негайний біль. Справжнє покращення було тоншим: вони додали потрібні індекси,
зменшили конкурентність для важких звітів і застосували timeouts та черги ресурсів (на рівні планувальника додатку),
щоб один шумний робочий набір не захопив весь вузол.

Операційний урок: зміни, що виглядають безпечними в ізоляції, можуть бути катастрофічними під навантаженням. У Postgres
налаштування пам’яті підступно гострі. В екосистемі Percona/MySQL аналогічний відкат часто — «зробити buffer pool величезним»,
забувши про filesystem cache, запас на OS або потреби при бекапі/відновленні.

3) Нудна але правильна практика, що врятувала ситуацію: вправи з відновлення і чеклісти просування

Компанія, пов’язана з платежами, працювала на PostgreSQL з жорсткою політикою: щомісячні вправи з відновлення в staging,
і квартальні «promote a replica» game days. Це тип практики, що ніколи не виграє внутрішніх нагород, бо не доставляє фіч.
Це також скорочує інциденти і робить їх менш драматичними, що вкрай нелюбимо у людей, які люблять драму.

Однієї ночі контролер сховища став некоректним і почав повертати переривчасті I/O помилки. PostgreSQL почав логувати
fsync failures. Команда не сперечалась, чи логи «справжні». Їх runbook трактував fsync failures як ризик «stop-the-world».
Вони відфейнсили вузол, просували репліку і перемістили трафік.

Ключовий момент: вони вже знали, що репліку можна просунути чисто, бо робили це неодноразово.
Вони також знали, що PITR-ланцюг цілий, бо вправи відновлення перевіряли WAL архіви і base backups.
Коли керівництво спитало «ми в безпеці?», on-call міг відповісти з доказами, а не оптимізмом.

Операційний урок: практика перетворює інцидент о 3:00 на чекліст. Інструменти PostgreSQL добре підходять для цієї дисципліни,
але такий підхід працює і для Percona — особливо якщо регулярно перевіряти відновлення XtraBackup і процедури застосування binlog.

Поширені помилки: симптом → корінна причина → виправлення

1) Симптом: використання диска постійно зростає в PostgreSQL

Корінна причина: неактивний replication slot утримує WAL, або архівування WAL не працює і WAL накопичується.

Виправлення: перевірити pg_replication_slots, видалити невикористані слоти, полагодити споживача і налаштувати алерти на розмір утримуваного WAL. Перевірити archive_command і дозволи.

2) Симптом: періодичні сплески латентності кожні кілька хвилин (PostgreSQL)

Корінна причина: агресивне чекпоінтування через малий max_wal_size або неправильно налаштовані checkpoint-параметри.

Виправлення: підняти max_wal_size, встановити checkpoint_completion_target ближче до 0.9, переконатися, що сховище витримає рівномірні записи, і підтвердити, що ви не насичуєте I/O.

3) Симптом: запити повільнішають з дня на день; індекси здаються «менш ефективними» (PostgreSQL)

Корінна причина: блоут таблиць і індексів через недостатній vacuum або довгі транзакції, що перешкоджають прибиранню.

Виправлення: налаштувати autovacuum для гарячих таблиць, усунути довгі транзакції і запланувати реіндексацію/VACUUM відповідно.

4) Симптом: репліка показує Seconds_Behind_Master: NULL (Percona/MySQL)

Корінна причина: SQL thread зупинився через помилку (duplicate key, відсутня таблиця, drift схем).

Виправлення: перевірити Last_SQL_Error, вирішити відновити або акуратно відремонтувати. Віддавайте перевагу GTID і узгодженому розгортанню схем, щоб уникати дрейфу.

5) Симптом: «дедлоки зросли» після релізу (Percona/MySQL)

Корінна причина: нові шляхи запитів змінили порядок блокувань або відсутній індекс призвів до ширших блокувань і довшого часу утримання блоків.

Виправлення: проаналізувати логи дедлоків, додати індекси і забезпечити послідовний порядок транзакцій у коді додатку.

6) Симптом: InnoDB зависає з великим history list length (Percona/MySQL)

Корінна причина: довгі транзакції запобігають purge; іноді великі read-транзакції або відкриті idle транзакції.

Виправлення: ідентифікувати й завершити довгі транзакції, встановити розумні таймаути і виправити обробку підключень у додатку. Моніторити history list length.

7) Симптом: після фейловера записи відбуваються на двох вузлах (будь-яка екосистема)

Корінна причина: відсутній фейнсинг; клієнти все ще підключені до старого primary; ризик split-brain.

Виправлення: реалізувати фейнсинг на рівні мережі/балансувальника; забезпечити один-письменник через автоматизацію і health checks; блокувати старий primary від прийому записів.

8) Симптом: бекапи «успішні», а відновлення — ні (будь-яка екосистема)

Корінна причина: бекапи не тестувалися; відсутні WAL/binlogs; неправильні дозволи/шляхи; відсутні ключі шифрування; undocumented кроки відновлення.

Виправлення: запланувати вправи з відновлення, автоматизувати відновлення, перевіряти контрольні суми і трактувати цілі відновлення як виробничі вимоги.

Чеклісти / покроковий план

Чекліст A: триаж о 3:00 (працює для обох)

  1. Підтвердьте обсяг впливу: одна служба чи все? Це латентність, помилки чи коректність даних?
  2. Перевірте насичення хоста: CPU, пам’ять, I/O wait, диск повний.
  3. Перевірте живість бази даних: чи можна підключитись? Чи запити виконуються чи застрягли?
  4. Знайдіть найгірших порушників: найдовші запити, ланцюги очікування блокувань, шторм підключень.
  5. Перевірте стан реплікації перед будь-яким фейловером: lag, зупинені нитки, утримання WAL тощо.
  6. Зупиніть кровотечу: вбийте runaway запити, призупиніть батчі, затротлінгуйте трафік, зніміть навантаження.
  7. Тільки потім налаштовуйте: обережні зміни конфігурації, що зменшують тиск; уникайте торгівлі стійкістю/коректністю.
  8. Прийміть рішення про відновлення: стабілізувати на місці проти фейловера проти відновлення з бекапу.

Чекліст B: PostgreSQL «безпечні кроки для стабілізації»

  1. Знайдіть блокери і, за потреби, завершіть їх (особливо довгі DDL, що утримують блоки).
  2. Скасуйте runaway запити, щоб зменшити I/O і конфлікти блокувань.
  3. Перевірте архівування WAL і утримання слотів, щоб уникнути каскадів disk-full.
  4. Перевірте здоров’я autovacuum для найгарячіших таблиць; налаштуйте по таблиці, якщо потрібно.
  5. Підтвердіть тиск checkpoint; налаштовуйте WAL/checkpoint в спокійне вікно, а не під панікою, якщо альтернатива — це відмова.

Чекліст C: Percona Server «безпечні кроки для стабілізації»

  1. Підтвердьте, який вузол записувальний primary; перевірте, що додаток пише саме туди.
  2. Перевірте реплікаційні нитки; не просувайте зламану репліку.
  3. Перегляньте стан InnoDB: довгі транзакції, purge-pressure, deadlocks.
  4. Зменшіть шторм підключень; розгляньте тимчасові caps або виправлення пулінгу.
  5. Підтвердіть налаштування стійкості перед їх зміною; якщо міняєте — документуйте ризик і план відкату.

Покроково: практикуйте відновлення (річ, що робить 3AM керованим)

  1. Візьміть один production набір бекапів і відновіть його в ізольоване середовище.
  2. Для PostgreSQL: відновіть base backup, відтворіть WAL до цільового часу, запустіть перевірки цілісності (запити + smoke tests додатку).
  3. Для Percona/XtraBackup: відновіть директорію, apply logs (prepare), стартуйте сервер, застосуйте binlogs/GTID для PITR за потреби, валідируйте smoke tests.
  4. Виміряйте час відновлення і задокументуйте як реальний RTO.
  5. Повторюйте, поки не зможете робити це з runbook без імпровізацій.

Поширені запитання

1) Якщо мене цікавлять лише операції о 3:00, чи обрати PostgreSQL?

Якщо ви починаєте з нуля і цінуєте передбачувану семантику відновлення — так, PostgreSQL часто безпечніша ставка.
Але якщо ваша команда вже має міцні MySQL/Percona runbook’и і інструменти — операційна зрілість перемагає теоретичну простоту.

2) Чи складніший Percona Server, ніж «ванільний MySQL», в експлуатації?

Зазвичай ні. Percona Server часто обирають саме тому, що він покращує видимість для операторів і сумісність з інструментами продуктивності.
Складність походить від вибору топології реплікації і налаштувань MySQL, а не від бренду Percona.

3) Хто частіше здивує мене ростом використання диска?

PostgreSQL дивує WAL-ретенцією (особливо replication slots) і блоутом, якщо vacuum занедбаний.
Percona/MySQL дивує ретенцією binlog, ростом undo/history і накопиченням бекапів.
У будь-якому випадку: диск — це SLO першого класу.

4) Який найпростіший надійний підхід до бекапів для кожного?

PostgreSQL: фізичні base backups плюс архівування WAL для PITR, з регулярними вправами відновлення.
Percona: стратегія XtraBackup full/incremental плюс ретенція binlog для PITR, з регулярними вправами відновлення і перевіркою GTID.

5) Що легше безпечно переключати?

Фейловер в PostgreSQL концептуально чистий (promote фізичної репліки), але треба керувати маршрутизацією клієнтів і фейнсингом.
Фейловер у Percona/MySQL може бути плавним з GTID і автоматикою, але split-brain і руйнування реплікації — частіші режими відмови.

6) Який найпоширеніший «self-own» о 3:00 в PostgreSQL?

Додавати довгі транзакції і накопичувати проблеми з vacuum, поки блоут і конфлікти блокувань не перетворяться на відмову.
Друга найпоширеніша — неправильно налаштоване архівування WAL або забуті replication slots, що заповнюють диск.

7) Який найпоширеніший «self-own» о 3:00 в Percona Server?

Ставити асинхронну реплікацію поряд із очікуванням синхронності і просувати неправильну репліку, або робити фейловер без фейнсингу старого primary.
Також: «оптимізація» через торгівлю стійкістю, що потім призводить до втрати даних під час збою.

8) Чи можна зробити будь-яку з них «просто о 3:00» незалежно від вибору?

Так. Рецепт нудний: послідовний моніторинг, тестовані бекапи, стандартизовані процедури фейловера, чітка відповідальність за зміни схеми,
і інцидентний playbook, що починається з перевірок насичення і коректності.

9) Яка з них легша для відладки продуктивності запитів під тиском?

PostgreSQL з pg_stat_statements і чіткими wait events чудово підходить для цільової діагностики.
Percona/MySQL також має міцну інструментацію (performance schema, SHOW ENGINE INNODB STATUS), але команди часто не використовують її повністю.
Легшою є та система, з якою ваша команда практикується щомісяця, а не та, якою ви захоплюєтеся щокварталу.

Практичні кроки далі

Якщо ви вибираєте між PostgreSQL і Percona Server, орієнтуючись на «простоту о 3:00», приймайте рішення на підставі операційної реальності:
навичок вашої команди, автоматизації і толерантності до конкретних режимів відмов.

  • Обрати одну «золоту» топологію (один primary, визначені репліки, визначений метод фейловера) і заборонити довільні варіації.
  • Написати 3AM runbook зараз: перевірки насичення, перевірки блокувань, перевірки реплікації, процедура при повному диску і список «не робити».
  • Практикувати відновлення, поки це не стане рутинним. Якщо його не практикують — це не бекап.
  • Інструментувати реальні ризики: Postgres WAL/slots/vacuum; Percona реплікаційні нитки/GTID/ретенція binlog/InnoDB purge.
  • Фейнсити фейловери, щоб не можна було писати на два примарі одночасно. Це різниця між інцидентом і катастрофою.

І нарешті, будьте чесні щодо того, чого ви хочете: якщо ви прагнете менше рухомих частин і більш узгодженої семантики, PostgreSQL зазвичай спокійніший о 3:00.
Якщо вам потрібна екосистема MySQL з сильними інструментами і ваша організація вже добре її експлуатує — Percona Server може бути так само спокійним, якщо ви поважаєте реплікацію і стійкість.

← Попередня
Програмовані шейдери: коли «графіка» стала програмним забезпеченням
Наступна →
Забута захисна плівка на кулері: найкумедніша причина перегріву

Залишити коментар