# 持久化

本頁說明 ToriiDB 的磁碟結構、snapshot 與 log 共用的紀錄格式，以及每次寫入如何落盤。

## 儲存結構

每個資料庫的目錄內含編號的 snapshot 與 log 檔：

```text
<dir>/
└── db_0/
    ├── 00000002.snap
    ├── 00000003.log
    ├── 00000003.snap
    └── 00000004.log
```

| 檔案 | 內容 |
|---|---|
| `NNNNNNNN.snap` | 完整狀態：壓縮當下每個存活項目一行 `SET` |
| `NNNNNNNN.log` | 編號前一個 snapshot 之後附加的操作 |
| `*.tmp` | 寫入中的 snapshot；若當機殘留，下次載入時移除 |

Snapshot `N` 包含到 log `N` 為止（含）的所有內容。檔名必須是剛好八位數字加 `.snap` 或 `.log`；目錄內其他檔案一律忽略。

沒有逐鍵檔案鏡像。Snapshot 與 log 是磁碟上唯一的事實來源。

## 紀錄格式

Snapshot 與 log 使用相同的行格式：一個 JSON `AOFRecord` 加 `\n`：

| 欄位 | 意義 |
|---|---|
| `ts` | 操作的 Unix 時間戳（snapshot 中為 entry 的 `CreatedAt`） |
| `cmd` | `SET`、`DEL`、`EXPIRE`、`EXPIREAT` 或 `PERSIST` |
| `key` | 受影響的鍵 |
| `value` | `SET` 的值；`EXPIRE`/`EXPIREAT` 的秒數或時間戳文字 |
| `expire_at` | 選用的絕對過期時間 |
| `vector` | 選用的 base64 little-endian `float32` 向量 |

## 寫入路徑

每個修改都在資料庫寫鎖下執行：

1. 更新記憶體中的 entry。
2. 若目前 log 尚未開啟，以 `O_CREATE|O_APPEND|O_WRONLY` 開啟。
3. 附加紀錄並呼叫 `File.Sync()`。
4. 若 log 大小 >= max(snapshot 大小, 1 MiB) * 2，執行壓縮。

門檻刻意相對於 snapshot：固定門檻會在存活資料逼近門檻後，幾乎每次寫入都重寫整個資料庫。

附加向量與寫入 embedding 快取也會各自附加 `SET` 紀錄。每分鐘的過期清理只從記憶體刪除，不寫任何紀錄。

## 相關頁面

- [重播與復原](/zh/persistence-replay)
- [壓縮與回收](/zh/persistence-compaction)
- [架構](/zh/architecture)
