Бэкап кластера или базы – как не ошибиться в выборе?
В предыдущей части Инструкция по установке Postgres для OLTP приложений и 1С. Часть 1 Базовая конфигурация. мы импортозаместились на Postgres , настало время сохранить нажитое.
Что мы хотим от бэкапа базы – да во общем немного. Главное сократить технологическое окно и он был консистентным (целостным), по крайней мере на уровне транзакций.
Есть еще логическая консистентность , например, когда накладная в 1С при проведении сохраняется в одной транзакции, а проводки формируются в другой. С точки зрения пользователя это одна «транзакция\действие», но с точки зрения СУБД два. А с точки зрения программиста 1С объединять это в одну транзакцию чревато избыточными блокировками.
В Postgres есть понятие база , которую теоретически можно копировать, делать резервные копии и она соотвествует термину база в 1С.
Интернет забит статьями, что это возможно через pg_dump (или клоны). Достаточно спросить ChatGPT о альтернативе и мы введем ИИ в ступор. Это тот самый случай когда важно не то, что ИИ знает, а что он не знает.
Однако при сравнении с другими СУБД возникают неудобные вопросы.
В СУБД типа Postgres, MSSQL , Oracle есть две сущности которые часто путают
Instance (Cluster в Postgres) набор процессов, которые обслуживают одну или несколько баз данных
База данных – группировка объектов\метаданных\таблиц\процедур\функций и т.д. в Instance для работы какого либо приложения (База данных в Postgres и MS SQL соответствует базе в терминах 1С)
Есть еще более узкое понятие схема. В разных СУБД его интерпретируют по-разному. Например, в Postgres туда включаются почти все объекты, в MS SQL она используется для прав доступа, а в Oracle схема группирует объекты для использования каким-либо приложением (В Oracle схема соответствует базе в терминах 1С).
Сравним понятие база в Postgres и MS SQL.
Если сделать поверхностное сравнение, то видно что в:
А) Postgres права доступа и хранение Tablespace вынесен на уровень кластера.
Б) В MS SQL есть понятие login для кластера, и users внутри базы данных. Внутри базы данных находится основной набор прав, а login уже привязывается к users.
В) MS SQL структура хранения Files, File groups , Transaction log так же существуют и определяются на уровне базы данных, а не Instance. И это кардинально меняет ситуацию с бэкапом.
В Postgres несколько баз данных делят один Tablespaces. B 1C этим вообще трудно управлять, поскольку она будет создавать таблицы либо tablespace v81c_data v81c_index , а если их нет в pg_default.
Т.е. Вы НЕ можете в Postgres делать бэкап на уровне файлов для одной базы на уровне Tablespace, даже теоретически - поскольку эта сущность принадлежит Instance и в v81c_data будут данные других баз. А в MS SQL можете, поскольку Files и File groups определяются на уровне базы, и там есть консистентные команды Backup \ Restore именно на уровне базы данных поскольку файлы (включая Transaction log) и файловые группы определяются на уровне базы данных, а не Instance.
Здесь уместно сравнить Postgres c Oracle DBA Management Pack (oracle.com).
В Oracle команда CREATE DATABASE создает базу данных , но один Instance обслуживает одну базу данных в терминах Oracle. В Oracle RAC больше одного Instance могут обслуживать базу данных.
А то что мы понимаем под базой данных 1С отражается в Oracle так Схема.
Серверу баз данных в терминах «1С:Предприятия» соответствует понятие DATABASE в терминах Oracle Database. Базе данных в терминах «1С:Предприятия» соответствует схема данных в терминах Oracle Database. При создании информационной базы данных в «1С:Предприятии» в Oracle Database создается пользователь и его схема данных.
«1С:Предприятие 8» использует при работе с Oracle Database следующие табличные пространства (tablespace):
для данных ‑ V81C_DATA;
для индексов ‑ V81C_INDEX;
для LOB ‑ V81C_LOB;
для временных файлов ‑ V81C_TEMP.
Если табличные пространства (tablespace) с такими именами существуют, то они будут использованы; если нет, то будут созданы при создании информационной базы, при этом datafiles будут иметь пути по умолчанию.
В oracle есть утилиты EXP\IMP похожие на pg_dump (Postgres) или BCP (MS SQL), которые позволяют выгрузить отдельную схему. Но это не консистентный бэкап по умолчанию о чем можно почитать тут Original Export (oracle.com) consistent parameter) . Однако полноценные бэкапы делаются через RMAN на уровне кластера и никого это не смущает, поскольку Схема данных в Oracle это просто набор объектов, а не «база данных» . Т.е. термины Oracle более честные, чем у Postgres. Надеюсь, эксперты Postgres расскажут почему?
В Oracle возможно сделать полноценный бэкап Tablespace - Backing Up the Database (oracle.com) отдельно через RMAN и восстановить при определенных условиях.
Сказка о целостных копиях в pg_dump
В документации Postgres про pg_dump пишут так:
PostgreSQL : Документация: 16: pg_dump : Компания Postgres Professional
pg_dump — это программа для создания резервных копий базы данных PostgreSQL. Она создаёт целостные копии, даже если база параллельно используется. Программа pg_dump не препятствует доступу других пользователей к базе данных (ни для чтения, ни для записи).
Звучит замечательно. Давайте подумаем, как произойдет Online бэкап в 1С, где объект документ накладная состоит из Шапки и ТабличнойЧасти (Товары) и еще делает проводки в регистры – тоже отдельные таблицы.
Чат GTP и ссылки утверждают, что все будет ок, потому что открывается транзакция.
Правда не рассказывают какой ценой.
Для этого достаточно включить логгирование и посмотреть, что нам покажет расширенный лог.
Для включения логгирования достаточно указать в postgresql.conf
logging_collector = on
log_directory = 'D:\\PostgresSQL\\15_3\\trace' #обратите внимание на формат каталога он может отличаться в разных ОС
log_min_messages = log
log_min_duration_statement = 0
Запустим:
pg_dump --username=postgres --host=post1c_server--file=e:\backup\MIS_PURE_8_3_POS_compr_NoRepeatRead.pgbak --format=d --jobs=3 --verbose --compress=9 MIS_PURE_8_3_POS
В логе посмотрим как идет копирование документа public._document21655 и его табличной части public._document21655_vt21690 . Особенно обратите внимание на разницу старта копирования таблиц.
2024-01-23 10:48:03.570 GMT [16612] LOG: statement: SET TRANSACTION ISOLATION LEVEL REPEATABLE READ, READ ONLY
2024-01-23 10:48:03.570 GMT [16612] LOG: duration: 0.011 ms
2024-01-23 10:48:03.570 GMT [16612] LOG: statement: SET TRANSACTION SNAPSHOT '00000007-000013BE-1'
2024-01-23 10:48:34.059 GMT [6516] LOG: statement: LOCK TABLE public._document21655 IN ACCESS SHARE MODE NOWAIT
2024-01-23 10:48:34.060 GMT [6516] LOG: duration: 0.272 ms
2024-01-23 10:48:34.061 GMT [6516] LOG: statement: COPY public._document21655 (_idrref, _version, _marked, _date_time, _number, _posted, _fld21670, _fld21671, _fld21672rref, _fld21673rref, _fld21674rref, _fld21675rref, _fld21676, _fld21677, _fld21678, _fld21679, _fld21680rref, _fld21681rref, _fld21682, _fld21683rref, _fld21684rref, _fld21685rref, _fld21686, _fld21687rref, _fld21688_type, _fld21688_n, _fld21688_s, _fld21689, _fld21741rref, _fld628) TO stdout;
2024-01-23 10:48:34.062 GMT [16612] LOG: duration: 3.721 ms
2024-01-23 10:48:34.271 GMT [12188] LOG: statement: LOCK TABLE public._document21655_vt21690 IN ACCESS SHARE MODE NOWAIT
2024-01-23 10:48:34.271 GMT [12188] LOG: duration: 0.253 ms
2024-01-23 10:48:34.272 GMT [12188] LOG: statement: COPY public._document21655_vt21690 (_document21655_idrref, _fld628, _keyfield, _lineno21691, _fld21692, _fld21693rref, _fld21694) TO stdout;
2024-01-23 10:48:34.273 GMT [12188] LOG: duration: 0.945 ms
С точки зрения целостности все хорошо – используется уровень изоляции SET TRANSACTION ISOLATION LEVEL REPEATABLE READ. Он достаточно жесткий, т.е. работа параллельных процессов на запись во время длительного бэкапа будет парализована. Каждая таблица блокируется полностью до окончания команд COPY.
А если посмотреть сравнение уровней изоляции в Postgres и сравнить с Serializable то они не сильно отличаются для Postgres.
Кстати в pg_dump можно задать уровень изоляции --serializable-deferrable и описан он так (более того его рекомендуют для абсолютной целостности).
--serializable-deferrable
Использовать при выгрузке транзакцию с уровнем изоляции serializable для получения снимка, согласованного с последующими состояниями базы. Правда для этого нужно выждать момент, когда в потоке транзакций нет аномалий, и поэтому нет риска, что выгрузка завершится неудачно, и риска отката других транзакций с ошибкой serialization_failure. Более подробно изоляция транзакций и управление одновременным доступом описывается в Главе 13.
Параметр не особо полезен в случаях, когда требуется восстановление после сбоя. Он полезен для создания копии базы данных, в которой формируются отчёты и выполняются другие операции чтения, в то время как в основной базе продолжается обычная работа. Без этого параметра выгрузка может содержать не целостное состояние базы данных. Например, если используется пакетная обработка, статус пакета может отражаться как завершённый, в то время как в выгрузке будут не все элементы пакета.
При указании этого параметра в логе появляется:
2024-01-25 11:31:39.589 GMT [16496] LOG: statement: SET TRANSACTION ISOLATION LEVEL SERIALIZABLE, READ ONLY, DEFERRABLE
Если почитать Postgres Pro Standard : Документация: 10: 13.2. Изоляция транзакций : Компания Postgres Professional
Уровень Serializable обеспечивает самую строгую изоляцию транзакций. На этом уровне моделируется последовательное выполнение всех зафиксированных транзакций, как если бы транзакции выполнялись одна за другой, последовательно, а не параллельно. Однако, как и на уровне Repeatable Read, на этом уровне приложения должны быть готовы повторять транзакции из-за сбоев сериализации. Фактически этот режим изоляции работает так же, как и Repeatable Read, только он дополнительно отслеживает условия, при которых результат параллельно выполняемых сериализуемых транзакций может не согласовываться с результатом этих же транзакций, выполняемых по очереди.
Но последнее предложение
Например, еслииспользуется пакетная обработка, статус пакета может отражаться какзавершённый, в то время как в выгрузке будут не все элементы пакета .
странное.
По тестам и по данным логов– если pg_dump стартовал, он даже в Repeatable read зафиксирует все на момент старта. Или имелось что-то другое ввиду?
--serializable-deferrable уже слишком жестко действует - у меня при наличии сессии PgAdmin бэкап с этим параметром вообще не стартовал.
Насколько комфортно использовать pg_dump при параллельной работе?
Это легко оценить если запустить его параллельно с одним потоком записи (раз в секунду) – вот таким скриптом 1С
&НаСервереБезКонтекста
Процедура ВыполнитьНаСервере(Операция)
Перем ОперацияСсылка, ИсходныйКомментарий;
ОперацияОбъект=Операция.ПолучитьОбъект();
ИсходныйКомментарий=ОперацияОбъект.Комментарий;
Для Сч=1 По 60 Цикл
ОперацияОбъект.Комментарий= Строка(?(Сч=60,"Цикл завершен","В процессе")) + Строка(ТекущаяДата());
ОперацияОбъект.Записать();
СУУ_УниверсальныеСервер.Пауза(1);
КонецЦикла;
Сообщить("Запись завершена");
// Вставить содержимое обработчика.
КонецПроцедуры
А потом посмотреть, что попадет в бэкап и как (в repeatable read и serialize результат одинаков).
По результатам видно (в логах тоже самое) что в бэкап попадает только то что было зафиксировано До начала выгрузки. При этом даже с такой легкой нагрузкой выгрузка тормозит.
Итак, краткое резюме по pg_dump:
Может делать online копирование одной базы из кластера, но ценой жесткого режима изоляции с провоцированием отвала работающих сессий по таймауту. Может просто не запустится, если на сервере работает какой то нагруженный процесс
Может копировать таблицы параллельно через параметр –job, но скорость определяется самой большой таблицей , которая копируется последовательно.
Может копировать только данные таблиц , а для индексов фиксирует только команды пересоздания, что сильно замедлит восстановление.
Очень зависит от прав доступа, ведь это по сути вызов команды copy для таблиц.
Вы еще хотите использовать pg_dump для online бэкапов?
Вообще попытки использовать для online backup pg_dump равносильно использованию BCP (bulk copy program utility) для бэкапа MS SQL или EXP (Original export) для бэкапа Oracle. Эти утилиты предназначены только для переноса информации между Instance, когда источник в режиме только чтение либо не нагружен, а скорость переноса не так критична. Актуально для различных задач переноса данных между кластерами на разных ОС.
В остальных случаях лучше сделать холодный бэкап Postgres, чем такой Online бэкап, и время простоя будет меньше и проблем с целостностью не будет, а вы свободны в выборе средства для сжатия файлов кластера
Когда гарантии восстановления может дать только страховой полис
Быть уверенным в бэкапе можно только, если Вы его восстановили, особенно в Postgres, где в отличии от MS SQL и Oracle, нет понятия верификации бэкапа. Потому что, когда Ваши деньги в банке, они в Банке, а не у Вас.
И очень внимательно нужно читать документацию. Например
pg_restore --create --dbname restored_mis_pure --jobs=5 --username=postgres --host=post1c_serverE:\Backup\MIS_PURE_8_3_POS_compr_RepeatRead.pgbak
rem опасно!!!
rem С этим параметром --create база, заданная параметром -d, применяется только для подключения и выполнения начальных команд DROP DATABASE и CREATE DATABASE. Все данные восстанавливаются в базу данных, имя которой записано в архиве.
Т.е. попытка создать этим параметром базу под другим именем restored_mis_pure и развернуть копию в том же кластере , просто перезатрет MIS_PURE_8_3_POS.
Философский вопрос – зачем при авторизации в pg_restore обязательно указывать существующую базу, если учетные записи пользователей на уровне кластера, не базы?
Поэтому просто восстановить не получится, надо создать сначала пустую базу на основе шаблона template0
CREATE DATABASE "RESTORED_MIS_PURE"
WITH
OWNER = postgres
TEMPLATE = template0
ENCODING = 'UTF8'
LC_COLLATE = 'Russian_Russia.1251'
LC_CTYPE = 'Russian_Russia.1251'
TABLESPACE = pg_default
CONNECTION LIMIT = -1
IS_TEMPLATE = False;
Восстановить командой
pg_restore --clean --if-exists --exit-on-error --verbose --dbname RESTORED_MIS_PURE --jobs=5 --username=postgres --host=post1c_serverE:\Backup\MIS_PURE_8_3_POS_compr_NoRepeatRead.pgbak
где –clean --if-exists чистит существующую базу (пустую) с проверкой и без ошибок.
--jobs ускоряет процесс восстановления, но индексы все равно будут пересоздаваться заново, поэтому задумайтесь о вашем recovery time objective. Может все холодный бэкап был бы быстрее, чем такой горячий?
Ну и наконец нужно проверить, что хотя бы основные таблицы восстановились правильно (нет верификации бэкапа). Хотя бы потрогаем все их командой
VACUUM (ANALYZE, VERBOSE, PARALLEL 5)
Зачем это нужно? Ответ будет в следующих сериях, а пока просто поверьте, что Postgres при старте не проверяет все нужные объекты в наличии, а только когда к ним обратишься. Как вообще с этим живут?
Как делать бэкап в Postgres надежно
Чтобы делать надежные бэкапы в Postgres нужно понять главное – термин база данных в Postgres это обман. Назовите ее чем угодно, но работайте только с кластером. Для него существует и холодный бэкап и горячий бэкап через pg_basebackup . Разбивайте большие базы данных по отдельным кластерам на других портах. Поверьте, это меньшее зло (если учитывать расход памяти) чем держать все «базы» в одном кластере и это тот самый случай когда нужно внимательно спланировать Deploy.
Огорчу что, даже холодный бэкап\восстановление не так прост, а online бэкап\восстановление через pg_basebackup еще сложнее поскольку это по сути подъем реплики. Вы когда-нибудь слышали при работе с Oracle или MS SQL понятие junction?
А вот в Postgres без этого холодный или горячий бэкап со своими tablespace вы нормально не восстановите. Тут самое время задуматься о бесплатности PostgresSQL - бесплатный сыр для 1С или ступенька к Enterprise версии.
А что Вы думали – внедрить бесплатный Postgres не проще чем, великану в стране 1С пересесть на слона?
И даже простая уборка мусора в нагруженной базе требует особого подхода Как эффективно настроить autovacuum в Postgresдля 1С.
Порекомендуйте пожалуйста платный backup \ restore кластера для Postgres и чтобы не больше двух строчек в командном файле, а то после MS SQL и Oracle есть ощущение какого то downshift. Хочется как раньше «заплати и живи спокойно».
До новых встреч на нашем канале t.me/Chat1CUnlimited