Redis Cluster 中 EVAL 報 CROSSSLOT 錯誤是因為腳本中所有 key 必須落在同一哈希槽,而 Cluster 僅校驗 KEYS 數(shù)組中的 key,要求其哈希標簽一致(如 user:{1001}:a 和 user:{1001}:b),動態(tài)拼接的 key 不參與預(yù)檢,運行時可能觸發(fā) MOVED。
為什么 EVAL 在 Redis Cluster 中會報 CROSSSLOT 錯誤
Redis Cluster 要求同一個 Lua 腳本里所有 key 必須落在同一個哈希槽(hash slot)上,否則執(zhí)行 EVAL 時直接返回 CROSSSLOT Keys in request don't hash to the same slot。這不是 Lua 本身的問題,而是 Cluster 架構(gòu)的硬性約束:腳本必須路由到唯一節(jié)點執(zhí)行,不能跨節(jié)點協(xié)調(diào)。
常見觸發(fā)場景包括:
- 腳本中硬編碼多個不同 key,比如
GET user:1001和GET order:2002(前綴不同,大概率不在同槽) - 用
KEYS[1]和KEYS[2]傳入兩個不相關(guān)的 key - 在腳本里拼接 key(如
"user:"..ARGV[1]),但沒確保所有生成 key 都屬于同一槽
如何讓 key 落在同一 slot:用 {...} 手動控制哈希標簽
Redis Cluster 計算 slot 時,只取 key 中第一個 { 和其后第一個 } 之間的字符串做 CRC16。只要多個 key 的花括號內(nèi)部分一致,它們就必然落在同一 slot。
實操建議:
- 設(shè)計 key 時主動嵌入統(tǒng)一標簽,例如
user:{1001}:profile和user:{1001}:settings—— 兩者都按"1001"計算 slot - 避免
user:{1001}:profile和order:{1001}:items混用 —— 標簽不同("1001"vs"1001"看似相同,但實際是兩個獨立標簽;不過這里其實相同,真正危險的是user:{1001}和order:{2002}) - 不要依賴業(yè)務(wù) ID 自身“看起來一樣”:
user:1001和cart:1001完全無關(guān),slot 必然不同
EVALSHA 能繞過 slot 檢查嗎?不能,校驗邏輯完全一致
EVALSHA 只是復(fù)用已加載腳本的 SHA1 值,不改變 key 路由規(guī)則。Cluster 仍會在執(zhí)行前解析腳本中的 KEYS 數(shù)組,并對每個 key 做 slot 校驗。
關(guān)鍵點:
- 即使腳本內(nèi)容不變,只要傳入的
KEYS參數(shù)跨槽,EVALSHA一樣報CROSSSLOT -
SCRIPT LOAD本身不校驗 slot,它只是存腳本;問題一定出在EVAL/EVALSHA調(diào)用時 - 某些客戶端(如 Jedis、redis-py)在 Cluster 模式下會自動嘗試
EVALSHA,但失敗后回退EVAL,這不會規(guī)避 slot 限制
真正安全的 Cluster Lua 腳本寫法
omegafw.gmcwatch.cn
rolexfw.gmcwatch.cn
patekfw.gmcwatch.cn
omegafw.swatchsh.com
rolexfw.swatchsh.com
patekfw.swatchsh.com
omegafw.paydyj.com
rolexfw.paydyj.com
patekfw.paydyj.com
omegafw.watchku.com
rolexfw.watchku.com
patekfw.watchku.com
omegafw.gmcwatch.cn
rolexfw.gmcwatch.cn
patekfw.sitezj.cn
核心原則:把 slot 綁定責(zé)任交給調(diào)用方,而不是腳本內(nèi)部動態(tài)計算。
正確做法:
- 腳本只操作
KEYS[1]、KEYS[2]…,且文檔明確要求這些 key 必須屬于同一邏輯實體(如“同一用戶的所有數(shù)據(jù)”) - 調(diào)用時傳入的 key 必須帶一致的哈希標簽,例如:
EVAL "return {KEYS[1], KEYS[2]}" 2 user:{1001}:a user:{1001}:b - 避免在 Lua 里用
redis.call("GET", "user:"..ARGV[1])這類硬編碼拼接 —— 這種 key 不在KEYS數(shù)組里,Cluster 無法預(yù)檢,運行時可能報MOVED或ASK錯誤 - 如果必須動態(tài)構(gòu)造 key,確保構(gòu)造邏輯和哈希標簽強綁定,例如
"user:{"..ARGV[1].."}:cache",并要求ARGV[1]是穩(wěn)定標簽值
最易被忽略的一點:Cluster 不檢查 Lua 腳本體內(nèi)的字符串拼接結(jié)果是否合法,只檢查顯式傳入 KEYS 數(shù)組的那些 key。所以看似“繞過”的寫法,往往在運行時才暴露問題。