Redis(設計與實現):---叢集之ASK錯誤、ASKING命令、REDIS_ASKING標識
一、ASK錯誤
在進行重新分片期間,源節點向目標節點遷移一個槽的過程中,可能會出現這樣一種情況:屬於被遷移槽的一部分鍵值對儲存在源節點裡面,而另一部分鍵值對則儲存在目標節點 裡面
當客戶端向源節點傳送一個與資料庫鍵有關的命令,並且命令要處理的資料庫鍵恰好就屬於正在被遷移的槽時:
- 源節點會先在自己的資料庫裡面查詢指定的鍵,如果找到的話,就直接執行客戶端傳送的命令(底層實現:如果節點收到一個關於鍵key的命令請求,並且鍵key所屬的槽i正好就指派給了這個節點,那麼節點會嘗試在自己的資料庫裡查詢鍵key,如果找到了的話,節點就直接執行客戶 端傳送的命令)
- 相反地,如果源節點沒能在自己的資料庫裡面找到指定的鍵,那麼這個鍵有可能已經被遷移到了目標節點,源節點將向客戶端返回一個ASK錯誤,指引客戶端轉向正在匯入槽的目標節點,並再次傳送之前想要執行的命令(底層實現:如果節點沒有在自己的資料庫裡找到鍵key,那麼節點會檢查自己的clusterState.migrating_slots_to[i],看鍵key所屬的槽i是否正在進行遷移,如果槽i的確在進行 遷移的話,那麼節點會向客戶端傳送一個ASK錯誤,引導客戶端到正在匯入槽i的節點去查詢 鍵key)
下圖展示了源節點判斷是否需要向客戶端傳送ASK錯誤的整個過程
單機模式/叢集模式下ASK錯誤的顯示
與前面介紹的MOVED錯誤情況類似:
- 叢集模式的redis-cli在接到ASK錯誤時也不會列印錯誤,而是自動根據錯誤提供的IP地址和埠進行轉向動作
- 而單機模式的redis-cli客戶端會列印ASK錯誤
- 備註: 叢集模式的redis-cli並未支援ASK自動轉向,上面展示的ASK自動轉向行為實際上是根據MOVED自動轉向行為虛構出來的。因此,當叢集模式的redis-cli真正支援ASK自動轉向時,它的行為和上面展示的行為可能會有所不同
演示案例 舉個例子,假設節點7002正在向節點7003遷移槽16198,這個槽包含"is"和"love"兩個鍵, 其中鍵"is"還留在節點7002,而鍵"love"已經被遷移到了節點7003
- 如果我們向節點7002傳送關於鍵"is"的命令,那麼這個命令會直接被節點7002執行:
- 而如果我們向節點7002傳送關於鍵"love"的命令,那麼客戶端會先被轉向至節點7003, 然後再次執行命令:
演示案例
- 假設在節點7002向節點7003遷移槽16198期間,有一個客戶端向節點7002傳送命令:
c GET "love"
因為鍵"love"正好屬於槽16198,所以節點7002會首先在自己的資料庫中查詢鍵"love",
但並沒有找到,通過檢查自己的clusterState.migrating_slots_to[16198],節點7002發現自己正
在將槽16198遷移至節點7003,於是它向客戶端返回錯誤:
c ASK 16198 127.0.0.1:7003
這個錯誤表示客戶端可以嘗試到IP為127.0.0.1,埠號為7003的節點去執行和槽16198有關的操作,如下圖所示:
接到ASK錯誤的客戶端會根據錯誤提供的IP地址和埠號,轉向至正在匯入槽的目標節 點,然後首先向目標節點傳送一個ASKING命令,
之後再重新傳送原本想要執行的命令
以前面的例子來說,當客戶端接收到節點7002返回的以下錯誤時:
ASK 16198 127.0.0.1:7003
客戶端會轉向至節點7003,首先傳送命令:
c ASKING
- 然後再次傳送命令:
c GET "love"
- 並獲得回覆:
c "you get the key 'love'"
- 整個過程如下圖所示:
二、ASKING命令(REDIS_ASKING標識)
ASKING命令功能:唯一要做的就是開啟傳送該命令的客戶端的REDIS_ASKING標識
- 以下是ASKING命令的虛擬碼實現:
def ASKING():
# 開啟標識
client.flags |= REDIS_ASKING
# 向客戶端返回OK 回覆
reply("OK")
- **REDIS_ASKING標識:**在一般情況下,如果客戶端向節點傳送一個關於槽i的命令,而槽i又沒有指派給這個節點的話,那麼節點將向客戶端返回一個MOVED錯誤;但是,如果節點的
clusterState.importing_slots_from[i]顯示節點正在匯入槽i,並且傳送命令的客戶端帶有REDIS_ASKING標識,那麼節點將破例執行這個關於槽i的命令一次,下圖展示了這個判斷過程。
- 當客戶端接收到ASK錯誤並轉向至正在匯入槽的節點時,客戶端會先向節點傳送一個 ASKING命令,然後才重新傳送想要執行的命令,這是因為如果客戶端不傳送ASKING命令,而直接傳送想要執行的命令的話,那麼客戶端傳送的命令將被節點拒絕執行,並返回 MOVED錯誤
- 另外要注意的是,客戶端的REDIS_ASKING標識是一個一次性標識,當節點執行了一個帶有REDIS_ASKING標識的客戶端傳送的命令之後,客戶端的REDIS_ASKING標識就會被移除。 舉個例子,如果我們在成功執行GET命令之後,再次向節點7003傳送GET命令,那麼第 二次傳送的GET命令將執行失敗,因為這時客戶端的REDIS_ASKING標識已經被移除:
演示案例 舉個例子,我們可以使用普通模式的redis-cli客戶端,向正在匯入槽16198的節點7003傳送以下命令:
- 雖然節點7003正在匯入槽16198,但槽16198目前仍然是指派給了節點7002,所以節點7003會向客戶端返回MOVED錯誤,指引客戶端轉向至節點7002
- 但是,如果我們在傳送GET命令之前,先向節點傳送一個ASKING命令,那麼這個GET命令就會被節點7003執行:
- 另外要注意的是,客戶端的REDIS_ASKING標識是一個一次性標識,當節點執行了一個帶有REDIS_ASKING標識的客戶端傳送的命令之後,客戶端的REDIS_ASKING標識就會被移除
三、ASK錯誤和MOVED錯誤的區別
ASK錯誤和MOVED錯誤都會導致客戶端轉向,它們的區別在於:
- MOVED錯誤代表槽的負責權已經從一個節點轉移到了另一個節點: 在客戶端收到關於槽i的MOVED錯誤之後,客戶端每次遇到關於槽i的命令請求時,都可以直接將命令請求傳送至MOVED錯誤所指向的節點,因為該節點就是目前負責槽i的節點。
- 與此相反,ASK錯誤只是兩個節點在遷移槽的過程中使用的一種臨時措施: 在客戶端收 到關於槽i的ASK錯誤之後,客戶端只會在接下來的一次命令請求中將關於槽i的命令請求發 送至ASK錯誤所指示的節點,但這種轉向不會對客戶端今後傳送關於槽i的命令請求產生任何 影響,客戶端仍然會將關於槽i的命令請求傳送至目前負責處理槽i的節點,除非ASK錯誤再 次出現
相關文章
- GO實現Redis:GO實現Redis叢集(5)GoRedis
- redis叢集報錯Redis
- 探索Redis設計與實現13:Redis叢集機制及一個Redis架構演進例項Redis架構
- 認識Redis叢集——Redis ClusterRedis
- 《Redis設計與實現》知識點目錄Redis
- Redis設計與實現Redis
- 《redis設計與實現》Redis
- Redis 叢集實現原理探討Redis
- Golang 實現 Redis(7): Redis 叢集與一致性 HashGolangRedis
- Redis服務之Redis5叢集相關命令說明Redis
- redis與叢集實用操作筆記Redis筆記
- Redis叢集實現方案選型分析Redis
- 進階的Redis之雜湊分片原理與叢集實戰Redis
- Redis 叢集原理與使用Redis
- Redis叢集與高可用Redis
- Redis設計於實現之字典Redis
- 11、redis使用ruby實現叢集高可用Redis
- Redis 設計與實現 4:字典Redis
- Redis 設計與實現 (九)--LuaRedis
- Redis系列:搭建Redis叢集(叢集模式)Redis模式
- 玩轉Redis叢集之SentinelRedis
- 玩轉Redis叢集之CodisRedis
- 玩轉Redis叢集之ClusterRedis
- 無人機叢集自組織搜尋模擬模型設計與實現無人機模型
- 探索Redis設計與實現10:Redis的事件驅動模型與命令執行過程Redis事件模型
- SpringSession+Redis實現叢集會話共享SpringGseSessionRedis會話
- Redis 設計與實現:資料庫Redis資料庫
- <Redis設計與實現>筆記【轉】Redis筆記
- Redis 設計與實現 3:字串 SDSRedis字串
- Redis 設計與實現 (三)--持久化Redis持久化
- Redis 設計與實現 (七)--事務Redis
- redis 叢集Redis
- redis叢集Redis
- Redis | 第7章 Redis 伺服器《Redis設計與實現》Redis伺服器
- Redis叢集案例與場景分析Redis
- Redis叢集搭建與簡單使用Redis
- docker搭建redis叢集和Sentinel,實現故障轉移DockerRedis
- Shiro+Redis實現tomcat叢集session共享RedisTomcatSession