---
url: /zh/operate/backup.md
description: Wow 服务必须备份什么、改为重建什么，以及怎样恢复它，从丢失的读模型到丢失的数据库。
---

# 备份与恢复

日志是 Wow 服务保存的唯一事实。快照、投影的视图、数仓和搜索索引都派生自日志，都可以重建；无法重建的，是日志以及同一个数据库中位于它旁边的少量状态。备份计划保护的是这些，其余一切都按重建来处理。

## 备份什么 {#what-to-back-up}

每个服务的数据库（PostgreSQL 或 MongoDB）的一份一致的备份，用数据库自己的工具获取。它包含：

| 数据 | 为什么无法重建 |
| --- | --- |
| 日志：每个聚合已提交的流，以及各分区的计数器 | 它就是历史本身 |
| 预留 | 哪个聚合持有每个唯一键 |
| 定时器 | 为稍后安排的命令 |
| 失败台账 | 订阅停放的、等待运维人员处理的聚合 |
| 检查点和租约 | 每个订阅在日志中的位置，以及哪个节点拥有哪个分区 |
| 格式记录 | 每张表或每个集合创建时所用的格式 |

把它们一起备份：只备份日志而不备份检查点，恢复出来的订阅不知道自己读到了哪里。[PostgreSQL](./postgresql#the-tables) 和 [MongoDB](./mongodb#the-collections) 列出了这些表和集合。

快照保存在同一个数据库中，数据库备份也会包含它们：与日志一起恢复时，它们与日志一致。不过，它们不是备份要保护的对象，恢复也从不需要它们是最新的。

## 不备份什么 {#what-not-to-back-up}

* 保存在自己存储中的**投影视图**：它们的订阅会从日志重新写出。
* 保存在别处的**快照**，例如在 [Elasticsearch](./elasticsearch) 上：从日志重建。
* **数仓**：ClickHouse Sink 会从日志重新写出（[ClickHouse](./clickhouse)）。
* 服务发布的 **Kafka topic**：它们为其他上下文携带的是服务提交的副本。

重建比恢复慢，所以大型读模型为了速度仍然值得单独备份。但它的正确性从不依赖备份。

## 恢复读模型 {#recover-a-read-model}

丢失或出错的投影视图、快照表或搜索索引要重建，而不是从备份恢复（[重建与重放](./replay)）：

* 投影：以新的订阅名部署它，它会从日志开头开始，追上之后再把读取方切换到它的新视图；
* 快照：从日志重建，可以针对选定的聚合，也可以针对新的存储；
* 数仓和搜索索引：从头启动它们的 Sink 订阅。

在此期间，服务照常处理命令。

## 恢复数据库 {#restore-a-database}

当服务的数据库丢失时：

1. **停止服务的每个节点。** 任何节点都不能写入正在恢复的数据库。
2. **恢复数据库最近一份一致的备份。**
3. **启动节点。** 订阅从恢复出的检查点继续，定时器触发，停放的聚合在台账中等待。
4. **重建保存在数据库之外的读模型。** 根据恢复后的日志中已不存在的提交写出的视图或索引，领先于日志；按上面的方式重建它。

备份之后的提交随之丢失。发送这些提交的调用方得到了成功的回答，其他上下文也可能已经从发布的事件流中读到了它们，所以恢复到更早的时间点是一个影响超出这个服务的决定：恢复到数据库所能提供的最近时间点，如果数据库有持续归档就利用它，并告知读取该服务事件流的那些上下文的负责人。

## 测试恢复 {#test-the-restore}

备份由一次恢复来证明：恢复到一个隔离的环境中，在其上启动一个节点，读取几个聚合的状态，并向它们发送一条命令，这条命令必须在下一个版本追加。
