跳转至

备份恢复

本文件面向 GoVector 的运维与开发团队,系统化阐述数据备份与恢复策略,覆盖全量与增量备份、自动与手动备份、存储位置与安全、恢复流程(完整与部分)、灾难恢复与业务连续性、验证与测试方法,以及数据迁移与版本升级中的备份要点。GoVector 采用嵌入式存储引擎,基于 bbolt(BoltDB)实现本地持久化,并通过 Protobuf 序列化点数据,确保数据在进程重启后可恢复。

项目结构与数据持久化概览

  • 存储引擎:bbolt(BoltDB),以键值形式存储集合数据;集合元数据保存在特殊桶中。
  • 序列化:点数据使用 Protobuf 编解码;集合元数据使用 JSON。
  • 服务模式:支持作为独立微服务运行,也支持以嵌入式库方式集成到应用中。
  • 关键路径:
  • 服务入口:命令行参数指定数据库文件路径与端口。
  • API 层:提供集合管理与点操作接口。
  • 存储层:负责集合桶创建、点写入/读取、删除、元数据读写等。
graph TB
subgraph "服务层"
S["API 服务器
api/server.go"] end subgraph "应用层" C["集合 Collection
core/collection.go"] end subgraph "存储层" ST["Storage 存储引擎
core/storage.go"] BB["bbolt 数据库文件
*.db"] end S --> C C --> ST ST --> BB

核心组件与备份关系

  • Storage:负责集合桶创建、点的 Upsert/Load/Delete、集合元数据的保存与加载。
  • Collection:封装集合维度、度量、索引类型与内存索引,提供 Upsert/Search/Delete 等操作;Upsert 会先落盘再更新内存索引。
  • API Server:对外暴露 REST 接口,内部持有 Storage 与 Collection,负责优雅停机。
  • 服务入口:解析命令行参数,初始化 Storage 与 Collection,启动 API 服务并监听信号进行优雅关闭。

架构总览

下图展示从 API 请求到存储落盘的关键调用链,以及优雅停机对数据一致性的影响。

sequenceDiagram
participant Client as "客户端"
participant API as "API 服务器"
participant Col as "Collection"
participant Store as "Storage"
participant DB as "bbolt 数据库"
Client->>API : "PUT /collections/{name}/points"
API->>Col : "Upsert(points)"
Col->>Store : "UpsertPoints(collection, points)"
Store->>DB : "事务写入点数据"
DB-->>Store : "写入成功"
Store-->>Col : "返回"
Col->>Col : "更新内存索引"
Col-->>API : "返回成功"
API-->>Client : "200 OK"
Note over API,DB : "优雅停机时,服务等待连接关闭,Storage.Close() 确保数据刷盘"

详细组件分析

Storage 组件(持久化核心)

  • 职责:集合桶管理、点的批量写入/删除、集合元数据保存/读取、集合列表查询。
  • 关键点:
  • 集合桶命名即集合名;集合元数据保存在特殊桶中,便于启动时自动重建集合。
  • 写入采用 bbolt 事务,保证原子性;序列化使用 Protobuf,元数据使用 JSON。
  • 支持可选的向量量化(SQ8),在存储前压缩向量并在加载时解压。
classDiagram
class Storage {

    -db : bbolt.DB
    -closed : bool
    -quantizer : Quantizer
    -useQuant : bool
    +EnsureCollection(name) error
    +UpsertPoints(colName, points) error
    +LoadCollection(colName) map[string]*PointStruct
    +DeletePoints(colName, ids) error
    +SaveCollectionMeta(name, meta) error
    +LoadCollectionMeta(name) *CollectionMeta
    +ListCollectionMetas() []CollectionMeta
    +Close() error

}

Collection 组件(集合与索引)

  • 职责:封装集合元信息与内存索引,提供 Upsert/Search/Delete。
  • 关键点:
  • Upsert 先持久化再更新内存索引,失败时尝试回滚存储侧变更,保持一致性。
  • 支持 Flat/HNSW 两种索引,按需选择。
classDiagram
class Collection {

    +Name : string
    +VectorLen : int
    +Metric : Distance
    -index : VectorIndex
    -storage : *Storage
    +Upsert(points) error
    +Search(query, filter, topK) []ScoredPoint
    +Delete(points, filter) (int, error)
    +Count() int

}

API Server 组件(REST 接口与优雅停机)

  • 职责:注册集合管理与点操作路由,启动/停止 HTTP 服务,加载集合元数据。
  • 关键点:
  • 启动时从存储加载集合元数据并重建集合。
  • 优雅停机通过 context 控制超时,确保请求处理完成后再关闭。
sequenceDiagram
participant Main as "主程序"
participant API as "API 服务器"
participant Store as "Storage"
Main->>API : "NewServer(addr, store)"
Main->>API : "AddCollection(col)"
Main->>API : "Start()"
API->>Store : "ListCollectionMetas()"
Store-->>API : "metas"
API->>API : "为每个元数据创建 Collection 并注册"
Main->>Main : "监听 OS 信号"
Main->>API : "Stop(ctx)"
API-->>Main : "HTTP 服务器关闭"

依赖关系分析

  • go.mod 显示核心依赖:bbolt(本地数据库)、Protobuf(序列化)、HNSW(索引)。
  • 服务入口通过命令行参数控制数据库文件路径与端口,便于在不同环境中部署与备份。
graph LR
M["go.mod"] --> BB["go.etcd.io/bbolt"]
M --> PB["google.golang.org/protobuf"]
M --> HNSW["github.com/coder/hnsw"]
MAIN["cmd/govector/main.go"] --> API["api/server.go"]
API --> CORE["core/collection.go"]
CORE --> STORE["core/storage.go"]

性能与可靠性考量

  • bbolt 事务写入保证单次 Upsert 的原子性,适合高并发下的批量写入场景。
  • 量化(SQ8)可显著降低磁盘占用,但会增加 CPU 开销;根据数据规模与硬件能力权衡启用。
  • 优雅停机与 Close() 调用确保数据刷盘,避免进程异常退出导致的数据丢失风险。

备份策略与实施步骤

备份时机与频率建议

  • 全量备份
  • 上线前:首次部署后立即进行一次全量备份。
  • 变更窗口:在业务低峰期(如凌晨)执行全量备份。
  • 规则:建议每日一次全量备份,或在重大数据变更后立即执行。
  • 增量备份
  • 建议:结合业务日志与 API 操作记录,可在每次大规模 Upsert/删除后触发增量备份。
  • 注意:bbolt 为单文件数据库,通常以“复制数据库文件”作为增量备份手段(见下一节)。

自动备份脚本

  • 建议脚本思路(示例步骤,不直接粘贴代码):
  • 停止服务或进入只读模式(可选)。
  • 复制数据库文件到备份目录(建议在同一文件系统内使用硬链接以减少 IO)。
  • 对备份文件进行压缩与校验(可选)。
  • 上传至对象存储或归档到本地/远程介质。
  • 清理过期备份(保留 N 天/周/月)。
  • 记录备份结果与告警。

  • 参考仓库中的脚本与命令:

  • 服务启动与停止:命令行参数指定数据库路径与端口,优雅停机。
  • 构建与清理:Makefile 提供构建、运行、清理目标,可用于自动化流水线。

手动备份操作步骤

  • 确认数据库文件路径(默认由命令行参数指定)。
  • 停止服务或切换到只读模式(可选,降低一致性风险)。
  • 复制数据库文件到安全位置(建议在同一文件系统内使用硬链接)。
  • 校验备份文件完整性(可使用校验和工具)。
  • 归档与保留策略:按天/周/月归档,保留周期遵循合规要求。

备份数据的存储位置与安全保护

  • 存储位置
  • 本地:数据库文件与备份文件应放置在独立挂载点,避免与日志同盘。
  • 远程:建议上传至对象存储(如 S3、NAS、云盘)或异地机房。
  • 安全保护
  • 文件权限:限制数据库文件与备份文件的访问权限(例如仅允许运行用户读写)。
  • 加密:传输与静态加密(如启用对象存储加密或本地磁盘加密)。
  • 审计:记录备份操作与访问日志,定期审计。

恢复流程与演练

完整恢复

  • 准备阶段
  • 选择最近一次成功的全量备份作为基线。
  • 确认备份文件可用且未被篡改。
  • 恢复步骤
  • 停止服务。
  • 将备份文件替换为当前数据库文件(或重命名为数据库文件名)。
  • 启动服务,确认集合元数据与点数据均正确加载。
  • 进行功能验证(检索、过滤、统计)。
  • 验证清单
  • 集合数量与元数据一致。
  • 点数量与版本信息正常。
  • 查询结果稳定。

部分恢复(按集合/按 ID)

  • 按集合恢复
  • 从备份中提取对应集合桶的数据(需要解析 bbolt 结构或使用工具导出)。
  • 在当前数据库中重建集合桶并导入数据。
  • 按 ID 删除/恢复
  • 使用 API 或存储层提供的删除接口进行删除。
  • 如需恢复,可通过备份中的历史数据重新 Upsert。

恢复演练(RTO/RPO)

  • RTO:通过预热镜像与快速启动脚本缩短恢复时间。
  • RPO:结合全量+增量策略,尽量贴近目标 RPO。
  • 演练频率:建议每季度进行一次完整演练,每年进行一次跨地域演练。

灾难恢复与业务连续性

灾难恢复计划

  • 灾难类型识别:硬件故障、网络中断、误删、病毒破坏、自然灾害。
  • 恢复优先级:先恢复核心集合,再恢复次要集合。
  • 多地备份:至少一个异地备份,确保跨区域容灾。
  • 自动化:通过脚本与 CI/CD 实现一键恢复与验证。

业务连续性保障

  • 读写分离:在只读副本上提供查询能力,主库负责写入。
  • 快速回滚:版本化备份与灰度发布,出现问题可快速回滚。
  • 监控告警:监控数据库文件大小、IO 延迟、备份成功率与恢复演练结果。

备份验证与恢复测试

备份验证

  • 校验和:对备份文件计算哈希,比对历史记录。
  • 结构检查:使用 bbolt 工具检查数据库文件完整性。
  • 元数据一致性:核对集合元数据与点数量是否匹配。

恢复测试

  • 抽样测试:随机抽取若干集合与点进行查询验证。
  • 压力测试:模拟生产流量进行检索与过滤测试。
  • 回归测试:对比恢复前后检索结果与性能指标。

数据迁移与版本升级注意事项

数据迁移

  • 迁移前:执行一次全量备份。
  • 迁移中:在维护窗口内停止服务,导出/导入数据(可借助 bbolt 工具或自定义导出脚本)。
  • 迁移后:进行恢复测试与功能验证。

版本升级

  • 升级前:执行全量备份。
  • 升级后:验证集合元数据与点数据一致性,进行检索回归测试。
  • 回滚策略:保留旧版本二进制与备份,必要时快速回滚。

故障排查指南

  • 无法启动或连接失败
  • 检查数据库文件是否存在与权限是否正确。
  • 查看服务日志,确认端口占用与优雅停机是否完成。
  • 数据不一致
  • 确认 Upsert 是否成功提交事务。
  • 检查存储层错误日志与 bbolt 事务状态。
  • 恢复失败
  • 校验备份文件完整性与版本兼容性。
  • 确认集合元数据桶存在且可读。

结论

GoVector 的持久化模型简单可靠:单文件 bbolt 数据库 + Protobuf/JSON 序列化,便于备份与恢复。通过合理的全量/增量策略、严格的备份验证与恢复演练、完善的灾难恢复与业务连续性方案,可有效保障数据安全与服务可用性。建议将备份纳入自动化流水线,持续优化 RTO/RPO,确保在任何情况下都能快速、准确地恢复数据。