dw做網(wǎng)站背景圖片設(shè)置關(guān)鍵詞批量調(diào)詞 軟件
Redis應(yīng)用
目錄
Redis應(yīng)用
Redis 除了做緩存,還能做什么?
Redis 可以做消息隊(duì)列么?
Redis 可以做搜索引擎么?
如何基于 Redis 實(shí)現(xiàn)延時(shí)任務(wù)?
Redis 除了做緩存,還能做什么?
- 分布式鎖:通過(guò) Redis 來(lái)做分布式鎖是一種比較常見的方式。通常情況下,我們都是基于 Redisson 來(lái)實(shí)現(xiàn)分布式鎖。關(guān)于 Redis 實(shí)現(xiàn)分布式鎖的詳細(xì)介紹,可以看我寫的這篇文章:分布式鎖詳解 。
- 限流:一般是通過(guò) Redis + Lua 腳本的方式來(lái)實(shí)現(xiàn)限流。如果不想自己寫 Lua 腳本的話,也可以直接利用 Redisson 中的
RRateLimiter
來(lái)實(shí)現(xiàn)分布式限流,其底層實(shí)現(xiàn)就是基于 Lua 代碼+令牌桶算法。 - 消息隊(duì)列:Redis 自帶的 List 數(shù)據(jù)結(jié)構(gòu)可以作為一個(gè)簡(jiǎn)單的隊(duì)列使用。Redis 5.0 中增加的 Stream 類型的數(shù)據(jù)結(jié)構(gòu)更加適合用來(lái)做消息隊(duì)列。它比較類似于 Kafka,有主題和消費(fèi)組的概念,支持消息持久化以及 ACK 機(jī)制。
- 延時(shí)隊(duì)列:Redisson 內(nèi)置了延時(shí)隊(duì)列(基于 Sorted Set 實(shí)現(xiàn)的)。
- 分布式 Session :利用 String 或者 Hash 數(shù)據(jù)類型保存 Session 數(shù)據(jù),所有的服務(wù)器都可以訪問(wèn)。
- 復(fù)雜業(yè)務(wù)場(chǎng)景:通過(guò) Redis 以及 Redis 擴(kuò)展(比如 Redisson)提供的數(shù)據(jù)結(jié)構(gòu),我們可以很方便地完成很多復(fù)雜的業(yè)務(wù)場(chǎng)景比如通過(guò) Bitmap 統(tǒng)計(jì)活躍用戶、通過(guò) Sorted Set 維護(hù)排行榜
Redis 可以做消息隊(duì)列么?
實(shí)際項(xiàng)目中使用 Redis 來(lái)做消息隊(duì)列的非常少,畢竟有更成熟的消息隊(duì)列中間件可以用。
先說(shuō)結(jié)論:可以是可以,但不建議使用 Redis 來(lái)做消息隊(duì)列。和專業(yè)的消息隊(duì)列相比,還是有很多欠缺的地方。
Redis 2.0 之前,如果想要使用 Redis 來(lái)做消息隊(duì)列的話,只能通過(guò) List 來(lái)實(shí)現(xiàn)。
通過(guò) RPUSH/LPOP
或者 LPUSH/RPOP
即可實(shí)現(xiàn)簡(jiǎn)易版消息隊(duì)列:
# 生產(chǎn)者生產(chǎn)消息
> RPUSH myList msg1 msg2
(integer) 2
> RPUSH myList msg3
(integer) 3
# 消費(fèi)者消費(fèi)消息
> LPOP myList
"msg1"
不過(guò),通過(guò) RPUSH/LPOP
或者 LPUSH/RPOP
這樣的方式存在性能問(wèn)題,我們需要不斷輪詢?nèi)フ{(diào)用 RPOP
或 LPOP
來(lái)消費(fèi)消息。當(dāng) List 為空時(shí),大部分的輪詢的請(qǐng)求都是無(wú)效請(qǐng)求,這種方式大量浪費(fèi)了系統(tǒng)資源。
因此,Redis 還提供了 BLPOP
、BRPOP
這種阻塞式讀取的命令(帶 B-Blocking 的都是阻塞式),并且還支持一個(gè)超時(shí)參數(shù)。如果 List 為空,Redis 服務(wù)端不會(huì)立刻返回結(jié)果,它會(huì)等待 List 中有新數(shù)據(jù)后再返回或者是等待最多一個(gè)超時(shí)時(shí)間后返回空。如果將超時(shí)時(shí)間設(shè)置為 0 時(shí),即可無(wú)限等待,直到彈出消息
# 超時(shí)時(shí)間為 10s
# 如果有數(shù)據(jù)立刻返回,否則最多等待10秒
BRPOP myList 10
null
List 實(shí)現(xiàn)消息隊(duì)列功能太簡(jiǎn)單,像消息確認(rèn)機(jī)制等功能還需要我們自己實(shí)現(xiàn),最要命的是沒有廣播機(jī)制,消息也只能被消費(fèi)一次。
Redis 2.0 引入了發(fā)布訂閱 (pub/sub) 功能,解決了 List 實(shí)現(xiàn)消息隊(duì)列沒有廣播機(jī)制的問(wèn)題。
Redis 發(fā)布訂閱 (pub/sub) 功能
pub/sub 中引入了一個(gè)概念叫 channel(頻道),發(fā)布訂閱機(jī)制的實(shí)現(xiàn)就是基于這個(gè) channel 來(lái)做的。
pub/sub 涉及發(fā)布者(Publisher)和訂閱者(Subscriber,也叫消費(fèi)者)兩個(gè)角色:
- 發(fā)布者通過(guò)
PUBLISH
投遞消息給指定 channel。 - 訂閱者通過(guò)
SUBSCRIBE
訂閱它關(guān)心的 channel。并且,訂閱者可以訂閱一個(gè)或者多個(gè) channel。
我們這里啟動(dòng) 3 個(gè) Redis 客戶端來(lái)簡(jiǎn)單演示一下:
pub/sub 實(shí)現(xiàn)消息隊(duì)列演示
pub/sub 既能單播又能廣播,還支持 channel 的簡(jiǎn)單正則匹配。不過(guò),消息丟失(客戶端斷開連接或者 Redis 宕機(jī)都會(huì)導(dǎo)致消息丟失)、消息堆積(發(fā)布者發(fā)布消息的時(shí)候不會(huì)管消費(fèi)者的具體消費(fèi)能力如何)等問(wèn)題依然沒有一個(gè)比較好的解決辦法。
為此,Redis 5.0 新增加的一個(gè)數(shù)據(jù)結(jié)構(gòu) Stream
來(lái)做消息隊(duì)列。Stream
支持:
- 發(fā)布 / 訂閱模式
- 按照消費(fèi)者組進(jìn)行消費(fèi)(借鑒了 Kafka 消費(fèi)者組的概念)
- 消息持久化( RDB 和 AOF)
- ACK 機(jī)制(通過(guò)確認(rèn)機(jī)制來(lái)告知已經(jīng)成功處理了消息)
- 阻塞式獲取消息
Stream
的結(jié)構(gòu)如下:
這是一個(gè)有序的消息鏈表,每個(gè)消息都有一個(gè)唯一的 ID 和對(duì)應(yīng)的內(nèi)容。ID 是一個(gè)時(shí)間戳和序列號(hào)的組合,用來(lái)保證消息的唯一性和遞增性。內(nèi)容是一個(gè)或多個(gè)鍵值對(duì)(類似 Hash 基本數(shù)據(jù)類型),用來(lái)存儲(chǔ)消息的數(shù)據(jù)。
這里再對(duì)圖中涉及到的一些概念,進(jìn)行簡(jiǎn)單解釋:
Consumer Group
:消費(fèi)者組用于組織和管理多個(gè)消費(fèi)者。消費(fèi)者組本身不處理消息,而是再將消息分發(fā)給消費(fèi)者,由消費(fèi)者進(jìn)行真正的消費(fèi)last_delivered_id
:標(biāo)識(shí)消費(fèi)者組當(dāng)前消費(fèi)位置的游標(biāo),消費(fèi)者組中任意一個(gè)消費(fèi)者讀取了消息都會(huì)使 last_delivered_id 往前移動(dòng)。pending_ids
:記錄已經(jīng)被客戶端消費(fèi)但沒有 ack 的消息的 ID。
下面是Stream
用作消息隊(duì)列時(shí)常用的命令:
XADD
:向流中添加新的消息。XREAD
:從流中讀取消息。XREADGROUP
:從消費(fèi)組中讀取消息。XRANGE
:根據(jù)消息 ID 范圍讀取流中的消息。XREVRANGE
:與XRANGE
類似,但以相反順序返回結(jié)果。XDEL
:從流中刪除消息。XTRIM
:修剪流的長(zhǎng)度,可以指定修建策略(MAXLEN
/MINID
)。XLEN
:獲取流的長(zhǎng)度。XGROUP CREATE
:創(chuàng)建消費(fèi)者組。XGROUP DESTROY
: 刪除消費(fèi)者組XGROUP DELCONSUMER
:從消費(fèi)者組中刪除一個(gè)消費(fèi)者。XGROUP SETID
:為消費(fèi)者組設(shè)置新的最后遞送消息 IDXACK
:確認(rèn)消費(fèi)組中的消息已被處理。XPENDING
:查詢消費(fèi)組中掛起(未確認(rèn))的消息。XCLAIM
:將掛起的消息從一個(gè)消費(fèi)者轉(zhuǎn)移到另一個(gè)消費(fèi)者。XINFO
:獲取流(XINFO STREAM
)、消費(fèi)組(XINFO GROUPS
)或消費(fèi)者(XINFO CONSUMERS
)的詳細(xì)信息
Stream
使用起來(lái)相對(duì)要麻煩一些,這里就不演示了。
總的來(lái)說(shuō),Stream
已經(jīng)可以滿足一個(gè)消息隊(duì)列的基本要求了。不過(guò),Stream
在實(shí)際使用中依然會(huì)有一些小問(wèn)題不太好解決比如在 Redis 發(fā)生故障恢復(fù)后不能保證消息至少被消費(fèi)一次。
綜上,和專業(yè)的消息隊(duì)列相比,使用 Redis 來(lái)實(shí)現(xiàn)消息隊(duì)列還是有很多欠缺的地方比如消息丟失和堆積問(wèn)題不好解決。因此,我們通常建議不要使用 Redis 來(lái)做消息隊(duì)列,你完全可以選擇市面上比較成熟的一些消息隊(duì)列比如 RocketMQ、Kafka。不過(guò),如果你就是想要用 Redis 來(lái)做消息隊(duì)列的話,那我建議你優(yōu)先考慮 Stream
,這是目前相對(duì)最優(yōu)的 Redis 消息隊(duì)列實(shí)現(xiàn)。
Redis 可以做搜索引擎么?
Redis 是可以實(shí)現(xiàn)全文搜索引擎功能的,需要借助 RediSearch ,這是一個(gè)基于 Redis 的搜索引擎模塊。
RediSearch 支持中文分詞、聚合統(tǒng)計(jì)、停用詞、同義詞、拼寫檢查、標(biāo)簽查詢、向量相似度查詢、多關(guān)鍵詞搜索、分頁(yè)搜索等功能,算是一個(gè)功能比較完善的全文搜索引擎了。
相比較于 Elasticsearch 來(lái)說(shuō),RediSearch 主要在下面兩點(diǎn)上表現(xiàn)更優(yōu)異一些:
- 性能更優(yōu)秀:依賴 Redis 自身的高性能,基于內(nèi)存操作(Elasticsearch 基于磁盤)。
- 較低內(nèi)存占用實(shí)現(xiàn)快速索引:RediSearch 內(nèi)部使用壓縮的倒排索引,所以可以用較低的內(nèi)存占用來(lái)實(shí)現(xiàn)索引的快速構(gòu)建。
對(duì)于小型項(xiàng)目的簡(jiǎn)單搜索場(chǎng)景來(lái)說(shuō),使用 RediSearch 來(lái)作為搜索引擎還是沒有問(wèn)題的(搭配 RedisJSON 使用)。
對(duì)于比較復(fù)雜或者數(shù)據(jù)規(guī)模較大的搜索場(chǎng)景還是不太建議使用 RediSearch 來(lái)作為搜索引擎,主要是因?yàn)橄旅孢@些限制和問(wèn)題:
- 數(shù)據(jù)量限制:Elasticsearch 可以支持 PB 級(jí)別的數(shù)據(jù)量,可以輕松擴(kuò)展到多個(gè)節(jié)點(diǎn),利用分片機(jī)制提高可用性和性能。RedisSearch 是基于 Redis 實(shí)現(xiàn)的,其能存儲(chǔ)的數(shù)據(jù)量受限于 Redis 的內(nèi)存容量,不太適合存儲(chǔ)大規(guī)模的數(shù)據(jù)(內(nèi)存昂貴,擴(kuò)展能力較差)。
- 分布式能力較差:Elasticsearch 是為分布式環(huán)境設(shè)計(jì)的,可以輕松擴(kuò)展到多個(gè)節(jié)點(diǎn)。雖然 RedisSearch 支持分布式部署,但在實(shí)際應(yīng)用中可能會(huì)面臨一些挑戰(zhàn),如數(shù)據(jù)分片、節(jié)點(diǎn)間通信、數(shù)據(jù)一致性等問(wèn)題。
- 聚合功能較弱:Elasticsearch 提供了豐富的聚合功能,而 RediSearch 的聚合功能相對(duì)較弱,只支持簡(jiǎn)單的聚合操作。
- 生態(tài)較差:Elasticsearch 可以輕松和常見的一些系統(tǒng)/軟件集成比如 Hadoop、Spark、Kibana,而 RedisSearch 則不具備該優(yōu)勢(shì)。
Elasticsearch 適用于全文搜索、復(fù)雜查詢、實(shí)時(shí)數(shù)據(jù)分析和聚合的場(chǎng)景,而 RediSearch 適用于快速數(shù)據(jù)存儲(chǔ)、緩存和簡(jiǎn)單查詢的場(chǎng)景。
如何基于 Redis 實(shí)現(xiàn)延時(shí)任務(wù)?
類似的問(wèn)題:
- 訂單在 10 分鐘后未支付就失效,如何用 Redis 實(shí)現(xiàn)?
- 紅包 24 小時(shí)未被查收自動(dòng)退還,如何用 Redis 實(shí)現(xiàn)?
基于 Redis 實(shí)現(xiàn)延時(shí)任務(wù)的功能無(wú)非就下面兩種方案:
- Redis 過(guò)期事件監(jiān)聽
- Redisson 內(nèi)置的延時(shí)隊(duì)列
Redis 過(guò)期事件監(jiān)聽的存在時(shí)效性較差、丟消息、多服務(wù)實(shí)例下消息重復(fù)消費(fèi)等問(wèn)題,不被推薦使用。
Redisson 內(nèi)置的延時(shí)隊(duì)列具備下面這些優(yōu)勢(shì):
- 減少了丟消息的可能:DelayedQueue 中的消息會(huì)被持久化,即使 Redis 宕機(jī)了,根據(jù)持久化機(jī)制,也只可能丟失一點(diǎn)消息,影響不大。當(dāng)然了,你也可以使用掃描數(shù)據(jù)庫(kù)的方法作為補(bǔ)償機(jī)制。
- 消息不存在重復(fù)消費(fèi)問(wèn)題:每個(gè)客戶端都是從同一個(gè)目標(biāo)隊(duì)列中獲取任務(wù)的,不存在重復(fù)消費(fèi)的問(wèn)題。