Redis(設計與實現):---叢集之ASK錯誤、ASKING命令、REDIS_ASKING標識

你走吧起風了__發表於2020-11-03

一、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錯誤再 次出現

相關文章