Реализация восстановления после аварий Сергей Бурладян
Avito• база 2 Тб• 4 сервера: master, standby index, standby site, standby
backup• 4 реплики * 2: сайта, индексации, платежей, дельты
объявлений• sphinx• экспорт в DWH• xrpc (pgq)• xdb: 16 баз, шардинг• 6 часов backup• 4 часа восстановления из backup, 8 часов применения
WAL• простой 10 минут
Master• UPDATE без WHERE
• backup standby, 12 часов задержки применения WAL
– pg9.2: if [ "$ftime" -gt "$apply_time" ]
– pg9.4: recovery_min_apply_delay (commit time)
Master• promote standby
• асинхронная репликация
– теряем часть транзакций
– необходимы процедуры восстановления
• руками сложно, скрипт переключения
Masterpromote создаёт на новом мастере параллельную ветвь времени, а все остальные остаются в старой
• в DWH объявления, которых нет
• xrpc отработал для несуществующих данных
• sphinx выдаёт несуществующие объявления
• sequences начались снова с момента аварии
• логические реплики не работают
MasterНе всё так плохо :-)
• выгрузить в DWH промежуток аварии с запасом в час снова
– 10 минут — мало изменят отчёты, можно игнорировать
• xrpc, повезло:
– геокодинг
– локально для асинхронных вызовов на мастере
– карма
– TODO, "обратные" функции, триггеры типа undo londiste
MasterНе всё так плохо :-)
• sphinx
– сайта, через 10 минут создан с нуля
– backoffice, не критично, зарефрешить последний час, пересоздаётся с нуля раз в месяц
• sequences, открутить важные вперёд на 100 000
– item_id
– user_id
– order_id
• UNDO для логических реплик
Undo• subscriber undo [TICK_ID]
– rewind all tables with it UNDO log
• subscriber add-undo-all
– enable UNDO log on all tables
• subscriber remove-undo-all
– disable UNDO log on all tables
• --enable-undolog
– replay generate UNDO log
Undo• триггер на таблицах subscriber
• undo_log таблица с 'I,'U','D', hstore и tick_id
• получение tick_id с мастера
• все строки во всех наблюдаемых таблицах с tick_id больше мастера — отменить
– INSERT -> DELETE
– UPDATE -> UPDATE
– DELETE -> INSERT
• londiste.completed: last_tick_id = master tick_id
Скрипт переключения• 3 standby — выбираем ближайший к мастеру
• останавливаем другие standby
– pg9.2, pg9.4 можно не перезапускать
• promote
• сдвиг sequences вперёд
• londiste undo
• запускаем standby
• ждём новый timeline на standby
• обновление в DNS
Скрипт переключения• ждём новый timeline на standby
– нет SQL функции
– протокол репликации:
psql -h "$host" -F' ' -c 'IDENTIFY_SYSTEM' \
'dbname=replication replication=true'
systemid | timeline | xlogpos
---------------------+----------+------------
6080348884119699418 | 1 | 1/4E0E42D0
Standby• 3 standby
• всё на мастер с потерей части трафика
• быстрое создание нового
– из архива (1 Gbp, NFS HDD) — 12 часов
– с соседнего standby pg_basebackup (10 Gbps, SSD, мало WAL применять) — 4-6 часа
Sphinx сайта• отдельная реплика для индексации
• данные подготовлены и разложены для быстрой вычитки в sphinx
• реплика: все данные в shared_buffers
• полная вычитка всех активных объявлений и перестроение всего индекса каждые 10 минут
• через 10 минут — все индексы готовы заново!
Экспорт в DWH• отдельная реплика со всеми дельтами всех
объявлений за последние 4 дня
– ручной запуск экспорта с указанием промежутка времени
• архив за полгода
• в крайнем случае — pause standby и заново выгрузить все объявления
Базы-клиенты xrpc• так как xrpc поверх pgq — можно заново проиграть
(redo) часть событий на восстановленной базе
Xdb• бинарная репликация
• 8 машин, standby на соседней
xdb1 -> xdb2, xdb2 -> xdb3, ..., xdb8 -> xdb1
• задержка проигрывания — 4 дня
Реплика• на основе возможностей postgres
– view
– deferred триггер
• подготавливаются данные на мастере в таблице
• londiste реплицирует готовые данные в реплику
Реплика• мастер: набор таблиц с данными для выдачи на сайте
• deferred триггер на каждой, вызывает refresh функцию
• view site_data_v с join по item_id
• таблица site_data_m
• refresh функция:– блокировка item_id– delete from site_data_m where item_id = i_item_id– insert into site_data_m select * from site_data_v where
item_id = i_item_id
• londiste на site_data_m
Реплика• отдельный сервер (* N)
• shared_buffers = sizeof(site_data_m + indexs)
• всё в памяти, fsync=off
• 7000 TPS чтение
• 1000 событий в сек. в очередь на мастере
• londiste (pgq): учёт транзакций, tick_id
Авария реплики• pgq — два или больше потребителя одной очереди
• переключаемся на копию
• упавшую — копируем с рабочей
• заново инициализировать — долго
– реплика сайта — 4 часа
– реплика индексации — 8 часов
• делаем копию с оставшейся реплики
Авария репликиАлгоритм копирования:
• stop londiste
• преподписать к очереди
• pg_dump -f
• pg_restore -j10
• repca: UPDATE londiste.consumer
• master: UPDATE pgq.subscription
• start londiste
• 15 минут!
Авария реплики– pgqadm ticker.conf unregister ОЧЕРЕДЬ 'название londiste
consumer'– pgqadm ticker.conf register ОЧЕРЕДЬ 'название londiste consumer'– pgqadm ticker.conf status; ждём Lag больше, чем у копии
1) pg_dump -Fc -h prod_host --serializable-deferrable dst_db pg_restore -Fc -d dst_db -j10
2) dst: select * from londiste.completed
3) src: select * from pgq.consumer where co_name = 'название londiste consumer'
4) src: select * from pgq.subscription where sub_consumer = 'id из (3)'
5) src: update pgq.subscription set sub_last_tick = 'tick_id из (2)', sub_batch = null, sub_next_tick = null where sub_consumer = 'id из (3)'
Backup• fsync в archive_command
– сейчас — копия в облаке
• в планах: два сервера
– archive_command на оба
– restore_command с двух
– pg9.2 pg_receivexlog не работает● как защититься от удаления WAL при checkpoint?
(в 9.4 сделали --slot)● не делает fsync (в 9.5 сделали --synchronous)● не проверяет целостность при запуске
Backup и streaming• в архив пишет master (archive_command), а копия идёт
со standby
• backup завершился, а master не успел отправить WAL в архив = битый backup
– pg9.2 не используем streaming :-)
– pg9.2 12 часов задержка backup сервера
– pg9.5 archive_mode=always, пишем в архив со standby!
Backup, проверкаскрипт для проверки, распаковка на тестовый сервер:
• запуск кластера, recovery.conf: recovery_end_command
• ждём RECOVERY_END, перезапуск для log: 'end-of-recovery checkpoint', 'database system is ready'
• анализ csvlog файла, пропускаем SQLSTATE '57P03' # 'the database system is starting up'
– есть только severity: LOG
– для каждой базы вызываем: select check_backup(last_txtime)
● например, create_time - last_txtime < 1 минута
Backup, проверкаredo_start = backup_label: START WAL LOCATIONrecovery_end = pg_controldata: Minimum recovery ending location
• сообщения, анализируемые в log файле– 'redo starts at ([0-9A-F/]+)': redo_start– 'consistent recovery state reached at ([0-9A-F/]+)':
recovery_end– 'redo done at ([0-9A-F/]+)'– 'last completed transaction was at log time ([0-9-]+ [0-
9:.]+\+[0-9]+)': last_txtime– 'archive recovery complete'– 'database system is ready to accept connections'
• vacuum базы, sec scan, index scan– отказались, отчёты по восстановленной базе
Синхронная репликация• приложение-сервисы
• сервис платежей
– отдельный кластер master-slave
– синхронная репликация
• так как только платежи — TPS и latency сети хватает
• можно восстанавливаться и при асинхронной репликации
• недостаточно восстановить postgres, нужно думать о связанных системах
– логическая репликация
– внешние индексы (sphinx)
– экспорт данных (DWH и т.п)
• тестирование резервных копий