---
title: "无停机升级"
description: "在自托管环境中无停机升级 Quire。"
image: "https://docs.quirelms.com/og.png"
---

> Documentation Index
> Fetch the complete documentation index at: https://docs.quirelms.com/zh-Hans/llms.txt
> Use this file to discover all available pages before exploring further.

# 无停机升级

<span id="upgrading-without-downtime"></span>

相关规则见 `docs/architecture/23-ops.md` 第 7 节和 `docs/architecture/07-data.md` 第 4.1 节。此处介绍具体流程。

## 安全升级的保证 <!--quire:the-guarantee-that-makes-it-safe-->

**版本 R 可以正确使用架构 R 和 R 减 1 的数据库模式。**每项数据库模式变更都分为扩展、过渡和收缩三个阶段：

1. **扩展**：添加新列、表或索引。旧代码会忽略它。
2. **过渡**：至少持续一个版本；新代码同时写入两种结构，并读取新结构；可续跑的任务会回填旧行。
3. **收缩**：在后续版本中单独移除旧结构。

因此，在滚动升级的任何时刻，新旧进程都可以共用同一个数据库。系统没有向下迁移：如果迁移在一小时前删除了某列，就无法恢复这一小时内写入的数据。

每个版本的 `schema-compat` CI 任务都会用新版数据库模式运行上一版本的测试，以检查这一保证。

## 开始之前 <!--quire:before-you-start-->

1. 阅读版本说明。需要维护窗口的版本会明确说明，并估算所需时长；每个版本最多只有一次这样的发布。
2. 执行恢复演练，或确认此版本的演练已成功（见 [backup-restore.md](/zh-Hans/ops/backup-restore/)）。演练失败会阻止升级。
3. 创建基础备份：`docker compose -f docker/compose.yaml --profile backup
   run --rm backup`。

## 单主机上的 Docker Compose <!--quire:docker-compose-one-host-->

```sh
export QUIRE_RELEASE=2026.10.0            # or set it in docker/.env
docker compose -f docker/compose.yaml pull   # or build
docker compose -f docker/compose.yaml run --rm migrate
docker compose -f docker/compose.yaml up -d --no-deps web content collab
docker compose -f docker/compose.yaml up -d --no-deps worker scheduler
```

此顺序是有意安排的：

1. **先迁移**，旧版本仍在处理流量时执行。扩展迁移对旧版本不可见。
2. **然后升级 Web 层**。收到 SIGTERM 后，每个 Web 进程会将 `/readyz` 状态设为 `draining`，在 30 秒内完成正在处理的请求，以重连提示关闭流，然后退出。`stop_grace_period` 为 40 秒，因此 Compose 不会中断正常的退出过程。
3. **最后升级 worker**，先确保产生新事件格式，再让新消费者开始处理。Worker 会立即停止获取任务，并有 120 秒时间完成处理。无法完成的任务会在其他位置重新获取；由于每个任务都有幂等性，这是安全的。Scheduler 会在下一个周期移交领导权。

在单主机上，Compose 会依次替换各个容器，因此每项服务都会短暂中断。要完全消除中断，可在自己的代理后运行两个 Web 层容器（通过覆盖文件添加没有发布端口的第二个 web 服务），每次只重建一个，并等待其报告健康后再重建另一个。

## 多主机或编排器 <!--quire:several-hosts-or-an-orchestrator-->

按相同顺序操作：从单个任务运行一次迁移，以 surge 为 1、不可用实例数为 0 的方式滚动升级 Web 层，然后升级 worker。就绪探针指向 `/readyz`，存活探针指向 `/healthz`。

有专用租户数据库时，`migrate` 步骤会先迁移控制数据库，然后逐个迁移 `ops.tenant_database` 中列出的每个数据库，并为每个数据库分别加锁。某个租户数据库迁移失败不会影响其他数据库。全部迁移完成后，会比较各数据库的迁移记录；如果任何数据库应用的迁移与控制数据库不完全一致，命令会以非零状态退出并指出哪些数据库落后或超前。同一命令也会在每个数据库中安装队列表，因为 worker 会在固定租户写入任务的数据库中读取任务。

```sh
bun apps/worker/src/migrate.ts   # what the Compose step runs
bun run db:migrate:all                            # the same, from a checkout
```

每个专用数据库都通过其注册名称访问。名为 `env:QUIRE_DB_NORTHWIND_URL` 的数据库需要：

| 变量 | 用途 |
| --- | --- |
| `QUIRE_DB_NORTHWIND_URL` | 应用角色，供 Web 层和 worker 使用 |
| `QUIRE_DB_NORTHWIND_URL_MIGRATOR` | 迁移角色，供此命令和迁移操作使用 |
| `QUIRE_DB_NORTHWIND_URL_SUPERUSER` | 可选：迁移前重新应用启动配置（角色、模式、辅助函数） |

未配置 `_MIGRATOR` 连接的已注册数据库会作为失败报告，绝不会跳过。控制数据库迁移完成后即可滚动升级 Web 层。落后一小时会发出警告，落后一天会触发告警。

## pgvector <!--quire:pgvector-->

从迁移 0264 开始，只要服务器安装了扩展，语料检索就会使用 pgvector HNSW 索引；Compose 的 `postgres` 服务已通过 `docker/postgres.Dockerfile` 编译此扩展。切换镜像后的首次 `migrate` 会通过超级用户启动配置创建扩展，然后迁移 0264 会添加生成的向量列并构建索引。添加列时会在排他锁下重写一次 `app.ai_chunk`，因此检索请求会等待；没有其他表会受到影响。

未安装 pgvector 的服务器上，迁移 0264 会记录提示但不做任何更改，检索仍使用精确匹配。pgvector 版本低于 0.8 时，虽然会创建列和索引，检索仍使用精确匹配，直到扩展升级（运行 `alter extension vector update`），因为带筛选条件的 HNSW 扫描需要 0.8 提供的迭代扫描功能。若要在尚未安装扩展的服务器上稍后启用，请安装扩展、再次运行启动配置（或以超级用户身份运行 `create extension vector`），然后以 `quire_migrator` 身份执行：

```sql
set maintenance_work_mem = '1GB';  -- the HNSW build is much faster in memory
select ops.ai_chunk_enable_vector_index();
```

该操作具有幂等性，会返回 `enabled` 或 `unavailable`。请在每个专用租户数据库中也运行此操作。

## 回滚 <!--quire:rolling-back-->

始终可以回滚**代码**：将 `QUIRE_RELEASE` 设为上一标签，再次运行 `up -d`。由于同一版本范围内的数据库模式双向兼容，因此此操作有效。

不提供**数据库模式回滚**。以下更改无法撤销；如果必须恢复，请按相应方式处理：

| 无法恢复的更改 | 恢复方式 |
| --- | --- |
| 收缩迁移删除了某列 | 将时间点恢复到删除之前的新数据库，提取数据并合并 |
| 原地更改数据 | 同上，然后协调恢复后产生的写入 |
| 已发送的 webhook 和事件 | 发送补偿事件，绝不删除 |
| 已发送的电子邮件 | 由人员撰写后续邮件 |
| 审计哈希链 | 永远不重写；追加更正记录 |

因此，收缩迁移会单独发布：之后若需恢复，就有一个清晰的时间边界。

## 检查升级结果 <!--quire:checking-the-upgrade-->

```sh
docker compose -f docker/compose.yaml ps           # every service healthy
curl -fsS http://localhost:8080/readyz             # ready, and what is configured
docker compose -f docker/compose.yaml logs migrate # the migrations applied
```

Source: https://docs.quirelms.com/zh-Hans/ops/upgrade/index.mdx
