備份與遷移
「系統 > 備份與遷移」頁(/admin/backup)承擔四類工作:完整備份的匯出與下載、整庫匯入還原、輕量的站點配置匯出 / 匯入,以及 Qdrant 向量庫的檢查與重建。完整備份是引擎中立的邏輯歸檔,同一個 zip 可以在 SQLite 與 PostgreSQL 部署之間互相匯入,跨機遷移、跨引擎遷移、災備恢復全部走這一條路徑,不需要碰 pg_dump 或資料庫檔案。
完整備份匯出
歸檔裡有什麼
匯出產物是單個 zip,內部結構:
aivory-docker-backup-20260711-153000-xxxxxxxxxx.zip
├── manifest.json # 格式版本、源引擎方言、各錶行數、是否含檔案
├── db/
│ ├── users.jsonl # 每表一個 JSONL,每行一個 JSON 物件
│ ├── conversations.jsonl # 按外部索引鍵安全順序排列,引擎中立
│ ├── messages.jsonl
│ └── ... # 全部資料表
├── files/ # 可選:勾選「包含上傳檔案與生成產物」時才有
│ ├── uploads/... # 使用者上傳的檔案
│ └── artifacts/... # 模型生成的產物(圖片、文件等)
└── qdrant/ # 可選:部署配置了 QDRANT_URL 時自動包含
└── collections/
└── aivory_c1536.jsonl # 逐 collection 的向量點位匯出
資料行是邏輯 JSONL 而非資料庫轉儲:二進位制列 Base64 編碼,大整數保精度,這正是它能跨 SQLite / PostgreSQL 的原因。向量 collection 按 aivory_c<維度> 命名(如 1536 維模型對應 aivory_c1536)。
匯出選項
| 選項 | 預設 | 說明 |
|---|---|---|
| 包含上傳檔案與生成產物 | 開 | 一併打包 uploads 與 artifacts 目錄,歸檔明顯更大;不勾則只有資料庫行 |
| Qdrant 向量 | 自動 | 部署配置了 QDRANT_URL 即包含,無需勾選;未配置 Qdrant 的部署自然沒有這一段 |
非同步任務與歸檔列表
頁面上的「匯出備份」走非同步任務:點選後任務在後臺生成,頁面顯示進度(準備中、讀取資料庫、寫入歸檔),期間可以離開頁面。要點:
- 同一時間只能有一個匯出任務在跑;匯出與下文的向量維護任務互斥,一方執行時另一方拒絕啟動。
- 匯出基於只讀事務,是一致的時間點快照,期間使用者的正常使用不受影響。
- 完成後歸檔出現在「已生成的歸檔」列表,點「下載」儲存到本地。歸檔檔案本身存放在伺服器的
BACKUP_DIR目錄(預設./data/backups,compose 部署對映在資料卷裡),列表按生成時間倒序。
另有一個同步流式端點適合指令碼化定期備份:GET /api/admin/backup/export(查詢引數 files=1 含檔案,qdrant=0 可排除向量),響應直接是 zip 流。小庫用它一步到位,大庫建議走頁面上的非同步任務,避免下載連線長時間掛著。
非同步匯出生成的歸檔會一直留在伺服器的 BACKUP_DIR 裡,系統不做自動輪換。每個全量歸檔可能相當大(含檔案與向量時尤甚),下載離機儲存後,記得定期到該目錄刪除陳舊歸檔,避免資料卷被備份佔滿。
順帶區分:普通使用者在「設定 > 隱私」裡也有一個"匯出全部資料",那是單個使用者自己的 GDPR 式 JSON 匯出,與這裡的全站管理員備份是兩回事,見分享與資料管理。
匯入(整庫替換)
操作步驟
- 在「匯入與還原」區選擇一個上面匯出的 zip 歸檔。
- 在確認彈窗中輸入確認詞
REPLACE(必須完全一致)。 - 點選「匯入並替換資料」,等待完成。
- 匯入成功後當前會話立即失效並自動登出,用備份裡的賬號密碼重新登入。
替換語義
匯入是整庫替換,不是合併:
- 在一個數據庫事務裡清空所有表,再按外部索引鍵安全順序從 JSONL 逐表過載;歸檔裡存在而當前版本沒有的列會被跳過(前向相容)。中途任何一步失敗都不會提交,資料庫保持匯入前的狀態。
- 檔案恢復:歸檔中記錄的儲存路徑字首會被重寫為本機的上傳 / 產物目錄,換機器換路徑不需要手工處理。
- 向量恢復:歸檔含 Qdrant 段時逐 collection 重建點位;向量恢復的告警不會中斷匯入,可事後用向量維護補齊。
- PostgreSQL 目標庫會自動重置自增序列;SQLite 目標庫在恢復期間臨時關閉外部索引鍵檢查。因此跨引擎匯入開箱即用:SQLite 的備份可以匯入 Postgres 部署,反之亦然。
- 匯入完成後設定快取即刻失效,備份裡的站點配置立即生效。
匯入完成後,除了"執行本次匯入的管理員郵箱"之外,備份帶來的所有 admin 賬號一律被自動降級為普通使用者。這是防提權設計:否則任何人拿到一份構造過的備份,就能給自己塞進一個管理員賬號。
實際影響:新例項首啟時建立的管理員郵箱,應與舊例項的管理員郵箱一致。如果不一致,匯入後舊例項的管理員會全部變成普通使用者,而執行匯入的賬號(其郵箱在備份裡可能只是普通使用者甚至不存在)也未必還能登入管理後臺,需要進資料庫手工修復。按下文遷移清單操作即可避開這個坑。
大小上限與 Cloudflare
匯入上傳的硬上限由環境變數 MAX_BACKUP_BYTES 控制,預設 20 GiB(見核心環境變數)。如果站點掛在 Cloudflare 後面,注意 Cloudflare 對請求體另有 100 MB 級別的套餐上限,大歸檔必須繞開代理直連源站匯入,做法見 Cloudflare 代理配置。反向代理層(如 Nginx 的 client_max_body_size)同樣要為備份匯入放寬,見反向代理。
匯入常見報錯
| 現象 | 原因與處理 |
|---|---|
| 400,提示確認詞不符 | REPLACE 必須逐字元一致(全大寫、無空格) |
| 413 / 請求體過大 | 歸檔超過 MAX_BACKUP_BYTES,或被 Cloudflare / 反代的請求體上限攔截;調大上限或走直連通道 |
| manifest 校驗失敗 | 上傳的不是本系統匯出的備份 zip,或下載 / 傳輸中被截斷;重新匯出並核對檔案大小 |
| restore failed (no changes committed) | 恢復中途出錯,事務已整體回滾,資料庫仍是匯入前的狀態;看服務端日誌定位具體的表與行後重試 |
| 匯入成功但知識庫檢索不到內容 | 備份未含向量段(舊例項沒配 Qdrant)或向量恢復有警告;到「向量檢查」跑一次重建即可 |
配置匯出 / 匯入(輕量路徑)
完整備份之下還有一條更輕的路徑:只搬站點配置,不搬使用者資料。適合把開發機的一套配置複製到生產、跨部署統一配置、或災備前留檔。
| 方向 | 行為 |
|---|---|
| 匯出配置 ZIP | 打包站點設定、渠道、模型、技能、OAuth 提供商、圖片風格、使用者組、模型額度,以及圖示與技能資產檔案 |
| 匯入配置歸檔 | 按 ID UPSERT 合併:同 ID 的配置行被覆蓋,本地多出的配置保留;絕不觸碰使用者、對話、訊息、上傳檔案、會話與日誌。也相容舊版純 JSON 配置檔案 |
配置匯入是非破壞性的、會話安全的:不需要輸 REPLACE,匯入後不用重新登入,單個確認框即可執行。
配置 ZIP 裡帶著明文的渠道 API Key、OAuth Client Secret、SMTP 密碼、物件儲存與搜尋金鑰(否則匯入後無法直接工作)。請像對待金鑰本身一樣保管這個檔案,不要提交進倉庫或散發。
跨引擎遷移:SQLite 到 PostgreSQL 實操
以最常見的"單機 SQLite 起步,長大後遷到 Postgres"為例:
- 舊例項:進入「備份與遷移」,勾選「包含上傳檔案與生成產物」匯出完整備份並下載(配置了 Qdrant 的話向量自動在包裡)。
- 新例項:按 Docker Compose 部署拉起 Postgres 模式的全套服務,訪問站點完成
/setup首啟設定。首啟管理員的郵箱必須與舊例項管理員一致(密碼可以隨意設,匯入後以備份裡的密碼為準)。 - 用這個管理員登入,進入「系統 > 備份與遷移」,選擇歸檔、輸入
REPLACE、匯入。序列、外部索引鍵、路徑重寫全部自動處理。 - 匯入完成自動登出,用舊例項的賬號密碼重新登入。
- 驗證:使用者列表數量、抽查幾個對話與上傳檔案、知識庫檢索是否命中。若備份未含向量(舊例項沒配 Qdrant)或檢索異常,用下文「向量維護」重建。
反方向(Postgres 遷回 SQLite)步驟完全相同,見 SQLite 模式。
跨機遷移清單
換伺服器(引擎不變)時,按順序核對:
- 舊機匯出完整備份(含檔案、含向量)並下載到本地。
- 新機部署同版本或更新版本的映象(舊版本匯入新備份沒有保證)。
- 新機首啟
/setup用舊例項管理員的郵箱建號(降級規則,見上文紅色警告)。 - 歸檔大於 100 MB 且新機掛了 Cloudflare:準備好直連通道(本機
127.0.0.1:8787或灰雲子域),見 Cloudflare 代理配置。 - 匯入、重新登入、驗證(使用者 / 對話 / 檔案 / 知識庫檢索 / 渠道金鑰是否工作)。
- DNS 切到新機;舊機資料保留到驗證徹底完成之後再下線。
向量維護
「向量檢查」區管理 Qdrant 向量庫與資料庫的一致性,常見使用場景:匯入了一份不含向量的備份、從舊品牌歸檔遷移、Qdrant 資料卷丟失或更換了 Qdrant 例項。
| 操作 | 行為 | 費用 |
|---|---|---|
| 檢查向量 | 逐一核對資料庫中的文件切塊在 Qdrant 裡是否有非空向量,產出審計報告:應有 / 正常 / 缺失 / 空向量 / 跳過,並列出問題樣例 | 免費,只讀比對,不呼叫嵌入介面 |
| 重建缺失向量 | 對缺失與空向量的切塊,用資料庫裡已儲存的切塊文本重新呼叫嵌入模型,把向量寫回對應的 aivory_c<維度> collection;按嵌入模型分組分批執行,完成後報告重建 / 失敗數量 | 消耗嵌入 API 呼叫,大知識庫一次重建會產生真實的嵌入費用,執行前留意嵌入渠道的計費 |
要點:
- 重建不需要原始檔案,切塊文本本來就在資料庫裡,所以"備份未含向量"並不損失任何資料,只是需要花一次嵌入費用恢復檢索。
- 兩種任務都是非同步後臺任務,頁面輪詢進度;同一時間只能跑一個,且與備份匯出互斥。
- 前提是部署配置了 Qdrant 向量後端;未配置時按鈕直接報"向量後端未配置"。
升級與日常備份習慣
版本升級走滾動更新:
cd /opt/aivory # 你的 compose 目錄
docker compose pull
docker compose up -d
資料庫結構在新版本首次啟動時自動遷移(增量加列、建表),無需手工執行 SQL。即便如此,請養成兩個習慣:
- 升級前先匯出一份完整備份。自動遷移是前向的,回滾到舊版本不受保證;有歸檔在手,任何升級事故都能用一次匯入回到升級前。
- 定期匯出並離機儲存。
BACKUP_DIR裡的歸檔和資料庫在同一臺機器上,磁碟故障會一起丟;把下載的歸檔放到物件儲存或另一臺機器上才算真正的災備。配置 ZIP 也建議在每次大改配置後留一份。
小團隊自部署,每週一次全量(含檔案)+ 每次升級前一次,已經足以把最壞情況的損失控制在幾天之內。歸檔是普通 zip,可直接用任何物件儲存的生命週期策略做保留輪換。