Fallback 模式讀取
本頁說明 fallback 模式下 Get 如何從記憶體與本地 JSON 檔取值,以及從檔案還原的值會是什麼型別。
流程
graph TB
Start[Get key] --> M{記憶體命中?}
M -->|命中且有效| Ret[回傳 Data]
M -->|命中但過期| Exp[刪記憶體,回傳 Not found]
M -->|未命中| F[讀取 md5 分片路徑]
F -->|檔案不存在| NF[Not found]
F -->|JSON 解析失敗| PF[Failed to parse]
F -->|已過期| FE[刪檔,回傳 Not found]
F -->|有效| Back[回填記憶體並回傳]
步驟
| 步驟 | 條件 | 行為 | 原始碼 |
|---|---|---|---|
| 1 | 記憶體命中且未過期 | 立即回傳 | get.go:64-75 |
| 2 | 記憶體命中但已過期 | 刪除記憶體項目,回傳 Not found;不查檔案,檔案交給背景掃除 |
get.go:68-72 |
| 3 | 記憶體未命中 | 讀取 {DBPath}/{DB}/{md5[0:2]}/{md5[2:4]}/{md5[4:6]}/{md5}.json |
get.go:81-86 |
| 4 | 檔案不存在 | 回傳 Not found |
get.go:85-87 |
| 5 | 內容不是合法 Cache JSON |
回傳 Failed to parse |
get.go:89-93 |
| 6 | 已過期 | 刪除檔案,回傳 Not found |
get.go:95-100 |
| 7 | 有效 | 寫入記憶體快取(下一次讀取直接命中),回傳 Data |
get.go:102-106 |
讀得到哪些資料
| 資料來源 | Fallback 模式下能否讀到 |
|---|---|
本實例在 fallback 期間 Set 的值 |
能。Set 先寫記憶體,再排入批次寫檔,寫檔前就能讀到 |
| 本實例在正常模式期間讀寫過的值 | 能,仍在記憶體快取中 |
| 上一個程序在 fallback 期間寫出的檔案 | 能,從檔案還原 |
| 只存在於 Redis、本實例從未讀過的值 | 不能,fallback 模式不存取 Redis |
從檔案還原的型別
記憶體快取保存寫入時的原始 Go 值;從檔案還原的值經 encoding/json 解碼為 interface{},型別會改變:
| 寫入時的值 | 記憶體命中時 | 從檔案還原時 |
|---|---|---|
"hello" |
string |
string |
42 |
int |
float64 |
map[string]any{"uid": 1} |
map[string]any(uid 為 int) |
map[string]interface{}(uid 為 float64) |
| 自訂 struct | 原 struct | map[string]interface{} |
需要固定型別時,呼叫端應以 json.Marshal → json.Unmarshal 轉回目標型別,而非直接 type assertion。
範例:重啟後從檔案讀取
// 程序 A 在 Redis 失聯期間寫入,批次寫檔後結束
if err := rf.Set("session:abc", map[string]any{"uid": 1}, time.Hour); err != nil {
log.Fatal(err)
}
// 程序 B 啟動時 Redis 仍失聯 → fallback 模式;記憶體為空,改讀檔案
value, err := rf.Get("session:abc")
if err != nil {
log.Fatal(err)
}
uid := value.(map[string]interface{})["uid"].(float64) // 從檔案還原後為 float64
fmt.Println(uid)