Skip to main content
Поврежденные или некорректные снимки ClickHouse Keeper могут приводить к серьезной нестабильности системы, например к несогласованности метаданных, переходу таблиц в состояние только для чтения, исчерпанию ресурсов или сбоям резервного копирования. В этой статье рассматриваются:

Обзор снимков Keeper

Что такое снимок?

Снимок — это сериализованное состояние внутренних данных Keeper (таких как метаданные о кластерах, путях координации таблиц и конфигурациях) на определённый момент времени. Снимки крайне важны для повторной синхронизации узлов Keeper в кластере, восстановления метаданных при сбоях, а также для процессов запуска и перезапуска, которым требуется заведомо корректное состояние Keeper.

Где найти снимки?

Снимки хранятся в виде файлов в локальной файловой системе узлов Keeper. По умолчанию они находятся в /var/lib/clickhouse/coordination/snapshots/ либо по пользовательскому пути, заданному параметром snapshot_storage_path в файле keeper_server.xml. Снимки именуются последовательно (например, snapshot.23): чем новее снимок, тем больше его номер. В многоузловых кластерах у каждого узла Keeper есть свой каталог снимков.
Для восстановления критически важно, чтобы снимки на разных узлах были согласованными.

Основные симптомы и проявления поврежденных снимков Keeper

В таблице ниже перечислены некоторые распространенные симптомы и проявления поврежденных снимков Keeper: Признаки в журналах: Прежде чем диагностировать повреждение снимка, проверьте журналы Keeper на наличие характерных шаблонов ошибок:

Восстановление после повреждения снимков Keeper

Прежде чем работать с какими-либо файлами, обязательно:
  1. Остановите все узлы Keeper, чтобы предотвратить дальнейшее повреждение данных
  2. Сделайте резервную копию всего содержимого, скопировав весь каталог coordination в безопасное место
  3. Проверьте кворум кластера, чтобы убедиться, что хотя бы на одном узле сохранились корректные данные

  1. Восстановление из существующей резервной копии

Используйте этот способ, если:
  • Повреждение метаданных Keeper или снимков делает текущие данные невосстановимыми.
  • Существует резервная копия с заведомо корректным состоянием Keeper.
Чтобы восстановить данные из существующей резервной копии, выполните следующие шаги:
  1. Найдите и проверьте самую новую резервную копию на согласованность метаданных.
  2. Остановите сервисы ClickHouse и Keeper.
  3. Замените повреждённые снимки и журналы версиями из каталога резервной копии.
  4. Перезапустите кластер Keeper и проверьте синхронизацию метаданных.
Регулярно создавайте резервные копииЕсли резервные копии устарели, вы можете потерять недавние изменения метаданных. Поэтому мы рекомендуем создавать резервные копии регулярно.

  1. Откат к более старому снимку

Используйте этот процесс в следующих случаях:
  • Недавние снимки повреждены, но более старые всё ещё пригодны к использованию.
  • Инкрементальные журналы не повреждены, что позволяет выполнить согласованное восстановление.
Чтобы откатиться к более старому снимку, выполните следующие шаги:
  1. Найдите и выберите корректный старый снимок (например, snapshot.19) в каталоге Keeper.
  2. Удалите более новые снимки и журналы.
  3. Перезапустите Keeper, чтобы он заново воспроизвёл журналы и восстановил состояние метаданных.
Риск рассинхронизации метаданныхСуществует риск рассинхронизации метаданных, если снимки и журналы отсутствуют или неполны.

  1. Восстановление метаданных с помощью SYSTEM RESTORE REPLICA

Следуйте этому процессу, если:
  • метаданные Keeper утрачены или повреждены, но данные таблицы по-прежнему есть на диске
  • таблицы перешли в режим только для чтения из-за отсутствия метаданных ZooKeeper/Keeper
  • вам нужно заново создать метаданные в Keeper на основе локально доступных частей данных
Чтобы восстановить метаданные, выполните следующие действия:
  1. Убедитесь, что данные таблицы локально присутствуют в пути к данным вашего clickhouse-server, заданном параметром <path> в конфигурации. (по умолчанию /var/lib/clickhouse/data/)
  2. Для каждой затронутой таблицы выполните:
  1. Для восстановления на уровне базы данных (если используется движок базы данных Replicated):
  1. Дождитесь завершения синхронизации:
  1. Проверьте восстановление: убедитесь, что в system.replicas значение is_readonly = 0, и следите за system.detached_parts
Как это работаетSYSTEM RESTORE REPLICA отсоединяет все существующие части, заново создает метаданные в Keeper (как будто это новая пустая таблица), а затем снова присоединяет все части. Это позволяет избежать повторной загрузки данных по сети.
Предварительные требованияЭто работает только в том случае, если локальные части данных не повреждены. Если данные тоже повреждены, используйте стратегию № 5 (пересобрать кластер).

  1. Удаление и повторное создание метаданных реплики в Keeper

Используйте этот порядок действий, если:
  • Ошибка возникает только на одной реплике кластера, и её метаданные в Keeper повреждены или несогласованы
  • Вы сталкиваетесь с ошибками вида “Part XXXXX intersects previous part YYYYY”
  • Вам нужно полностью сбросить метаданные реплики в Keeper, сохранив локальные данные
Чтобы удалить и заново создать метаданные, выполните следующие действия:
  1. На затронутой реплике отсоедините таблицу:
  1. Удалите метаданные реплики из Keeper (выполните на любой реплике):
Чтобы найти правильный путь ZooKeeper:
  1. Повторно выполните ATTACH таблицы (она будет в режиме только для чтения):
  1. Восстановите метаданные реплики:
  1. Синхронизируйтесь с другими репликами:
  1. Проверьте system.detached_parts на всех репликах после восстановления
Выполните на всех затронутых репликахЕсли повреждение затронуло несколько реплик, повторите эти шаги на каждой из них по очереди.
Для всей базы данныхЕсли используется база данных Replicated, вместо этого можно выполнить SYSTEM DROP REPLICA ... FROM DATABASE db_name.
Альтернатива: использование флага force_restore_data Для автоматического восстановления всех реплицируемых таблиц при запуске сервера:
  1. Остановите сервер ClickHouse
  2. Создайте флаг восстановления:
  1. Запустите сервер ClickHouse
  2. Сервер автоматически удалит флаг и восстановит все реплицированные таблицы
  3. Следите за ходом восстановления в журналах
Этот подход полезен, когда нужно одновременно восстановить несколько таблиц.

  1. Пересборка кластера Keeper

Используйте этот процесс в следующих случаях:
  • Для восстановления недоступны пригодные снимки, Log или резервные копии.
  • Необходимо заново создать весь кластер Keeper и его метаданные.
Чтобы пересобрать кластер Keeper, выполните следующие шаги:
  1. Полностью остановите кластеры ClickHouse и Keeper.
  2. Сбросьте каждый узел Keeper, очистив каталоги снимков и Log.
  3. Инициализируйте один узел Keeper в качестве лидера и постепенно добавляйте остальные узлы.
  4. Повторно импортируйте метаданные, если они доступны из внешних записей.
Трудоёмкий процессЭтот процесс требует много времени и сопряжён с риском длительного простоя. Потребуется полное восстановление данных.

  1. Удаление осиротевших узлов из снимка при запуске

Осиротевший узел — это узел, родительский узел которого отсутствует в снимке. Keeper отказывается загружать такой снимок и записывает в Log сообщение Failure to load from latest snapshot with index. Используйте этот способ, только если:
  • снимок повреждён именно таким образом и
  • нет ни корректной резервной копии, ни более старого снимка (если применимы стратегии 1 и 2, лучше воспользоваться ими).
Задайте оба следующих параметра в секции <keeper_server> и перезапустите узел:
Требуется хранилище узлов в памятиОчистка реализована только для хранилища узлов в памяти. Если в <coordination_settings> включён use_lsmt_storage, Keeper не запустится при установленном remove_orphaned_nodes_on_startup: отключите use_lsmt_storage на время восстановительного перезапуска, а после успешного запуска включите его снова.
После этого Keeper удаляет осиротевшие поддеревья и запускается. Удаление фиксируется в Log на уровне WARNING двумя строками: в первой указываются общее число удалённых узлов и корни удалённых осиротевших поддеревьев, во второй — отсутствующие в снимке пути, которые ограничивают повреждённую область. Потомки этих корней также удаляются, но по отдельности не перечисляются. Проверив конец локального Log, Keeper до запуска Raft записывает исправленное дерево в новый файл снимка с тем же индексом Log. Результаты завершённых исправлений имеют приоритет над более старыми копиями с этим индексом, поэтому прерванное восстановление можно продолжить, не выбирая повреждённый снимок. Новые узлы-follower получают исправленный снимок, а при последующих перезапусках включать удаление осиротевших узлов больше не требуется. Исходный снимок и все копии с тем же индексом сохраняются с префиксом имени файла orphaned_; эти файлы не используются при загрузке снимков и не удаляются автоматически по сроку хранения. Сохраните их для диагностики, а когда они станут не нужны, удалите вручную. Для восстановительного перезапуска требуется дополнительное место на диске под исправленный снимок и резервную копию исходного. Если не удаётся записать исправленный снимок, синхронизировать изменения в локальном каталоге или сохранить исходные файлы, Keeper отказывается запускаться. При следующем запуске прерванная архивация будет завершена.
Восстанавливается только локальное состояниеУзлы удаляются только из копии данных этого узла. Peer-узлы, на которых эти узлы сохранились, начнут расходиться с ним, а если digest_enabled установлен в false, это расхождение впоследствии никак не обнаруживается. Используйте этот способ только для восстановления одного узла либо применяйте его ко всем узлам кластера. После запуска узла верните обеим настройкам значения по умолчанию.
Keeper намеренно отказывается запускаться, если локальная запись в Log после снимка ссылается на повреждённую область: на удалённый узел, на что-либо ниже него или на непосредственного родителя удалённого поддерева. Непосредственный родитель считается конфликтом, поскольку набор его дочерних узлов больше не соответствует дереву, для которого был записан Log, поэтому даже запись, затрагивающая только его, — в том числе создание или удаление соседнего узла под ним, которое обновляет его восстановленные счётчики дочерних узлов, — может воспроизвестись иначе. Записи, которые проверяют только существование родителя, его версию или идентификаторы изменений, например Check, CheckNotExists, CheckStat, не сравнивающий ни numChildren, ни cversion, Set или SetACL, перезаписывающие только собственные данные или ACL родителя, а также наблюдения за данными, дочерними узлами и существованием, которые клиент повторно регистрирует после переподключения, на это не влияют и не блокируют восстановление; постоянные наблюдения, повторно регистрируемые клиентом, вообще не обращаются к дереву и никогда не блокируют восстановление. Узлы выше по цепочке не затрагиваются: у них ровно те же данные, версия и дочерние узлы, что и на исправном peer-узле, поэтому запись, которая затрагивает только такой узел, воспроизводится идентично и не блокирует восстановление. Исключение составляют записи, которые обходят всё поддерево ниже своего пути, например рекурсивное удаление или рекурсивное получение списка узлов: они видят удалённые узлы и конфликтуют на любом уровне, если только Keeper не отклонит запрос до обращения к дереву (например, рекурсивное удаление /). То же относится к закрытию сеанса (Close) в конце Log, если этому сеансу принадлежал удалённый эфемерный узел или принадлежит сохранившийся эфемерный узел непосредственно под таким родителем: при закрытии сеанса удаляются его эфемерные узлы и обновляются счётчики их родителей. Воспроизведение любой такой записи на восстановленном дереве незаметно привело бы к другому состоянию. В этом случае восстановите узел с исправного peer-узла: остановите его, очистите его каталог coordination и дайте ему заново синхронизироваться с лидером либо воспользуйтесь стратегией 1 или 2.