An update about Redis developments in 2019

Salvatore Sanfilippo

關於 Redis 在 2019 年的開發進展更新

昨天,一位關心 Redis 的使用者在 Hacker News 上寫下了以下內容:

https://news.ycombinator.com/item?id=19204436

我很喜歡 Redis,但對於目前開發中的一些變更抱持著些許懷疑。respv3 協定有一些功能,雖然聽起來很酷,但也可能會大幅增加客戶端函式庫程式碼的複雜度。還有許多工作投入在細緻化的 acl 上。我無法想像為什麼這會是必要的,或為什麼會比其他變更如多執行緒支援、更好的持久化模型、資料型別等擁有更高的優先順序。

— 使用者留言結束 —

我感覺他/她(不太確定)並不是唯一一個把 ACLs 視為因 Redis Labs 的目標而被強加的功能的人,理由是「企業用戶」之類的。此外,留言中的其他觀點也很有意思,我認為非常值得一一回應,以便向 Redis 社群清楚地傳達未來的方向。

為了方便說明,我會將這篇部落格文章分成幾個小節,逐一回應原始留言中提到的每個功能。

RESP3

如同我之前在這裡所寫的,RESP3 的目標其實是要簡化客戶端的整體面貌。理想上,每個客戶端都會有一個底層,不再試圖重新發明某種高階介面:redis.call(“get”,”foo”)。不再需要費心處理轉換,因為現在協定本身就具備足夠的語意,能告訴客戶端某個回覆在呼叫端手上應該呈現為什麼樣子,也不需要事先知道大多數指令的指紋(fingerprint)。我認為使用者所指的,應該是 RESP3 對 Out of Band Communications(帶外通訊)的支援,也就是回覆中的「attributes」。

我真心相信,在 Redis 的未來中,「Client Side Caching(客戶端快取)」將會是一件大事。這是每個可擴展系統邏輯上的下一步。然而,若沒有伺服器端的協助,客戶端快取失效將會是一場惡夢。這正是 RESP3 主要支援回覆中 attributes 的原因。不過,Redis 6 很可能*不會實作其中的任何功能*。即將成為 Redis 6 的 Redis unstable 已經有了一個幾乎完整的 RESP3 實作,而且其中並沒有 attributes。實作 RESP3 的客戶端如果想做到真正面向未來,只要選擇丟棄 attributes 即可,而且即使在未來的 Redis 版本中,若使用者沒有啟用某種特殊功能,attributes 很可能根本就不會被傳送。舉例來說,對於 Client Side Caching 來說,連線必須被置於某種特殊模式。此外,如你所知,Redis 6 將會與 RESP2 完全向下相容。事實上,我開始認為對 RESP2 的支援將永遠不會被移除,因為其成本幾乎為零,而且一旦我們已經投入心力實作了 RESP2 與 RESP3 之間的抽象層,就沒有什麼好理由去破壞向下相容性。

通常我不喜歡無緣無故地改動東西,然而 RESP2 的限制已經對客戶端生態系造成了很大的影響。我希望打造一個客戶端環境,讓使用者在不同客戶端之間切換時都能感到熟悉,而 API 就是 Redis 的 API,而不是客戶端作者自行發明的層級。順帶一提,我並不反對在底層 API 之外再提供*更高階*的 API,但應該要有一個共同的基礎,讓客戶端能夠在對指令一無所知的情況下發送指令。

ACLs

ACL 規範是我四年前親自撰寫的。我等了這麼久,是為了說服自己現在確實是實作它的時機:我們在沒有任何 ACL 的情況下走了很長一段路,只靠一些技巧,主要是重新命名指令。然而,請不要以為 ACL 的主要動機是企業客戶對安全性的需求。附帶的效果是,ACL 也能為了安全性而提供使用者驗證,但這項功能的主要目標是*維運*上的需求。

讓我舉個例子。你有一個 Redis 執行個體,並打算用它來做一件新事:延遲任務處理。你從網路上取得一個函式庫,看起來運作良好。現在,這個你並未逐行檢視過的函式庫,究竟憑什麼能夠呼叫「FLUSHALL」並瞬間清空你的資料庫?也許該函式庫的測試中就包含了這樣的指令,而你發現時為時已晚。或者,你剛聘請了一位資淺的開發者,他一直在 Redis 執行個體上呼叫「KEYS *」,而你們公司的 Redis 政策是「禁止使用 KEYS 指令」。

另一個情境是雲端服務供應商:他們需要小心地重新命名管理指令,甚至要想辦法掩蓋這些指令,避免因某些原因外洩。更多技巧:例如讓 MONITOR 不會在輸出中顯示這些指令。有了 ACLs,你可以設定 Redis,讓預設使用者在未經特定驗證的情況下,無法執行任何管理性或具危險性的操作。我認為這對維運來說將是一大改進。

此外,據我所知,ACLs 是我為 Redis 寫過最好的程式碼之一。幾乎完全沒有 CPU 成本,除非你使用 key patterns,但即使如此成本也很小。實作完全自包含在 acl.c 檔案中,核心的其他部分只有少數幾處對 ACL API 的呼叫。由於完全模組化,並沒有為系統增加複雜度。事實上,ACL 的程式碼還讓我們得以針對 AUTH 指令進行一些不錯的重構。

多執行緒

Redis 可能會獲得兩種多執行緒支援。我相信使用者所指的是「類似 memcached」的 Multi Threading(多執行緒),也就是讓單一 Redis 執行個體能夠擴展到多個執行緒,以便在 GET 或 SET 等簡單指令上提升每秒可處理的操作數。這涉及讓 I/O、指令解析等過程多執行緒化。所以我們就稱這種模式為「I/O Threading(I/O 多執行緒)」。

另一種多執行緒的做法,則是讓慢指令在不同的執行緒中執行,以免阻擋其他客戶端。我們將這種執行緒模型稱為「Slow Commands Threading(慢指令多執行緒)」。

好了,這就是計畫:據我所知,I/O Threading 不會在 Redis 中實現,因為經過深思熟慮後,我認為那會帶來大量複雜度卻沒有充分的理由。實際上,許多 Redis 的部署本來就是受限於網路或記憶體。此外,我非常信奉 Share-Nothing(無共享)架構,因此我想要擴展 Redis 的方式,是透過改進在同一主機上執行多個 Redis 執行個體的支援,特別是透過 Redis Cluster。關於這點,2019 年將會發生兩件事:

A) Redis Cluster 的多個執行個體將能夠協調運作,以更明智地使用本地執行個體的磁碟,也就是說,避免同時進行 AOF 重寫。

B) 我們將會作為 Redis 專案的一部分,發布一個 Redis Cluster Proxy(叢集代理),讓使用者即使在客戶端沒有完善的 Cluster 協定實作,也能將叢集抽象化。

另一件需要注意的事是,Redis 並不是 Memcached,但和 memcached 一樣,它是一個記憶體內系統。讓像 memcached 這樣擁有非常簡單資料模型的記憶體內系統多執行緒化,是非常合理的。對於磁碟儲存系統來說,多執行緒是必要的。而對於複雜的記憶體內系統來說,多執行緒則處於一個會讓事情變得棘手的中間地帶:Redis 的客戶端並非相互隔離,且資料結構很複雜。一個執行 LPUSH 的執行緒需要為其他執行 LPOP 的執行緒提供服務。能獲得的好處較少,卻要增加大量的複雜度。

相反地,我*真正非常想要*的是慢操作的多執行緒,而透過 Redis 模組系統,我們已經朝著正確的方向前進。然而在未來(不確定是在 Redis 6 還是 7),我們將在模組系統中實現 Key-Level Locking(鍵層級鎖定),讓執行緒能夠完全取得對某個鍵的控制權來處理慢操作。現在,模組可以實作指令,並以完全分離的方式為客戶端產生回覆,但要存取共享資料集仍需要全域鎖:這一點未來將會消失。

更完善的持久化

最近我們做了許多努力來改進 Redis 這類基礎功能。最近實作的最佳成果之一,就是在 AOF 檔案中加入 RDB 前導(preamble)。此外,在 Redis 4 和 5 中,關於複寫(replication)也投入了大量工作,現在的水準已與過去完全不可同日而語。是的,改進這些部分仍然是我的主要重點之一。

資料結構

現在 Redis 從第 5 版開始已經有了 Streams。對於 Redis 6 和 7,計畫首先是透過改變某些實作,讓現有的東西在記憶體使用上更有效率。然而,要新增資料結構還有很多需要考量的地方。我花了數年時間才想清楚,如何透過 streams,在時間序列與串流的情境下,填補 lists、pub/sub 和 sorted sets 之間的空隙。我真的希望 Redis 是一組正交的資料結構,讓使用者可以自行組合,而不是一組現成可用的*工具*。Streams 是一個抽象的日誌,所以我認為這是一個非常值得的補充。然而,對於其他東西,若未經非常長遠的考量,我並不完全確定它們是否值得被放進核心。無論如何,近年來在新增資料結構方面確實投入了更多心力。HyperLogLogs、更進階的位元操作、streams、阻塞式有序集合操作(ZPOP* 和 BZPOP*)以及 streams 都是很好的例子。

結論

我相信 Redis 社群應該要了解為什麼要做某件事,以及為什麼某些事反而被延後。我常犯的錯誤是透過 Twitter 來溝通,好像每個人都在那裡,但很多人其實有自己的生活 :-D,根本不在意。部落格是告知社群好得多的方式,我需要花更多時間寫部落格。順帶一提,我很喜歡寫文章,所以這是雙贏。需要意識到的一件重要事情是,Redis 並沒有一個穩固的路線圖,多年來我發現機會主義式的開發遠比擁有路線圖更有成效。有需求嗎?我看到需要了嗎?我有心情寫程式嗎?現在是對的時機,因為沒有其他更重大的優先事項嗎?有一群使用者正在協助設計過程,提供提示、想法、測試東西嗎?時機對了,那就動手做吧。為 Redis 制定一個穩固的路線圖是很愚蠢的,因為開源核心團隊的規模很小,有時我會因為某個隨機的當機問題卡上好幾個星期……任何固定的長期計畫都行不通。此外,隨著 Redis 社群提供回饋,我的想法也會大幅改變,所以我每個月都得重寫路線圖。然而,寫部落格至少是一個不錯的解決方案,可以展示目前優先事項/想法的版本,並說明為什麼其他想法被放棄了。

最後一點補充:我在 Redis Labs 對於要在開源專案中放入什麼內容所擁有的自由度幾乎是無限的。我認為這在業界算是某種奇蹟,或者只是我在 Redis Labs 共事的人都是善良的人,他們理解我們所做的一切源自開源運動,並認為以這種方式持續下去是明智的。但這並不常見。如果我在 Redis 的路線圖上犯了錯,那肯定是我的錯。

原文由 Salvatore Sanfilippo 發布

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