來源:antirez
翻譯:Kevin (公眾號:中間件小哥)
Redis 5 中引入了一個(gè)名為 Streams 的新的 Redis 數(shù)據(jù)結(jié)構(gòu),吸引了社區(qū)極大的興趣。接下來,我會在社區(qū)里進(jìn)行調(diào)查,同用戶們談?wù)勊麄冊趯?shí)際生產(chǎn)中的使用場景,然后寫個(gè)博客記錄一下。
今天我想解決另一個(gè)問題:我有點(diǎn)懷疑許多用戶僅僅把Streams 作為解決類似 Kafka 所要解決的問題的一個(gè)手段。實(shí)際上,這個(gè)數(shù)據(jù)結(jié)構(gòu),在當(dāng)初設(shè)計(jì)的時(shí)候,在生產(chǎn)者/消費(fèi)者消息通信的場景下,也是可以用起來的。而且我意識到 Streams 是很擅長這個(gè)場景的,用法也很簡潔。Streaming 是一個(gè)很好的模式和“思維模型”,在被用來設(shè)計(jì)系統(tǒng)時(shí),可以獲得巨大的成功。但是 Redis Streams 就像大多數(shù) Redis 數(shù)據(jù)結(jié)構(gòu)一樣,是比較通用的結(jié)構(gòu),可以用來對許多不同的問題進(jìn)行建模。在本篇博文中,我將聚焦在作為純粹數(shù)據(jù)結(jié)構(gòu)的?Streams,完全忽略其阻塞式的操作、消費(fèi)者群組和所有和消息通訊有關(guān)的部分。
作為?CSV?文件加強(qiáng)版的?Streams
如果你要把一系列結(jié)構(gòu)化的數(shù)據(jù)項(xiàng)記錄下來,并且覺得用數(shù)據(jù)庫畢竟有點(diǎn)“殺雞用牛刀”,那么你可能會說:讓我們以“僅追加”(append only)模式打開一個(gè)文件,然后把每一行作為 CSV(逗號分隔的值)格式記錄下來:
(以 append only 模式打開 data.csv 文件)
time=1553096724033,cpu_temp=23.4,load=2.3
time=1553096725029,cpu_temp=23.2,load=2.1
看起來是很簡單的,是吧,人們一直也是這么做的:這是一個(gè)一致的模式,如果你知道你在做什么的話。但是和這個(gè)(文件)模式對等的 in-memory(內(nèi)存)模式是怎樣的呢?內(nèi)存比 append only 文件更強(qiáng)大,自然也就沒有類似 CSV 文件的一些限制:
做范圍查詢比較難(效率低);
太多冗余信息:每條記錄中的時(shí)間差不多是一樣的,而且許多列都是重復(fù)的。同時(shí),在你想切換到不同的一組列時(shí),如果移除這些冗余信息,這會使得格式的靈活性更低。
數(shù)據(jù)項(xiàng)的位移就是文件中的字節(jié)位移:如果我們改變文件的結(jié)構(gòu),那么位移值就會是錯(cuò)的,所以實(shí)際上這里沒有真正的 primary Id 的概念。
我不能移除這些數(shù)據(jù)條目,在沒有 GC(垃圾收集)能力的情況下,只能將他們標(biāo)記為“失效”,如果不重寫 log(日志)的話。而且因?yàn)槟承┰?,日志重寫的性能很差,如果能夠避免的話,就再好不過了。
從另外一個(gè)角度看,這些 CSV 條目的日志也有好的方面:他們沒有固定的結(jié)構(gòu),數(shù)據(jù)列可以變化,容易生成,而且畢竟其結(jié)構(gòu)也是比較緊湊的。Redis Streams 的設(shè)計(jì)理念就是取長補(bǔ)短,其結(jié)果就是一個(gè)和 Redis Sorted sets 非常類似的混合型數(shù)據(jù)結(jié)構(gòu):他們看起來像是一個(gè)基礎(chǔ)數(shù)據(jù)結(jié)構(gòu),為了達(dá)到這樣一個(gè)效果,在底層他們有多種表現(xiàn)形式。
Streams 101
(你可以跳過這個(gè)部分,如果你已經(jīng)了解 Redis Streams 的基礎(chǔ)的話)
Redis Streams 由差分壓縮(delta-compressed)的宏節(jié)點(diǎn)表示,這些節(jié)點(diǎn)通過基數(shù)樹(radix tree)連接在一起。其效果就是,可以非??斓倪M(jìn)行隨機(jī)查找、按需獲取范圍、刪除老的數(shù)據(jù)項(xiàng),從而創(chuàng)建一個(gè)帶上限的 stream,等等。同時(shí),給程序員的接口和 CSV 文件是非常類似的:
> XADD mystream * cpu-temp 23.4 load 2.3
"1553097561402-0"
> XADD mystream * cpu-temp 23.2 load 2.1
"1553097568315-0"
從上面的例子我們看到,XADD 命令自動產(chǎn)生和返回了記錄 ID,記錄 ID 是單調(diào)遞增的,由 2 個(gè)部分組成:<時(shí)間>-<計(jì)數(shù)器>,時(shí)間以毫秒表示,對于在同一毫秒中產(chǎn)生的記錄,計(jì)數(shù)器會遞增。
以“只追加(append only)CSV 文件”的思想作為基礎(chǔ),我們構(gòu)建的第一個(gè)新的抽象是:既然我們使用星號作為 XADD 命令的 ID 參數(shù),從服務(wù)側(cè)我們就可以免費(fèi)得到記錄 ID。這個(gè) ID 不僅可以用來指示一個(gè) stream 中的某一條數(shù)據(jù)記錄,也關(guān)聯(lián)了這條記錄加入 stream 的時(shí)間。實(shí)際上,XRANGE 命令既可以做范圍查詢,也可以查詢單條記錄。
> XRANGE mystream 1553097561402-0 1553097561402-0
1) 1) "1553097561402-0"
?? 2) 1) "cpu-temp"
?? 2) "23.4"
?? 3) "load"
?? 4) "2.3"
在這個(gè)例子中,為了標(biāo)識單個(gè)元素,我使用了相同的 ID 作為范圍查詢的起止條件。但是,我也可以使用任何范圍條件,加上一個(gè) COUNT 參數(shù)來限制查詢結(jié)果的個(gè)數(shù)。同樣的,也不必詳細(xì)指明完整的 ID 作為范圍條件,可以只用 ID 的 Unix 毫秒時(shí)間戳部分,來獲取給定時(shí)間范圍內(nèi)的元素。
> XRANGE mystream 1553097560000 1553097570000
1) 1) "1553097561402-0"
?? 2) 1) "cpu-temp"
?? 2) "23.4"
?? 3) "load"
?? 4) "2.3"
2) 1) "1553097568315-0"
?? 2) 1) "cpu-temp"
?? 2) "23.2"
?? 3) "load"
?? 4) "2.1"
現(xiàn)在,沒必要展示更多的 Streams API 了,詳細(xì)的內(nèi)容可以參考 Redis 文檔。讓我們聚焦在其使用模式上:XADD 用來添加元素,XRANGE(也包括 XREAD)是用來獲取范圍內(nèi)的元素(取決于你的目的),讓我們看下為什么我把 Streams 稱為一個(gè)如此強(qiáng)大的數(shù)據(jù)結(jié)構(gòu)。
如果你想對 Streams 及其 API 了解更多的話,請一定看下這篇教程:https://redis.io/topics/streams-intro
網(wǎng)球選手
幾天前我和一個(gè)最近正在學(xué)習(xí) Redis 的朋友一起對一個(gè)應(yīng)用進(jìn)行建模,這個(gè)應(yīng)用是用來記錄本地的網(wǎng)球場、本地的選手和比賽的。用來對選手建模的方法是顯而易見的:一個(gè)選手是一個(gè)小的對象,所以一個(gè) hash 值加上選手:<id>的鍵就夠了。當(dāng)你使用 Redis 作為首要的應(yīng)用數(shù)據(jù)建模的手段,你會馬上意識到,你需要一個(gè)方法來記錄在一個(gè)給定網(wǎng)球俱樂部中舉行的比賽。如果選手 1 和選手 2 打了一場比賽,選手 1 贏了,我們可以在一個(gè) stream 中記錄如下:
> XADD club:1234.matches * player-a 1 player-b 2 winner 1
"1553254144387-0"
通過這個(gè)簡單的操作,我們得到了:
一個(gè)唯一的比賽 ID:stream 中的 ID;
不需要為了標(biāo)識一場比賽而創(chuàng)建一個(gè)對象;
免費(fèi)的范圍查詢可以對比賽記錄進(jìn)行分頁,也可以查看在過去一個(gè)給定時(shí)刻的比賽記錄;
在 Streams 出現(xiàn)前,我們需要創(chuàng)建一個(gè)按時(shí)間排序的 sorted set。sorted set 中的元素就是比賽的 ID,同時(shí)還需要作為 hash 值保存在一個(gè)不同的 key 中。這不僅意味著更多的工作,同時(shí)也帶來了難以想象的內(nèi)存浪費(fèi)。還有更多的你能想到的情況(后面可以看到)。
目前,可以看到的一點(diǎn)是,Redis Streams 就是一種處于僅追加模式(append only)的 Sorted Set,以時(shí)間作為鍵,每個(gè)元素是一個(gè)小的 hash 值。在對 Redis 進(jìn)行建模的場景下,帶來革命性的一點(diǎn)就是他的簡潔。
內(nèi)存使用
上述用例不僅意味著一個(gè)從行為上看更為一致的模式。比起老的 Sorted set + hash 的方式,Stream 方案的內(nèi)存開銷是如此之低,以至于之前不具有可行性的東西,現(xiàn)在完全是可行的。
以下數(shù)字是按之前的配置計(jì)算的、保存 100 萬條比賽數(shù)據(jù)的開銷:
Sorted Set + Hash 內(nèi)存開銷 = 220 MB (242 RSS)
Stream 內(nèi)存開銷 = 16.8 MB (18.11 RSS)
這超過了一個(gè)數(shù)量級的差別(準(zhǔn)確的說是 13 倍的差別),而且這意味著那些之前在內(nèi)存中開銷太大的用例,現(xiàn)在完全是可行的。神奇的地方就在于 Redis Streams:宏節(jié)點(diǎn)可以包含多個(gè)以 listpack 數(shù)據(jù)結(jié)構(gòu)、非常緊湊的方式編碼的元素。例如,即使整數(shù)在語義上是字符串,但 listpack 可以把他們編碼為二進(jìn)制形式。在這個(gè)基礎(chǔ)上,我們可以進(jìn)行差分壓縮和“相同列”的壓縮。同時(shí),因?yàn)楹旯?jié)點(diǎn)在基數(shù)樹(在設(shè)計(jì)上僅占用很少的內(nèi)存)中鏈接在一起,我們也可以通過 ID 和時(shí)間進(jìn)行查詢。所有這些加在一起,使得內(nèi)存占用很少。有意思的是,在語義上,用戶看不到任何使得 Streams 如此高效的實(shí)現(xiàn)細(xì)節(jié)。
現(xiàn)在,讓我們做一個(gè)簡單的計(jì)算。如果我可以用 18MB 的內(nèi)存存儲 1 百萬條記錄,180MB 存 1 千萬條,1.8GB 存 1 億條記錄。如果有 18GB 內(nèi)存的話,可以存 10 億條記錄。
時(shí)間序列
依我看,我們需要重點(diǎn)關(guān)注的是,上述我們使用 Stream 表示網(wǎng)球比賽的用法,在語義上,同使用 Stream 處理一個(gè)時(shí)間序列是完全不同的。是的,邏輯上我們?nèi)匀辉谟涗浤撤N事件,但一個(gè)重要的區(qū)別是,在一種場景下,我們記錄和創(chuàng)建記錄條目來呈現(xiàn)對象;在時(shí)間序列場景下,我們只是測量某些外部發(fā)生的事情,而這并不會表示成一個(gè)對象。你可能認(rèn)為這個(gè)區(qū)別不重要,但其實(shí)不然。對于 Redis 用戶,重要的一點(diǎn)是需要建立一個(gè)概念,Redis Streams 可以用來創(chuàng)建具有全序的小對象,每個(gè)對象都有一個(gè) ID。
時(shí)間序列是一個(gè)最基礎(chǔ)的使用場景,顯然,也是最重要的使用場景,但在 Streams 出現(xiàn)前,Redis 對這種場景是有些無能為力的。Streams 的內(nèi)存特性和靈活性,加上帶上限的 stream(capped stream)的能力(參考 XADD 命令的參數(shù)選項(xiàng)),在開發(fā)者的手中是一個(gè)非常有力的工具。
結(jié)論
Streams 是非常靈活的,而且有很多使用場景。好了,話不多說,上述的例子我想要傳達(dá)的一個(gè)關(guān)鍵信息就是關(guān)于內(nèi)存使用的分析,也許對于許多讀者來說這已經(jīng)很明顯了,但是最近幾個(gè)月和人們的交談給我一種感覺,在 Streams 和 Streams 的使用場景之間有著很強(qiáng)的關(guān)聯(lián)性,就好像這個(gè)數(shù)據(jù)結(jié)構(gòu)只擅長這種場景一樣,但其實(shí)不是這樣的。:-)
多優(yōu)質(zhì)中間件技術(shù)資訊/原創(chuàng)/翻譯文章/資料/干貨,請關(guān)注“中間件小哥”公眾號!
另外有需要云服務(wù)器可以了解下創(chuàng)新互聯(lián)cdcxhl.cn,海內(nèi)外云服務(wù)器15元起步,三天無理由+7*72小時(shí)售后在線,公司持有idc許可證,提供“云服務(wù)器、裸金屬服務(wù)器、高防服務(wù)器、香港服務(wù)器、美國服務(wù)器、虛擬主機(jī)、免備案服務(wù)器”等云主機(jī)租用服務(wù)以及企業(yè)上云的綜合解決方案,具有“安全穩(wěn)定、簡單易用、服務(wù)可用性高、性價(jià)比高”等特點(diǎn)與優(yōu)勢,專為企業(yè)上云打造定制,能夠滿足用戶豐富、多元化的應(yīng)用場景需求。
本文標(biāo)題:作為一個(gè)純粹數(shù)據(jù)結(jié)構(gòu)的RedisStreams-創(chuàng)新互聯(lián)
瀏覽地址:http://chinadenli.net/article14/iogge.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供微信小程序、面包屑導(dǎo)航、用戶體驗(yàn)、ChatGPT、域名注冊、商城網(wǎng)站
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)
猜你還喜歡下面的內(nèi)容