Why RESP3 will be the only protocol supported by Redis 6

Salvatore Sanfilippo

為什麼 RESP3 將是 Redis 6 唯一支援的協定

原文由 Salvatore Sanfilippo 發布,訂閱此部落格

[編按!我正在重新考慮這一切,因為來自 Stack Overflow 的 Marc Gravell 建議我們可以為了向下相容而針對每個連線切換協定,只要發送一個指令來啟用 RESP3 就好。這表示不再需要一個切換伺服器行為的全域設定。這樣一來,對我而言就可接受多了,我也正在重新思考這篇部落格文章的核心觀點]

Redis 5 發布幾週後,我開始著手實作 RESP3,經過幾天的工作,終於看到這件事成真,感覺非常好。RESP3 是 Redis 從 6 版開始將採用的全新客戶端-伺服器協定。位於 https://github.com/antirez/resp3 的規格文件應該已經清楚說明了,這個對舊協定 RESP2 的演進將如何改善 Redis 生態系。但簡單來說,最重要的是 RESP3 比 RESP2 更具「語意」。舉例來說,它有了 map、set(無序的元素集合)、回傳資料的屬性等概念,可以用額外資訊來豐富回覆內容等等。最終目標是讓新的 Redis 客戶端需要做的事更少,也就是說,只要決定一套固定的規則,就能把每一種 RESP3 回覆型別轉換為客戶端函式庫所用程式語言中對應的適當型別。

在我對 Redis 未來的想像中,客戶端在底層會變得更聰明,盡力處理好連線、管線化與狀態管理,而在使用者面向的一端,看起來卻簡單得多,理想中的 Redis 客戶端甚至會像這樣:

result = redis.call(“GET”,keyname);

當然,在這之上你還可以建構更進階的抽象層,但最底層就應該長這樣,而且回傳的回覆不應該需要針對特定指令做臨時性的過濾:RESP3 的回傳型別本身就應該包含足夠的資訊,能直接回傳適當的資料型別。所以 HGETALL 會回傳 RESP3 的「map」,LRANGE 則會回傳「array」,而 EXISTS 會回傳 RESP3 的「boolean」。

這也讓新指令即使在客戶端函式庫並未*特別*為它設計的情況下,仍能如預期般運作。而在 RESP2 的時代,情況往往是該指令靠著像「method missing」這類機制勉強能動,但之後當客戶端函式庫*真正*去實作這個指令時,回傳的型別卻改變了,進而引入了難以察覺的不相容問題。

然而,雖然新協定只是對舊協定的一種漸進式改進,它仍會在客戶端函式庫端(當然)以及*應用程式層*帶來破壞性的不相容。舉例來說,ZSCORE 現在會回傳 double,而不是字串,因此應用程式碼就需要更新,或者,客戶端函式庫也可以提供一個相容選項,把 RESP3 的回覆轉回原本 RESP2 的型別。

Lua 腳本如果沒有針對新協定修改,也將無法再正常運作,因為 Lua 同樣會透過 redis.call() 指令看到更多具語意的型別回傳。同樣地,Lua 也將能夠回傳 RESP3 中實作的所有新資料型別。

正因為如此,大家對我的決定感到擔憂:我打算讓 Redis 6 *只*支援 RESP3。不會有任何相容模式讓你把 Redis 6 伺服器切回 RESP2,所以你要嘛升級客戶端函式庫並升級你的應用程式(或使用客戶端函式庫的向下相容模式),要嘛就無法升級到 Redis 6。

我這麼做是有充分理由的,我想說明為什麼會做出這個決定,以及我將如何為使用者和客戶端函式庫的作者減輕相關問題。先從緩解措施開始說起:

  • Redis 5 在 Redis 6 發布後仍會獲得為期 2 年的完整支援。所有關鍵的更新都會向後移植到 Redis 5,並持續提供修補層級的版本。
  • Redis 6 預計在約 1 到 1.5 年後發布。然而,Redis 6 大約在一個月內就會切換到 RESP3。因此大家將會有很長一段時間使用、實驗並接觸這個採用新協定的不穩定版 Redis。而和其他許多軟體不同,Redis 的不穩定版向來有不少一般使用者在使用,一方面因為它是 GitHub 上的預設分支,另一方面也因為傳統上 Redis 的不穩定版其實從來沒有真的那麼不穩定,這將帶來大量的前期曝光。
  • 關於這點我還不是百分之百確定,但 Lua 腳本引擎可能會提供一個相容模式,以回傳與 Redis 5 相同的型別。不過這個相容模式預設不會啟用,而是需要對每個執行的腳本主動選擇加入,做法是在呼叫 Redis 指令之前,先呼叫一個特殊的 redis.resp2_compat() 函式。因此,無論設定為何,每一台 Redis 6 伺服器的行為都將保持一致,就像過去 10 年來的 Redis 一樣。

以上是緩解措施。而接下來,則是我不會讓 Redis 6 同時支援兩種版本的原因:

  1. 這或多或少完全沒有用處。如果大家把 Redis 6 切換到 RESP2 模式,他們其實還停留在過去,只是在等 Redis 7 推出、拿掉對 RESP2 的支援,然後把一切搞壞。在此期間,當你面對一台 Redis 6 伺服器時,你永遠不知道它會*回傳什麼*,完全取決於它是如何設定的。於是同一個客戶端函式庫,針對同一個指令,可能回傳 Hash,也可能回傳 Array。
  2. 這會帶來更多工作與複雜度,卻沒有正當理由(見第「1」點)。許多指令將需要檢查舊協定,才能決定要用什麼格式來回覆。
  3. 把 Redis 6 的新功能與協定變更綁在一起,我們就是給了使用者充分的理由去進行轉換,並移植他們的客戶端與應用程式。到了某個時間點,一切都會結束,我們就能專注於新事物。否則,我們會有一群 Redis 6 的使用者為了新功能而升級到新伺服器,卻仍停留在舊協定上,到了 Redis 7 又會重演同樣的戲碼。
  4. 如果有人告訴你,改寫客戶端函式庫是一件極為繁重的工作,那我恐怕不敢苟同。是的,的確有些地方需要修改,但現在我正在實作伺服器端,我發現其實並沒有那麼糟。真正糟糕的是,大多數客戶端的工作根本沒有報酬,完全是出於熱情與樂於分享的精神才得以完成。我敢說,我們很快就會看到許多 RESP3 的實作出現。
  5. RESP3 的設計讓客戶端可以自動偵測對方是 RESP2 還是 RESP3,並自動切換,因此新的客戶端將同時適用於 Redis <= 5 與 Redis 6。

大概就是這樣了。希望這能闡明我的觀點及其背後的原因,同時,在協定切換期間將採取的緩解措施,也能說服使用者,這並不會是一次非常「劇烈」的破壞性變更。

本文章由 muse-spark-1.2-contributor 進行翻譯

留言