在线不卡日本ⅴ一区v二区_精品一区二区中文字幕_天堂v在线视频_亚洲五月天婷婷中文网站

  • <menu id="lky3g"></menu>
  • <style id="lky3g"></style>
    <pre id="lky3g"><tt id="lky3g"></tt></pre>

    2 萬(wàn)字 + 20張圖|細(xì)說 Redis 九種數(shù)據(jù)類型和應(yīng)用場(chǎng)景

    2 萬(wàn)字 + 20張圖|細(xì)說 Redis 九種數(shù)據(jù)類型和應(yīng)用場(chǎng)景

    我們都知道 Redis 提供了豐富的數(shù)據(jù)類型,常見的有五種:String(字符串),Hash(哈希),List(列表),Set(集合)、Zset(有序集合)。

    隨著 Redis 版本的更新,后面又支持了四種數(shù)據(jù)類型:BitMap(2.2 版新增)、HyperLogLog(2.8 版新增)、GEO(3.2 版新增)、Stream(5.0 版新增)。

    每種數(shù)據(jù)對(duì)象都各自的應(yīng)用場(chǎng)景,你能說出它們各自的應(yīng)用場(chǎng)景嗎?

    面試過程中,這個(gè)問題也很常被問到,又比如會(huì)舉例一個(gè)應(yīng)用場(chǎng)景來問你,讓你說使用哪種 Redis 數(shù)據(jù)類型來實(shí)現(xiàn)。

    所以,這次我們就來學(xué)習(xí) Redis 數(shù)據(jù)類型的使用以及應(yīng)用場(chǎng)景。篇幅比較長(zhǎng),大家收藏慢慢看。

    String

    介紹

    String 是最基本的 key-value 結(jié)構(gòu),key 是唯一標(biāo)識(shí),value 是具體的值,value其實(shí)不僅是字符串, 也可以是數(shù)字(整數(shù)或浮點(diǎn)數(shù)),value 最多可以容納的數(shù)據(jù)長(zhǎng)度是 512M。

    內(nèi)部實(shí)現(xiàn)

    String 類型的底層的數(shù)據(jù)結(jié)構(gòu)實(shí)現(xiàn)主要是 int 和 SDS(簡(jiǎn)單動(dòng)態(tài)字符串)。

    SDS 和我們認(rèn)識(shí)的 C 字符串不太一樣,之所以沒有使用 C 語(yǔ)言的字符串表示,因?yàn)?SDS 相比于 C 的原生字符串:

    • SDS 不僅可以保存文本數(shù)據(jù),還可以保存二進(jìn)制數(shù)據(jù)。因?yàn)?SDS 使用 len 屬性的值而不是空字符來判斷字符串是否結(jié)束,并且 SDS 的所有 API 都會(huì)以處理二進(jìn)制的方式來處理 SDS 存放在 buf[] 數(shù)組里的數(shù)據(jù)。所以 SDS 不光能存放文本數(shù)據(jù),而且能保存圖片、音頻、視頻、壓縮文件這樣的二進(jìn)制數(shù)據(jù)。
    • **SDS 獲取字符串長(zhǎng)度的時(shí)間復(fù)雜度是 O(1)**。因?yàn)?C 語(yǔ)言的字符串并不記錄自身長(zhǎng)度,所以獲取長(zhǎng)度的復(fù)雜度為 O(n);而 SDS 結(jié)構(gòu)里用 len 屬性記錄了字符串長(zhǎng)度,所以復(fù)雜度為 O(1)。
    • Redis 的 SDS API 是安全的,拼接字符串不會(huì)造成緩沖區(qū)溢出。因?yàn)?SDS 在拼接字符串之前會(huì)檢查 SDS 空間是否滿足要求,如果空間不夠會(huì)自動(dòng)擴(kuò)容,所以不會(huì)導(dǎo)致緩沖區(qū)溢出的問題。

    字符串對(duì)象的內(nèi)部編碼(encoding)有 3 種 :int、raw和 embstr。

    如果一個(gè)字符串對(duì)象保存的是整數(shù)值,并且這個(gè)整數(shù)值可以用long類型來表示,那么字符串對(duì)象會(huì)將整數(shù)值保存在字符串對(duì)象結(jié)構(gòu)的ptr屬性里面(將void*轉(zhuǎn)換成 long),并將字符串對(duì)象的編碼設(shè)置為int。

    如果字符串對(duì)象保存的是一個(gè)字符串,并且這個(gè)字符申的長(zhǎng)度小于等于 32 字節(jié),那么字符串對(duì)象將使用一個(gè)簡(jiǎn)單動(dòng)態(tài)字符串(SDS)來保存這個(gè)字符串,并將對(duì)象的編碼設(shè)置為embstr, embstr編碼是專門用于保存短字符串的一種優(yōu)化編碼方式:

    如果字符串對(duì)象保存的是一個(gè)字符串,并且這個(gè)字符串的長(zhǎng)度大于 32 字節(jié),那么字符串對(duì)象將使用一個(gè)簡(jiǎn)單動(dòng)態(tài)字符串(SDS)來保存這個(gè)字符串,并將對(duì)象的編碼設(shè)置為raw:

    可以看到embstr和raw編碼都會(huì)使用SDS來保存值,但不同之處在于embstr會(huì)通過一次內(nèi)存分配函數(shù)來分配一塊連續(xù)的內(nèi)存空間來保存redisObject和SDS,而raw編碼會(huì)通過調(diào)用兩次內(nèi)存分配函數(shù)來分別分配兩塊空間來保存redisObject和SDS。Redis這樣做會(huì)有很多好處:

    • embstr編碼將創(chuàng)建字符串對(duì)象所需的內(nèi)存分配次數(shù)從 raw 編碼的兩次降低為一次;
    • 釋放 embstr編碼的字符串對(duì)象同樣只需要調(diào)用一次內(nèi)存釋放函數(shù);
    • 因?yàn)閑mbstr編碼的字符串對(duì)象的所有數(shù)據(jù)都保存在一塊連續(xù)的內(nèi)存里面可以更好的利用 CPU 緩存提升性能。

    但是 embstr 也有缺點(diǎn)的:

    • 如果字符串的長(zhǎng)度增加需要重新分配內(nèi)存時(shí),整個(gè)redisObject和sds都需要重新分配空間,所以embstr編碼的字符串對(duì)象實(shí)際上是只讀的,redis沒有為embstr編碼的字符串對(duì)象編寫任何相應(yīng)的修改程序。當(dāng)我們對(duì)embstr編碼的字符串對(duì)象執(zhí)行任何修改命令(例如append)時(shí),程序會(huì)先將對(duì)象的編碼從embstr轉(zhuǎn)換成raw,然后再執(zhí)行修改命令。

    常用指令

    普通字符串的基本操作:

    # 設(shè)置 key-value 類型的值> SET name linOK# 根據(jù) key 獲得對(duì)應(yīng)的 value> GET name”lin”# 判斷某個(gè) key 是否存在> EXISTS name(integer) 1# 返回 key 所儲(chǔ)存的字符串值的長(zhǎng)度> STRLEN name(integer) 3# 刪除某個(gè) key 對(duì)應(yīng)的值> DEL name(integer) 1

    批量設(shè)置 :

    # 批量設(shè)置 key-value 類型的值> MSET key1 value1 key2 value2 OK# 批量獲取多個(gè) key 對(duì)應(yīng)的 value> MGET key1 key2 1) “value1″2) “value2”

    計(jì)數(shù)器(字符串的內(nèi)容為整數(shù)的時(shí)候可以使用):

    # 設(shè)置 key-value 類型的值> SET number 0OK# 將 key 中儲(chǔ)存的數(shù)字值增一> INCR number(integer) 1# 將key中存儲(chǔ)的數(shù)字值加 10> INCRBY number 10(integer) 11# 將 key 中儲(chǔ)存的數(shù)字值減一> DECR number(integer) 10# 將key中存儲(chǔ)的數(shù)字值鍵 10> DECRBY number 10(integer) 0

    過期(默認(rèn)為永不過期):

    # 設(shè)置 key 在 60 秒后過期(該方法是針對(duì)已經(jīng)存在的key設(shè)置過期時(shí)間)> EXPIRE name 60 (integer) 1# 查看數(shù)據(jù)還有多久過期> TTL name (integer) 51#設(shè)置 key-value 類型的值,并設(shè)置該key的過期時(shí)間為 60 秒> SET key value EX 60OK> SETEX key 60 valueOK

    不存在就插入:

    # 不存在就插入(not exists)>SETNX key value(integer) 1

    應(yīng)用場(chǎng)景

    緩存對(duì)象

    使用 String 來緩存對(duì)象有兩種方式:

    • 直接緩存整個(gè)對(duì)象的 JSON,命令例子:SET user:1 ‘{“name”:”xiaolin”, “age”:18}’。
    • 采用將 key 進(jìn)行分離為 user:ID:屬性,采用 MSET 存儲(chǔ),用 MGET 獲取各屬性值,命令例子:MSET user:1:name xiaolin user:1:age 18 user:2:name xiaomei user:2:age 20。

    常規(guī)計(jì)數(shù)

    因?yàn)?Redis 處理命令是單線程,所以執(zhí)行命令的過程是原子的。因此 String 數(shù)據(jù)類型適合計(jì)數(shù)場(chǎng)景,比如計(jì)算訪問次數(shù)、點(diǎn)贊、轉(zhuǎn)發(fā)、庫(kù)存數(shù)量等等。

    比如計(jì)算文章的閱讀量:

    # 初始化文章的閱讀量> SET aritcle:readcount:1001 0OK#閱讀量+1> INCR aritcle:readcount:1001(integer) 1#閱讀量+1> INCR aritcle:readcount:1001(integer) 2#閱讀量+1> INCR aritcle:readcount:1001(integer) 3# 獲取對(duì)應(yīng)文章的閱讀量> GET aritcle:readcount:1001″3″

    分布式鎖

    SET 命令有個(gè) NX 參數(shù)可以實(shí)現(xiàn)「key不存在才插入」,可以用它來實(shí)現(xiàn)分布式鎖:

    • 如果 key 不存在,則顯示插入成功,可以用來表示加鎖成功;
    • 如果 key 存在,則會(huì)顯示插入失敗,可以用來表示加鎖失敗。

    一般而言,還會(huì)對(duì)分布式鎖加上過期時(shí)間,分布式鎖的命令如下:

    SET lock_key unique_value NX PX 10000

    • lock_key 就是 key 鍵;
    • unique_value 是客戶端生成的唯一的標(biāo)識(shí);
    • NX 代表只在 lock_key 不存在時(shí),才對(duì) lock_key 進(jìn)行設(shè)置操作;
    • PX 10000 表示設(shè)置 lock_key 的過期時(shí)間為 10s,這是為了避免客戶端發(fā)生異常而無(wú)法釋放鎖。

    而解鎖的過程就是將 lock_key 鍵刪除,但不能亂刪,要保證執(zhí)行操作的客戶端就是加鎖的客戶端。所以,解鎖的時(shí)候,我們要先判斷鎖的 unique_value 是否為加鎖客戶端,是的話,才將 lock_key 鍵刪除。

    可以看到,解鎖是有兩個(gè)操作,這時(shí)就需要 Lua 腳本來保證解鎖的原子性,因?yàn)?Redis 在執(zhí)行 Lua 腳本時(shí),可以以原子性的方式執(zhí)行,保證了鎖釋放操作的原子性。

    // 釋放鎖時(shí),先比較 unique_value 是否相等,避免鎖的誤釋放if redis.call(“get”,KEYS[1]) == ARGV[1] then return redis.call(“del”,KEYS[1])else return 0end

    這樣一來,就通過使用 SET 命令和 Lua 腳本在 Redis 單節(jié)點(diǎn)上完成了分布式鎖的加鎖和解鎖。

    List

    介紹

    List 列表是簡(jiǎn)單的字符串列表,按照插入順序排序,可以從頭部或尾部向 List 列表添加元素

    列表的最大長(zhǎng)度為 2^32 – 1,也即每個(gè)列表支持超過 40 億個(gè)元素。

    內(nèi)部實(shí)現(xiàn)

    List 類型的底層數(shù)據(jù)結(jié)構(gòu)是由雙向鏈表或壓縮列表實(shí)現(xiàn)的:

    • 如果列表的元素個(gè)數(shù)小于 512 個(gè)(默認(rèn)值,可由 list-max-ziplist-entries 配置),列表每個(gè)元素的值都小于 64 字節(jié)(默認(rèn)值,可由 list-max-ziplist-value配置),Redis 會(huì)使用壓縮列表作為 List 類型的底層數(shù)據(jù)結(jié)構(gòu);
    • 如果列表的元素不滿足上面的條件,Redis 會(huì)使用雙向鏈表作為 List 類型的底層數(shù)據(jù)結(jié)構(gòu);

    但是在 Redis 3.2 版本之后,List 數(shù)據(jù)類型底層數(shù)據(jù)結(jié)構(gòu)就只由 quicklist 實(shí)現(xiàn)了,替代了雙向鏈表和壓縮列表。

    常用命令

    # 將一個(gè)或多個(gè)值value插入到key列表的表頭(最左邊),最后的值在最前面LPUSH key value [value …] # 將一個(gè)或多個(gè)值value插入到key列表的表尾(最右邊)RPUSH key value [value …]# 移除并返回key列表的頭元素LPOP key # 移除并返回key列表的尾元素RPOP key # 返回列表key中指定區(qū)間內(nèi)的元素,區(qū)間以偏移量start和stop指定,從0開始LRANGE key start stop# 從key列表表頭彈出一個(gè)元素,沒有就阻塞timeout秒,如果timeout=0則一直阻塞BLPOP key [key …] timeout# 從key列表表尾彈出一個(gè)元素,沒有就阻塞timeout秒,如果timeout=0則一直阻塞BRPOP key [key …] timeout

    應(yīng)用場(chǎng)景

    消息隊(duì)列

    消息隊(duì)列在存取消息時(shí),必須要滿足三個(gè)需求,分別是消息保序、處理重復(fù)的消息和保證消息可靠性。

    Redis 的 List 和 Stream 兩種數(shù)據(jù)類型,就可以滿足消息隊(duì)列的這三個(gè)需求。我們先來了解下基于 List 的消息隊(duì)列實(shí)現(xiàn)方法,后面在介紹 Stream 數(shù)據(jù)類型時(shí)候,在詳細(xì)說說 Stream。

    1、如何滿足消息保序需求?

    List 本身就是按先進(jìn)先出的順序?qū)?shù)據(jù)進(jìn)行存取的,所以,如果使用 List 作為消息隊(duì)列保存消息的話,就已經(jīng)能滿足消息保序的需求了。

    List 可以使用 LPUSH + RPOP (或者反過來,RPUSH+LPOP)命令實(shí)現(xiàn)消息隊(duì)列。

    • 生產(chǎn)者使用 LPUSH key value[value…] 將消息插入到隊(duì)列的頭部,如果 key 不存在則會(huì)創(chuàng)建一個(gè)空的隊(duì)列再插入消息。
    • 消費(fèi)者使用 RPOP key 依次讀取隊(duì)列的消息,先進(jìn)先出。

    不過,在消費(fèi)者讀取數(shù)據(jù)時(shí),有一個(gè)潛在的性能風(fēng)險(xiǎn)點(diǎn)。

    在生產(chǎn)者往 List 中寫入數(shù)據(jù)時(shí),List 并不會(huì)主動(dòng)地通知消費(fèi)者有新消息寫入,如果消費(fèi)者想要及時(shí)處理消息,就需要在程序中不停地調(diào)用 RPOP 命令(比如使用一個(gè)while(1)循環(huán))。如果有新消息寫入,RPOP命令就會(huì)返回結(jié)果,否則,RPOP命令返回空值,再繼續(xù)循環(huán)。

    所以,即使沒有新消息寫入List,消費(fèi)者也要不停地調(diào)用 RPOP 命令,這就會(huì)導(dǎo)致消費(fèi)者程序的 CPU 一直消耗在執(zhí)行 RPOP 命令上,帶來不必要的性能損失。

    為了解決這個(gè)問題,Redis提供了 BRPOP 命令。BRPOP命令也稱為阻塞式讀取,客戶端在沒有讀到隊(duì)列數(shù)據(jù)時(shí),自動(dòng)阻塞,直到有新的數(shù)據(jù)寫入隊(duì)列,再開始讀取新數(shù)據(jù)。和消費(fèi)者程序自己不停地調(diào)用RPOP命令相比,這種方式能節(jié)省CPU開銷。

    2、如何處理重復(fù)的消息?

    消費(fèi)者要實(shí)現(xiàn)重復(fù)消息的判斷,需要 2 個(gè)方面的要求:

    • 每個(gè)消息都有一個(gè)全局的 ID。
    • 消費(fèi)者要記錄已經(jīng)處理過的消息的 ID。當(dāng)收到一條消息后,消費(fèi)者程序就可以對(duì)比收到的消息 ID 和記錄的已處理過的消息 ID,來判斷當(dāng)前收到的消息有沒有經(jīng)過處理。如果已經(jīng)處理過,那么,消費(fèi)者程序就不再進(jìn)行處理了。

    但是 List 并不會(huì)為每個(gè)消息生成 ID 號(hào),所以我們需要自行為每個(gè)消息生成一個(gè)全局唯一ID,生成之后,我們?cè)谟?LPUSH 命令把消息插入 List 時(shí),需要在消息中包含這個(gè)全局唯一 ID。

    例如,我們執(zhí)行以下命令,就把一條全局 ID 為 111000102、庫(kù)存量為 99 的消息插入了消息隊(duì)列:

    > LPUSH mq “111000102:stock:99″(integer) 1

    3、如何保證消息可靠性?

    當(dāng)消費(fèi)者程序從 List 中讀取一條消息后,List 就不會(huì)再留存這條消息了。所以,如果消費(fèi)者程序在處理消息的過程出現(xiàn)了故障或宕機(jī),就會(huì)導(dǎo)致消息沒有處理完成,那么,消費(fèi)者程序再次啟動(dòng)后,就沒法再次從 List 中讀取消息了。

    為了留存消息,List 類型提供了 BRPOPLPUSH 命令,這個(gè)命令的作用是讓消費(fèi)者程序從一個(gè) List 中讀取消息,同時(shí),Redis 會(huì)把這個(gè)消息再插入到另一個(gè) List(可以叫作備份 List)留存。

    這樣一來,如果消費(fèi)者程序讀了消息但沒能正常處理,等它重啟后,就可以從備份 List 中重新讀取消息并進(jìn)行處理了。

    好了,到這里可以知道基于 List 類型的消息隊(duì)列,滿足消息隊(duì)列的三大需求(消息保序、處理重復(fù)的消息和保證消息可靠性)。

    • 消息保序:使用 LPUSH + RPOP;
    • 阻塞讀取:使用 BRPOP;
    • 重復(fù)消息處理:生產(chǎn)者自行實(shí)現(xiàn)全局唯一 ID;
    • 消息的可靠性:使用 BRPOPLPUSH

    但是,在用 List 做消息隊(duì)列時(shí),如果生產(chǎn)者消息發(fā)送很快,而消費(fèi)者處理消息的速度比較慢,這就導(dǎo)致 List 中的消息越積越多,給 Redis 的內(nèi)存帶來很大壓力。

    要解決這個(gè)問題,就要啟動(dòng)多個(gè)消費(fèi)者程序組成一個(gè)消費(fèi)組,一起分擔(dān)處理 List 中的消息。但是,List 類型并不支持消費(fèi)組的實(shí)現(xiàn)。

    這就要說起 Redis 從 5.0 版本開始提供的 Stream 數(shù)據(jù)類型了,Stream 同樣能夠滿足消息隊(duì)列的三大需求,而且它還支持「消費(fèi)組」形式的消息讀取。

    Hash

    介紹

    Hash 是一個(gè)鍵值對(duì)(key – value)集合,其中 value 的形式入:value=[{field1,value1},…{fieldN,valueN}]。Hash 特別適合用于存儲(chǔ)對(duì)象。

    Hash 與 String 對(duì)象的區(qū)別如下圖所示:

    內(nèi)部實(shí)現(xiàn)

    Hash 類型的底層數(shù)據(jù)結(jié)構(gòu)是由壓縮列表或哈希表實(shí)現(xiàn)的:

    • 如果哈希類型元素個(gè)數(shù)小于 512 個(gè)(默認(rèn)值,可由 hash-max-ziplist-entries 配置),所有值小于 64 字節(jié)(默認(rèn)值,可由 hash-max-ziplist-value 配置)的話,Redis 會(huì)使用壓縮列表作為 Hash 類型的底層數(shù)據(jù)結(jié)構(gòu);
    • 如果哈希類型元素不滿足上面條件,Redis 會(huì)使用哈希表作為 Hash 類型的 底層數(shù)據(jù)結(jié)構(gòu)。

    在 Redis 7.0 中,壓縮列表數(shù)據(jù)結(jié)構(gòu)已經(jīng)廢棄了,交由 listpack 數(shù)據(jù)結(jié)構(gòu)來實(shí)現(xiàn)了。

    常用命令

    # 存儲(chǔ)一個(gè)哈希表key的鍵值HSET key field value # 獲取哈希表key對(duì)應(yīng)的field鍵值HGET key field# 在一個(gè)哈希表key中存儲(chǔ)多個(gè)鍵值對(duì)HMSET key field value [field value…] # 批量獲取哈希表key中多個(gè)field鍵值HMGET key field [field …] # 刪除哈希表key中的field鍵值HDEL key field [field …] # 返回哈希表key中field的數(shù)量HLEN key # 返回哈希表key中所有的鍵值HGETALL key # 為哈希表key中field鍵的值加上增量nHINCRBY key field n

    應(yīng)用場(chǎng)景

    緩存對(duì)象

    Hash 類型的 (key,field, value) 的結(jié)構(gòu)與對(duì)象的(對(duì)象id, 屬性, 值)的結(jié)構(gòu)相似,也可以用來存儲(chǔ)對(duì)象。

    我們以用戶信息為例,它在關(guān)系型數(shù)據(jù)庫(kù)中的結(jié)構(gòu)是這樣的:

    我們可以使用如下命令,將用戶對(duì)象的信息存儲(chǔ)到 Hash 類型:

    # 存儲(chǔ)一個(gè)哈希表uid:1的鍵值> HSET uid:1 name Tom age 152# 存儲(chǔ)一個(gè)哈希表uid:2的鍵值> HSET uid:2 name Jerry age 132# 獲取哈希表用戶id為1中所有的鍵值> HGETALL uid:11) “name”2) “Tom”3) “age”4) “15”

    Redis Hash 存儲(chǔ)其結(jié)構(gòu)如下圖:

    在介紹 String 類型的應(yīng)用場(chǎng)景時(shí)有所介紹,String + Json也是存儲(chǔ)對(duì)象的一種方式,那么存儲(chǔ)對(duì)象時(shí),到底用 String + json 還是用 Hash 呢?

    一般對(duì)象用 String + Json 存儲(chǔ),對(duì)象中某些頻繁變化的屬性可以考慮抽出來用 Hash 類型存儲(chǔ)。

    購(gòu)物車

    以用戶 id 為 key,商品 id 為 field,商品數(shù)量為 value,恰好構(gòu)成了購(gòu)物車的3個(gè)要素,如下圖所示。

    涉及的命令如下:

    • 添加商品:HSET cart:{用戶id} {商品id} 1
    • 添加數(shù)量:HINCRBY cart:{用戶id} {商品id} 1
    • 商品總數(shù):HLEN cart:{用戶id}
    • 刪除商品:HDEL cart:{用戶id} {商品id}
    • 獲取購(gòu)物車所有商品:HGETALL cart:{用戶id}

    當(dāng)前僅僅是將商品ID存儲(chǔ)到了Redis 中,在回顯商品具體信息的時(shí)候,還需要拿著商品 id 查詢一次數(shù)據(jù)庫(kù),獲取完整的商品的信息。

    Set

    介紹

    Set 類型是一個(gè)無(wú)序并唯一的鍵值集合,它的存儲(chǔ)順序不會(huì)按照插入的先后順序進(jìn)行存儲(chǔ)。

    一個(gè)集合最多可以存儲(chǔ) 2^32-1 個(gè)元素。概念和數(shù)學(xué)中個(gè)的集合基本類似,可以交集,并集,差集等等,所以 Set 類型除了支持集合內(nèi)的增刪改查,同時(shí)還支持多個(gè)集合取交集、并集、差集。

    Set 類型和 List 類型的區(qū)別如下:

    • List 可以存儲(chǔ)重復(fù)元素,Set 只能存儲(chǔ)非重復(fù)元素;
    • List 是按照元素的先后順序存儲(chǔ)元素的,而 Set 則是無(wú)序方式存儲(chǔ)元素的。

    內(nèi)部實(shí)現(xiàn)

    Set 類型的底層數(shù)據(jù)結(jié)構(gòu)是由哈希表或整數(shù)集合實(shí)現(xiàn)的:

    • 如果集合中的元素都是整數(shù)且元素個(gè)數(shù)小于 512 (默認(rèn)值,set-maxintset-entries配置)個(gè),Redis 會(huì)使用整數(shù)集合作為 Set 類型的底層數(shù)據(jù)結(jié)構(gòu);
    • 如果集合中的元素不滿足上面條件,則 Redis 使用哈希表作為 Set 類型的底層數(shù)據(jù)結(jié)構(gòu)。

    常用命令

    Set常用操作:

    # 往集合key中存入元素,元素存在則忽略,若key不存在則新建SADD key member [member …]# 從集合key中刪除元素SREM key member [member …] # 獲取集合key中所有元素SMEMBERS key# 獲取集合key中的元素個(gè)數(shù)SCARD key# 判斷member元素是否存在于集合key中SISMEMBER key member# 從集合key中隨機(jī)選出count個(gè)元素,元素不從key中刪除SRANDMEMBER key [count]# 從集合key中隨機(jī)選出count個(gè)元素,元素從key中刪除SPOP key [count]

    Set運(yùn)算操作:

    # 交集運(yùn)算SINTER key [key …]# 將交集結(jié)果存入新集合destination中SINTERSTORE destination key [key …]# 并集運(yùn)算SUNION key [key …]# 將并集結(jié)果存入新集合destination中SUNIONSTORE destination key [key …]# 差集運(yùn)算SDIFF key [key …]# 將差集結(jié)果存入新集合destination中SDIFFSTORE destination key [key …]

    應(yīng)用場(chǎng)景

    集合的主要幾個(gè)特性,無(wú)序、不可重復(fù)、支持并交差等操作。

    因此 Set 類型比較適合用來數(shù)據(jù)去重和保障數(shù)據(jù)的唯一性,還可以用來統(tǒng)計(jì)多個(gè)集合的交集、錯(cuò)集和并集等,當(dāng)我們存儲(chǔ)的數(shù)據(jù)是無(wú)序并且需要去重的情況下,比較適合使用集合類型進(jìn)行存儲(chǔ)。

    但是要提醒你一下,這里有一個(gè)潛在的風(fēng)險(xiǎn)。Set 的差集、并集和交集的計(jì)算復(fù)雜度較高,在數(shù)據(jù)量較大的情況下,如果直接執(zhí)行這些計(jì)算,會(huì)導(dǎo)致 Redis 實(shí)例阻塞。

    在主從集群中,為了避免主庫(kù)因?yàn)?Set 做聚合計(jì)算(交集、差集、并集)時(shí)導(dǎo)致主庫(kù)被阻塞,我們可以選擇一個(gè)從庫(kù)完成聚合統(tǒng)計(jì),或者把數(shù)據(jù)返回給客戶端,由客戶端來完成聚合統(tǒng)計(jì)。

    點(diǎn)贊

    Set 類型可以保證一個(gè)用戶只能點(diǎn)一個(gè)贊,這里舉例子一個(gè)場(chǎng)景,key 是文章id,value 是用戶id。

    uid:1 、uid:2、uid:3 三個(gè)用戶分別對(duì) article:1 文章點(diǎn)贊了。

    # uid:1 用戶對(duì)文章 article:1 點(diǎn)贊> SADD article:1 uid:1(integer) 1# uid:2 用戶對(duì)文章 article:1 點(diǎn)贊> SADD article:1 uid:2(integer) 1# uid:3 用戶對(duì)文章 article:1 點(diǎn)贊> SADD article:1 uid:3(integer) 1

    uid:1 取消了對(duì) article:1 文章點(diǎn)贊。

    > SREM article:1 uid:1(integer) 1

    獲取 article:1 文章所有點(diǎn)贊用戶 :

    > SMEMBERS article:11) “uid:3″2) “uid:2”

    獲取 article:1 文章的點(diǎn)贊用戶數(shù)量:

    > SCARD article:1(integer) 2

    判斷用戶 uid:1 是否對(duì)文章 article:1 點(diǎn)贊了:

    > SISMEMBER article:1 uid:1(integer) 0 # 返回0說明沒點(diǎn)贊,返回1則說明點(diǎn)贊了

    共同關(guān)注

    Set 類型支持交集運(yùn)算,所以可以用來計(jì)算共同關(guān)注的好友、公眾號(hào)等。

    key 可以是用戶id,value 則是已關(guān)注的公眾號(hào)的id。

    uid:1 用戶關(guān)注公眾號(hào) id 為 5、6、7、8、9,uid:2 用戶關(guān)注公眾號(hào) id 為 7、8、9、10、11。

    # uid:1 用戶關(guān)注公眾號(hào) id 為 5、6、7、8、9> SADD uid:1 5 6 7 8 9(integer) 5# uid:2 用戶關(guān)注公眾號(hào) id 為 7、8、9、10、11> SADD uid:2 7 8 9 10 11(integer) 5

    uid:1 和 uid:2 共同關(guān)注的公眾號(hào):

    # 獲取共同關(guān)注> SINTER uid:1 uid:21) “7”2) “8”3) “9”

    給 uid:2 推薦 uid:1 關(guān)注的公眾號(hào):

    > SDIFF uid:1 uid:21) “5”2) “6”

    驗(yàn)證某個(gè)公眾號(hào)是否同時(shí)被 uid:1 或 uid:2 關(guān)注:

    > SISMEMBER uid:1 5(integer) 1 # 返回0,說明關(guān)注了> SISMEMBER uid:2 5(integer) 0 # 返回0,說明沒關(guān)注

    抽獎(jiǎng)活動(dòng)

    存儲(chǔ)某活動(dòng)中中獎(jiǎng)的用戶名 ,Set 類型因?yàn)橛腥ブ毓δ埽梢员WC同一個(gè)用戶不會(huì)中獎(jiǎng)兩次。

    key為抽獎(jiǎng)活動(dòng)名,value為員工名稱,把所有員工名稱放入抽獎(jiǎng)箱 :

    >SADD lucky Tom Jerry John Sean Marry Lindy Sary Mark(integer) 5

    如果允許重復(fù)中獎(jiǎng),可以使用 SRANDMEMBER 命令。

    # 抽取 1 個(gè)一等獎(jiǎng):> SRANDMEMBER lucky 11) “Tom”# 抽取 2 個(gè)二等獎(jiǎng):> SRANDMEMBER lucky 21) “Mark”2) “Jerry”# 抽取 3 個(gè)三等獎(jiǎng):> SRANDMEMBER lucky 31) “Sary”2) “Tom”3) “Jerry”

    如果不允許重復(fù)中獎(jiǎng),可以使用 SPOP 命令。

    # 抽取一等獎(jiǎng)1個(gè)> SPOP lucky 11) “Sary”# 抽取二等獎(jiǎng)2個(gè)> SPOP lucky 21) “Jerry”2) “Mark”# 抽取三等獎(jiǎng)3個(gè)> SPOP lucky 31) “John”2) “Sean”3) “Lindy”

    Zset

    介紹

    Zset 類型(有序集合類型)相比于 Set 類型多了一個(gè)排序?qū)傩?score(分值),對(duì)于有序集合 ZSet 來說,每個(gè)存儲(chǔ)元素相當(dāng)于有兩個(gè)值組成的,一個(gè)是有序結(jié)合的元素值,一個(gè)是排序值。

    有序集合保留了集合不能有重復(fù)成員的特性(分值可以重復(fù)),但不同的是,有序集合中的元素可以排序。

    內(nèi)部實(shí)現(xiàn)

    Zset 類型的底層數(shù)據(jù)結(jié)構(gòu)是由壓縮列表或跳表實(shí)現(xiàn)的:

    • 如果有序集合的元素個(gè)數(shù)小于 128 個(gè),并且每個(gè)元素的值小于 64 字節(jié)時(shí),Redis 會(huì)使用壓縮列表作為 Zset 類型的底層數(shù)據(jù)結(jié)構(gòu);
    • 如果有序集合的元素不滿足上面的條件,Redis 會(huì)使用跳表作為 Zset 類型的底層數(shù)據(jù)結(jié)構(gòu);

    在 Redis 7.0 中,壓縮列表數(shù)據(jù)結(jié)構(gòu)已經(jīng)廢棄了,交由 listpack 數(shù)據(jù)結(jié)構(gòu)來實(shí)現(xiàn)了。

    常用命令

    Zset 常用操作:

    # 往有序集合key中加入帶分值元素ZADD key score member [[score member]…] # 往有序集合key中刪除元素ZREM key member [member…] # 返回有序集合key中元素member的分值ZSCORE key member# 返回有序集合key中元素個(gè)數(shù)ZCARD key # 為有序集合key中元素member的分值加上incrementZINCRBY key increment member # 正序獲取有序集合key從start下標(biāo)到stop下標(biāo)的元素ZRANGE key start stop [WITHSCORES]# 倒序獲取有序集合key從start下標(biāo)到stop下標(biāo)的元素ZREVRANGE key start stop [WITHSCORES]# 返回有序集合中指定分?jǐn)?shù)區(qū)間內(nèi)的成員,分?jǐn)?shù)由低到高排序。ZRANGEBYSCORE key min max [WITHSCORES] [LIMIT offset count]# 返回指定成員區(qū)間內(nèi)的成員,按字典正序排列, 分?jǐn)?shù)必須相同。ZRANGEBYLEX key min max [LIMIT offset count]# 返回指定成員區(qū)間內(nèi)的成員,按字典倒序排列, 分?jǐn)?shù)必須相同ZREVRANGEBYLEX key max min [LIMIT offset count]

    Zset 運(yùn)算操作(相比于 Set 類型,ZSet 類型沒有支持差集運(yùn)算):

    # 并集計(jì)算(相同元素分值相加),numberkeys一共多少個(gè)key,WEIGHTS每個(gè)key對(duì)應(yīng)的分值乘積ZUNIONSTORE destkey numberkeys key [key…] # 交集計(jì)算(相同元素分值相加),numberkeys一共多少個(gè)key,WEIGHTS每個(gè)key對(duì)應(yīng)的分值乘積ZINTERSTORE destkey numberkeys key [key…]

    應(yīng)用場(chǎng)景

    Zset 類型(Sorted Set,有序集合) 可以根據(jù)元素的權(quán)重來排序,我們可以自己來決定每個(gè)元素的權(quán)重值。比如說,我們可以根據(jù)元素插入 Sorted Set 的時(shí)間確定權(quán)重值,先插入的元素權(quán)重小,后插入的元素權(quán)重大。

    在面對(duì)需要展示最新列表、排行榜等場(chǎng)景時(shí),如果數(shù)據(jù)更新頻繁或者需要分頁(yè)顯示,可以優(yōu)先考慮使用 Sorted Set。

    排行榜

    有序集合比較典型的使用場(chǎng)景就是排行榜。例如學(xué)生成績(jī)的排名榜、游戲積分排行榜、視頻播放排名、電商系統(tǒng)中商品的銷量排名等。

    我們以博文點(diǎn)贊排名為例,小林發(fā)表了五篇博文,分別獲得贊為 200、40、100、50、150。

    # arcticle:1 文章獲得了200個(gè)贊> ZADD user:xiaolin:ranking 200 arcticle:1(integer) 1# arcticle:2 文章獲得了40個(gè)贊> ZADD user:xiaolin:ranking 40 arcticle:2(integer) 1# arcticle:3 文章獲得了100個(gè)贊> ZADD user:xiaolin:ranking 100 arcticle:3(integer) 1# arcticle:4 文章獲得了50個(gè)贊> ZADD user:xiaolin:ranking 50 arcticle:4(integer) 1# arcticle:5 文章獲得了150個(gè)贊> ZADD user:xiaolin:ranking 150 arcticle:5(integer) 1

    文章 arcticle:4 新增一個(gè)贊,可以使用 ZINCRBY 命令(為有序集合key中元素member的分值加上increment):

    > ZINCRBY user:xiaolin:ranking 1 arcticle:4″51″

    查看某篇文章的贊數(shù),可以使用 ZSCORE 命令(返回有序集合key中元素個(gè)數(shù)):

    > ZSCORE user:xiaolin:ranking arcticle:4″50″

    獲取小林文章贊數(shù)最多的 3 篇文章,可以使用 ZREVRANGE 命令(倒序獲取有序集合 key 從start下標(biāo)到stop下標(biāo)的元素):

    # WITHSCORES 表示把 score 也顯示出來> ZREVRANGE user:xiaolin:ranking 0 2 WITHSCORES1) “arcticle:1″2) “200”3) “arcticle:5″4) “150”5) “arcticle:3″6) “100”

    獲取小林 100 贊到 200 贊的文章,可以使用 ZRANGEBYSCORE 命令(返回有序集合中指定分?jǐn)?shù)區(qū)間內(nèi)的成員,分?jǐn)?shù)由低到高排序):

    > ZRANGEBYSCORE user:xiaolin:ranking 100 200 WITHSCORES1) “arcticle:3″2) “100”3) “arcticle:5″4) “150”5) “arcticle:1″6) “200”

    電話、姓名排序

    使用有序集合的 ZRANGEBYLEX 或 ZREVRANGEBYLEX 可以幫助我們實(shí)現(xiàn)電話號(hào)碼或姓名的排序,我們以 ZRANGEBYLEX (返回指定成員區(qū)間內(nèi)的成員,按 key 正序排列,分?jǐn)?shù)必須相同)為例。

    注意:不要在分?jǐn)?shù)不一致的 SortSet 集合中去使用 ZRANGEBYLEX和 ZREVRANGEBYLEX 指令,因?yàn)楂@取的結(jié)果會(huì)不準(zhǔn)確。

    1、電話排序

    我們可以將電話號(hào)碼存儲(chǔ)到 SortSet 中,然后根據(jù)需要來獲取號(hào)段:

    > ZADD phone 0 13100111100 0 13110114300 0 13132110901 (integer) 3> ZADD phone 0 13200111100 0 13210414300 0 13252110901 (integer) 3> ZADD phone 0 13300111100 0 13310414300 0 13352110901 (integer) 3

    獲取所有號(hào)碼:

    > ZRANGEBYLEX phone – +1) “13100111100”2) “13110114300”3) “13132110901”4) “13200111100”5) “13210414300”6) “13252110901”7) “13300111100”8) “13310414300”9) “13352110901”

    獲取 132 號(hào)段的號(hào)碼:

    > ZRANGEBYLEX phone [132 (1331) “13200111100”2) “13210414300”3) “13252110901”

    獲取132、133號(hào)段的號(hào)碼:

    > ZRANGEBYLEX phone [132 (1341) “13200111100”2) “13210414300”3) “13252110901”4) “13300111100”5) “13310414300”6) “13352110901”

    2、姓名排序

    > zadd names 0 Toumas 0 Jake 0 Bluetuo 0 Gaodeng 0 Aimini 0 Aidehua (integer) 6

    獲取所有人的名字:

    > ZRANGEBYLEX names – +1) “Aidehua”2) “Aimini”3) “Bluetuo”4) “Gaodeng”5) “Jake”6) “Toumas”

    獲取名字中大寫字母A開頭的所有人:

    > ZRANGEBYLEX names [A (B1) “Aidehua”2) “Aimini”

    獲取名字中大寫字母 C 到 Z 的所有人:

    > ZRANGEBYLEX names [C [Z1) “Gaodeng”2) “Jake”3) “Toumas”

    BitMap

    介紹

    Bitmap,即位圖,是一串連續(xù)的二進(jìn)制數(shù)組(0和1),可以通過偏移量(offset)定位元素。BitMap通過最小的單位bit來進(jìn)行0|1的設(shè)置,表示某個(gè)元素的值或者狀態(tài),時(shí)間復(fù)雜度為O(1)。

    由于 bit 是計(jì)算機(jī)中最小的單位,使用它進(jìn)行儲(chǔ)存將非常節(jié)省空間,特別適合一些數(shù)據(jù)量大且使用二值統(tǒng)計(jì)的場(chǎng)景。

    內(nèi)部實(shí)現(xiàn)

    Bitmap 本身是用 String 類型作為底層數(shù)據(jù)結(jié)構(gòu)實(shí)現(xiàn)的一種統(tǒng)計(jì)二值狀態(tài)的數(shù)據(jù)類型。

    String 類型是會(huì)保存為二進(jìn)制的字節(jié)數(shù)組,所以,Redis 就把字節(jié)數(shù)組的每個(gè) bit 位利用起來,用來表示一個(gè)元素的二值狀態(tài),你可以把 Bitmap 看作是一個(gè) bit 數(shù)組。

    常用命令

    bitmap 基本操作:

    # 設(shè)置值,其中value只能是 0 和 1SETBIT key offset value# 獲取值GETBIT key offset# 獲取指定范圍內(nèi)值為 1 的個(gè)數(shù)# start 和 end 以字節(jié)為單位BITCOUNT key start end

    bitmap 運(yùn)算操作:

    # BitMap間的運(yùn)算# operations 位移操作符,枚舉值 AND 與運(yùn)算 & OR 或運(yùn)算 | XOR 異或 ^ NOT 取反 ~# result 計(jì)算的結(jié)果,會(huì)存儲(chǔ)在該key中# key1 … keyn 參與運(yùn)算的key,可以有多個(gè),空格分割,not運(yùn)算只能一個(gè)key# 當(dāng) BITOP 處理不同長(zhǎng)度的字符串時(shí),較短的那個(gè)字符串所缺少的部分會(huì)被看作 0。返回值是保存到 destkey 的字符串的長(zhǎng)度(以字節(jié)byte為單位),和輸入 key 中最長(zhǎng)的字符串長(zhǎng)度相等。BITOP [operations] [result] [key1] [keyn…]# 返回指定key中第一次出現(xiàn)指定value(0/1)的位置BITPOS [key] [value]

    應(yīng)用場(chǎng)景

    Bitmap 類型非常適合二值狀態(tài)統(tǒng)計(jì)的場(chǎng)景,這里的二值狀態(tài)就是指集合元素的取值就只有 0 和 1 兩種,在記錄海量數(shù)據(jù)時(shí),Bitmap 能夠有效地節(jié)省內(nèi)存空間。

    簽到統(tǒng)計(jì)

    在簽到打卡的場(chǎng)景中,我們只用記錄簽到(1)或未簽到(0),所以它就是非常典型的二值狀態(tài)。

    簽到統(tǒng)計(jì)時(shí),每個(gè)用戶一天的簽到用 1 個(gè) bit 位就能表示,一個(gè)月(假設(shè)是 31 天)的簽到情況用 31 個(gè) bit 位就可以,而一年的簽到也只需要用 365 個(gè) bit 位,根本不用太復(fù)雜的集合類型。

    假設(shè)我們要統(tǒng)計(jì) ID 100 的用戶在 2022 年 6 月份的簽到情況,就可以按照下面的步驟進(jìn)行操作。

    第一步,執(zhí)行下面的命令,記錄該用戶 6 月 3 號(hào)已簽到。

    SETBIT uid:sign:100:202206 2 1

    第二步,檢查該用戶 6 月 3 日是否簽到。

    GETBIT uid:sign:100:202206 2

    第三步,統(tǒng)計(jì)該用戶在 6 月份的簽到次數(shù)。

    BITCOUNT uid:sign:100:202206

    這樣,我們就知道該用戶在 6 月份的簽到情況了。

    如何統(tǒng)計(jì)這個(gè)月首次打卡時(shí)間呢?

    Redis 提供了 BITPOS key bitValue [start] [end]指令,返回?cái)?shù)據(jù)表示 Bitmap 中第一個(gè)值為 bitValue 的 offset 位置。

    在默認(rèn)情況下, 命令將檢測(cè)整個(gè)位圖, 用戶可以通過可選的 start 參數(shù)和 end 參數(shù)指定要檢測(cè)的范圍。所以我們可以通過執(zhí)行這條命令來獲取 userID = 100 在 2022 年 6 月份首次打卡日期:

    BITPOS uid:sign:100:202206 1

    需要注意的是,因?yàn)?offset 從 0 開始的,所以我們需要將返回的 value + 1 。

    判斷用戶登陸態(tài)

    Bitmap 提供了 GETBIT、SETBIT 操作,通過一個(gè)偏移值 offset 對(duì) bit 數(shù)組的 offset 位置的 bit 位進(jìn)行讀寫操作,需要注意的是 offset 從 0 開始。

    只需要一個(gè) key = login_status 表示存儲(chǔ)用戶登陸狀態(tài)集合數(shù)據(jù), 將用戶 ID 作為 offset,在線就設(shè)置為 1,下線設(shè)置 0。通過 GETBIT判斷對(duì)應(yīng)的用戶是否在線。50000 萬(wàn) 用戶只需要 6 MB 的空間。

    假如我們要判斷 ID = 10086 的用戶的登陸情況:

    第一步,執(zhí)行以下指令,表示用戶已登錄。

    SETBIT login_status 10086 1

    第二步,檢查該用戶是否登陸,返回值 1 表示已登錄。

    GETBIT login_status 10086

    第三步,登出,將 offset 對(duì)應(yīng)的 value 設(shè)置成 0。

    SETBIT login_status 10086 0

    連續(xù)簽到用戶總數(shù)

    如何統(tǒng)計(jì)出這連續(xù) 7 天連續(xù)打卡用戶總數(shù)呢?

    我們把每天的日期作為 Bitmap 的 key,userId 作為 offset,若是打卡則將 offset 位置的 bit 設(shè)置成 1。

    key 對(duì)應(yīng)的集合的每個(gè) bit 位的數(shù)據(jù)則是一個(gè)用戶在該日期的打卡記錄。

    一共有 7 個(gè)這樣的 Bitmap,如果我們能對(duì)這 7 個(gè) Bitmap 的對(duì)應(yīng)的 bit 位做 『與』運(yùn)算。同樣的 UserID offset 都是一樣的,當(dāng)一個(gè) userID 在 7 個(gè) Bitmap 對(duì)應(yīng)對(duì)應(yīng)的 offset 位置的 bit = 1 就說明該用戶 7 天連續(xù)打卡。

    結(jié)果保存到一個(gè)新 Bitmap 中,我們?cè)偻ㄟ^ BITCOUNT 統(tǒng)計(jì) bit = 1 的個(gè)數(shù)便得到了連續(xù)打卡 3 天的用戶總數(shù)了。

    Redis 提供了 BITOP operation destkey key [key …]這個(gè)指令用于對(duì)一個(gè)或者多個(gè) key 的 Bitmap 進(jìn)行位元操作。

    • opration 可以是 and、OR、NOT、XOR。當(dāng) BITOP 處理不同長(zhǎng)度的字符串時(shí),較短的那個(gè)字符串所缺少的部分會(huì)被看作 0 ??盏?key 也被看作是包含 0 的字符串序列。

    舉個(gè)例子,比如將三個(gè) bitmap 進(jìn)行 AND 操作,并將結(jié)果保存到 destmap 中,接著對(duì) destmap 執(zhí)行 BITCOUNT 統(tǒng)計(jì)。

    # 與操作BITOP AND destmap bitmap:01 bitmap:02 bitmap:03# 統(tǒng)計(jì) bit 位 = 1 的個(gè)數(shù)BITCOUNT destmap

    即使一天產(chǎn)生一個(gè)億的數(shù)據(jù),Bitmap 占用的內(nèi)存也不大,大約占 12 MB 的內(nèi)存(10^8/8/1024/1024),7 天的 Bitmap 的內(nèi)存開銷約為 84 MB。同時(shí)我們最好給 Bitmap 設(shè)置過期時(shí)間,讓 Redis 刪除過期的打卡數(shù)據(jù),節(jié)省內(nèi)存。

    HyperLogLog

    介紹

    Redis HyperLogLog 是 Redis 2.8.9 版本新增的數(shù)據(jù)類型,是一種用于「統(tǒng)計(jì)基數(shù)」的數(shù)據(jù)集合類型,基數(shù)統(tǒng)計(jì)就是指統(tǒng)計(jì)一個(gè)集合中不重復(fù)的元素個(gè)數(shù)。但要注意,HyperLogLog 是統(tǒng)計(jì)規(guī)則是基于概率完成的,不是非常準(zhǔn)確,標(biāo)準(zhǔn)誤算率是 0.81%。

    所以,簡(jiǎn)單來說 HyperLogLog 提供不精確的去重計(jì)數(shù)。

    HyperLogLog 的優(yōu)點(diǎn)是,在輸入元素的數(shù)量或者體積非常非常大時(shí),計(jì)算基數(shù)所需的內(nèi)存空間總是固定的、并且是很小的。

    在 Redis 里面,每個(gè) HyperLogLog 鍵只需要花費(fèi) 12 KB 內(nèi)存,就可以計(jì)算接近 2^64 個(gè)不同元素的基數(shù),和元素越多就越耗費(fèi)內(nèi)存的 Set 和 Hash 類型相比,HyperLogLog 就非常節(jié)省空間。

    這什么概念?舉個(gè)例子給大家對(duì)比一下。

    用 Java 語(yǔ)言來說,一般 long 類型占用 8 字節(jié),而 1 字節(jié)有 8 位,即:1 byte = 8 bit,即 long 數(shù)據(jù)類型最大可以表示的數(shù)是:2^63-1。對(duì)應(yīng)上面的2^64個(gè)數(shù),假設(shè)此時(shí)有2^63-1這么多個(gè)數(shù),從 0 ~ 2^63-1,按照l(shuí)ong以及1k = 1024 字節(jié)的規(guī)則來計(jì)算內(nèi)存總數(shù),就是:((2^63-1) * 8/1024)K,這是很龐大的一個(gè)數(shù),存儲(chǔ)空間遠(yuǎn)遠(yuǎn)超過12K,而HyperLogLog 卻可以用 12K 就能統(tǒng)計(jì)完。

    內(nèi)部實(shí)現(xiàn)

    HyperLogLog 的實(shí)現(xiàn)涉及到很多數(shù)學(xué)問題,太費(fèi)腦子了,我也沒有搞懂。

    常見命令

    HyperLogLog 命令很少,就三個(gè)。

    # 添加指定元素到 HyperLogLog 中PFADD key element [element …]# 返回給定 HyperLogLog 的基數(shù)估算值。PFCOUNT key [key …]# 將多個(gè) HyperLogLog 合并為一個(gè) HyperLogLogPFMERGE destkey sourcekey [sourcekey …]

    應(yīng)用場(chǎng)景

    百萬(wàn)級(jí)網(wǎng)頁(yè) UV 計(jì)數(shù)

    Redis HyperLogLog 優(yōu)勢(shì)在于只需要花費(fèi) 12 KB 內(nèi)存,就可以計(jì)算接近 2^64 個(gè)元素的基數(shù),和元素越多就越耗費(fèi)內(nèi)存的 Set 和 Hash 類型相比,HyperLogLog 就非常節(jié)省空間。

    所以,非常適合統(tǒng)計(jì)百萬(wàn)級(jí)以上的網(wǎng)頁(yè) UV 的場(chǎng)景。

    在統(tǒng)計(jì) UV 時(shí),你可以用 PFADD 命令(用于向 HyperLogLog 中添加新元素)把訪問頁(yè)面的每個(gè)用戶都添加到 HyperLogLog 中。

    PFADD page1:uv user1 user2 user3 user4 user5

    接下來,就可以用 PFCOUNT 命令直接獲得 page1 的 UV 值了,這個(gè)命令的作用就是返回 HyperLogLog 的統(tǒng)計(jì)結(jié)果。

    PFCOUNT page1:uv

    不過,有一點(diǎn)需要你注意一下,HyperLogLog 的統(tǒng)計(jì)規(guī)則是基于概率完成的,所以它給出的統(tǒng)計(jì)結(jié)果是有一定誤差的,標(biāo)準(zhǔn)誤算率是 0.81%。

    這也就意味著,你使用 HyperLogLog 統(tǒng)計(jì)的 UV 是 100 萬(wàn),但實(shí)際的 UV 可能是 101 萬(wàn)。雖然誤差率不算大,但是,如果你需要精確統(tǒng)計(jì)結(jié)果的話,最好還是繼續(xù)用 Set 或 Hash 類型。

    GEO

    Redis GEO 是 Redis 3.2 版本新增的數(shù)據(jù)類型,主要用于存儲(chǔ)地理位置信息,并對(duì)存儲(chǔ)的信息進(jìn)行操作。

    在日常生活中,我們?cè)絹碓揭蕾囁阉鳌案浇牟宛^”、在打車軟件上叫車,這些都離不開基于位置信息服務(wù)(Location-Based Service,LBS)的應(yīng)用。LBS 應(yīng)用訪問的數(shù)據(jù)是和人或物關(guān)聯(lián)的一組經(jīng)緯度信息,而且要能查詢相鄰的經(jīng)緯度范圍,GEO 就非常適合應(yīng)用在 LBS 服務(wù)的場(chǎng)景中。

    內(nèi)部實(shí)現(xiàn)

    GEO 本身并沒有設(shè)計(jì)新的底層數(shù)據(jù)結(jié)構(gòu),而是直接使用了 Sorted Set 集合類型。

    GEO 類型使用 GeoHash 編碼方法實(shí)現(xiàn)了經(jīng)緯度到 Sorted Set 中元素權(quán)重分?jǐn)?shù)的轉(zhuǎn)換,這其中的兩個(gè)關(guān)鍵機(jī)制就是「對(duì)二維地圖做區(qū)間劃分」和「對(duì)區(qū)間進(jìn)行編碼」。一組經(jīng)緯度落在某個(gè)區(qū)間后,就用區(qū)間的編碼值來表示,并把編碼值作為 Sorted Set 元素的權(quán)重分?jǐn)?shù)。

    這樣一來,我們就可以把經(jīng)緯度保存到 Sorted Set 中,利用 Sorted Set 提供的“按權(quán)重進(jìn)行有序范圍查找”的特性,實(shí)現(xiàn) LBS 服務(wù)中頻繁使用的“搜索附近”的需求。

    常用命令

    # 存儲(chǔ)指定的地理空間位置,可以將一個(gè)或多個(gè)經(jīng)度(longitude)、緯度(latitude)、位置名稱(member)添加到指定的 key 中。GEOADD key longitude latitude member [longitude latitude member …]# 從給定的 key 里返回所有指定名稱(member)的位置(經(jīng)度和緯度),不存在的返回 nil。GEOPOS key member [member …]# 返回兩個(gè)給定位置之間的距離。GEODIST key member1 member2 [m|km|ft|mi]# 根據(jù)用戶給定的經(jīng)緯度坐標(biāo)來獲取指定范圍內(nèi)的地理位置集合。GEORADIUS key longitude latitude radius m|km|ft|mi [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count] [ASC|DESC] [STORE key] [STOREDIST key]

    應(yīng)用場(chǎng)景

    滴滴叫車

    這里以滴滴叫車的場(chǎng)景為例,介紹下具體如何使用 GEO 命令:GEOADD 和 GEORADIUS 這兩個(gè)命令。

    假設(shè)車輛 ID 是 33,經(jīng)緯度位置是(116.034579,39.030452),我們可以用一個(gè) GEO 集合保存所有車輛的經(jīng)緯度,集合 key 是 cars:locations。

    執(zhí)行下面的這個(gè)命令,就可以把 ID 號(hào)為 33 的車輛的當(dāng)前經(jīng)緯度位置存入 GEO 集合中:

    GEOADD cars:locations 116.034579 39.030452 33

    當(dāng)用戶想要尋找自己附近的網(wǎng)約車時(shí),LBS 應(yīng)用就可以使用 GEORADIUS 命令。

    例如,LBS 應(yīng)用執(zhí)行下面的命令時(shí),Redis 會(huì)根據(jù)輸入的用戶的經(jīng)緯度信息(116.054579,39.030452 ),查找以這個(gè)經(jīng)緯度為中心的 5 公里內(nèi)的車輛信息,并返回給 LBS 應(yīng)用。

    GEORADIUS cars:locations 116.054579 39.030452 5 km ASC COUNT 10

    Stream

    介紹

    Redis Stream 是 Redis 5.0 版本新增加的數(shù)據(jù)類型,Redis 專門為消息隊(duì)列設(shè)計(jì)的數(shù)據(jù)類型。

    在前面介紹 List 類型實(shí)現(xiàn)的消息隊(duì)列,有兩個(gè)問題:1. 生產(chǎn)者需要自行實(shí)現(xiàn)全局唯一 ID;2. 不能以消費(fèi)組形式消費(fèi)數(shù)據(jù)。

    基于 Stream 類型的消息隊(duì)列就解決上面的問題,它不僅支持自動(dòng)生成全局唯一 ID,而且支持以消費(fèi)組形式消費(fèi)數(shù)據(jù)。

    常見命令

    Stream 消息隊(duì)列操作命令:

    • XADD:插入消息,保證有序,可以自動(dòng)生成全局唯一 ID;
    • XREAD:用于讀取消息,可以按 ID 讀取數(shù)據(jù);
    • XREADGROUP:按消費(fèi)組形式讀取消息;
    • XPENDING 和 XACK:
      • XPENDING 命令可以用來查詢每個(gè)消費(fèi)組內(nèi)所有消費(fèi)者已讀取但尚未確認(rèn)的消息,而 XACK 命令用于向消息隊(duì)列確認(rèn)消息處理已完成。

    應(yīng)用場(chǎng)景

    消息隊(duì)列

    生產(chǎn)者通過 XADD 命令插入一條消息:

    # * 表示讓 Redis 為插入的數(shù)據(jù)自動(dòng)生成一個(gè)全局唯一的 ID# 往名稱為 mymq 的消息隊(duì)列中插入一條消息,消息的鍵是 name,值是 xiaolin> XADD mymq * name xiaolin”1654254953808-0″

    插入成功后會(huì)返回全局唯一的 ID:”1654254953808-0″。消息的全局唯一 ID 由兩部分組成:

    • 第一部分“1654254953808”是數(shù)據(jù)插入時(shí),以毫秒為單位計(jì)算的當(dāng)前服務(wù)器時(shí)間;
    • 第二部分表示插入消息在當(dāng)前毫秒內(nèi)的消息序號(hào),這是從 0 開始編號(hào)的。例如,“1654254953808-0”就表示在“1654254953808”毫秒內(nèi)的第 1 條消息。

    消費(fèi)者通過 XREAD 命令從消息隊(duì)列中讀取消息時(shí),可以指定一個(gè)消息 ID,并從這個(gè)消息 ID 的下一條消息開始進(jìn)行讀?。ㄗ⒁馐禽斎胂?ID 的下一條信息開始讀取,不是查詢輸入ID的消息)。

    # 從 ID 號(hào)為 1654254953807-0 的消息開始,讀取后續(xù)的所有消息(示例中一共 1 條)。> XREAD Stream mymq 1654254953807-01) 1) “mymq” 2) 1) 1) “1654254953808-0” 2) 1) “name” 2) “xiaolin”

    如果想要實(shí)現(xiàn)阻塞讀(當(dāng)沒有數(shù)據(jù)時(shí),阻塞?。?,可以調(diào)用 XRAED 時(shí)設(shè)定 block 配置項(xiàng),實(shí)現(xiàn)類似于 BRPOP 的阻塞讀取操作。

    比如,下面這命令,設(shè)置了 block 10000 的配置項(xiàng),10000 的單位是毫秒,表明 XREAD 在讀取最新消息時(shí),如果沒有消息到來,XREAD 將阻塞 10000 毫秒(即 10 秒),然后再返回。

    # 命令最后的“$”符號(hào)表示讀取最新的消息> XREAD block 10000 Stream mymq $(nil)(10.00s)

    前面介紹的這些操作 List 也支持的,接下來看看 Stream 特有的功能。

    Stream 可以以使用 XGROUP 創(chuàng)建消費(fèi)組,創(chuàng)建消費(fèi)組之后,Stream 可以使用 XREADGROUP 命令讓消費(fèi)組內(nèi)的消費(fèi)者讀取消息。

    創(chuàng)建一個(gè)名為 group1 的消費(fèi)組,這個(gè)消費(fèi)組消費(fèi)的消息隊(duì)列是 mymq:

    # 創(chuàng)建一個(gè)名為 group1 的消費(fèi)組> XGROUP create mymq group1 0OK

    消費(fèi)組 group1 內(nèi)的消費(fèi)者 consumer1 從 mymq 消息隊(duì)列中讀取所有消息的命令如下:

    # 命令最后的參數(shù)“>”,表示從第一條尚未被消費(fèi)的消息開始讀取。> XREADGROUP group group1 consumer1 Stream mymq >1) 1) “mymq” 2) 1) 1) “1654254953808-0” 2) 1) “name” 2) “xiaolin”

    消息隊(duì)列中的消息一旦被消費(fèi)組里的一個(gè)消費(fèi)者讀取了,就不能再被該消費(fèi)組內(nèi)的其他消費(fèi)者讀取了。

    比如說,我們執(zhí)行完剛才的 XREADGROUP 命令后,再執(zhí)行一次同樣的命令,此時(shí)讀到的就是空值了:

    > XREADGROUP group group1 consumer1 Stream mymq >(nil)

    使用消費(fèi)組的目的是讓組內(nèi)的多個(gè)消費(fèi)者共同分擔(dān)讀取消息,所以,我們通常會(huì)讓每個(gè)消費(fèi)者讀取部分消息,從而實(shí)現(xiàn)消息讀取負(fù)載在多個(gè)消費(fèi)者間是均衡分布的。

    例如,我們執(zhí)行下列命令,讓 group2 中的 consumer1、2、3 各自讀取一條消息。

    # 讓 group2 中的 consumer1 從 mymq 消息隊(duì)列中消費(fèi)一條消息> XREADGROUP group group2 consumer1 count 1 Stream mymq >1) 1) “mymq” 2) 1) 1) “1654254953808-0” 2) 1) “name” 2) “xiaolin”# 讓 group2 中的 consumer2 從 mymq 消息隊(duì)列中消費(fèi)一條消息> XREADGROUP group group2 consumer2 count 1 Stream mymq >1) 1) “mymq” 2) 1) 1) “1654256265584-0” 2) 1) “name” 2) “xiaolincoding”# 讓 group2 中的 consumer3 從 mymq 消息隊(duì)列中消費(fèi)一條消息> XREADGROUP group group2 consumer3 count 1 Stream mymq >1) 1) “mymq” 2) 1) 1) “1654256271337-0” 2) 1) “name” 2) “Tom”

    基于 Stream 實(shí)現(xiàn)的消息隊(duì)列,如何保證消費(fèi)者在發(fā)生故障或宕機(jī)再次重啟后,仍然可以讀取未處理完的消息?

    Streams 會(huì)自動(dòng)使用內(nèi)部隊(duì)列(也稱為 PENDING List)留存消費(fèi)組里每個(gè)消費(fèi)者讀取的消息,直到消費(fèi)者使用 XACK 命令通知 Streams“消息已經(jīng)處理完成”。

    如果消費(fèi)者沒有成功處理消息,它就不會(huì)給 Streams 發(fā)送 XACK 命令,消息仍然會(huì)留存。此時(shí),消費(fèi)者可以在重啟后,用 XPENDING 命令查看已讀取、但尚未確認(rèn)處理完成的消息。

    例如,我們來查看一下 group2 中各個(gè)消費(fèi)者已讀取、但尚未確認(rèn)的消息個(gè)數(shù),命令如下:

    127.0.0.1:6379> XPENDING mymq group21) (integer) 32) “1654254953808-0” # 表示 group2 中所有消費(fèi)者讀取的消息最小 ID3) “1654256271337-0” # 表示 group2 中所有消費(fèi)者讀取的消息最大 ID4) 1) 1) “consumer1” 2) “1” 2) 1) “consumer2” 2) “1” 3) 1) “consumer3” 2) “1”

    如果想查看某個(gè)消費(fèi)者具體讀取了哪些數(shù)據(jù),可以執(zhí)行下面的命令:

    # 查看 group2 里 consumer2 已從 mymq 消息隊(duì)列中讀取了哪些消息> XPENDING mymq group2 – + 10 consumer21) 1) “1654256265584-0” 2) “consumer2” 3) (integer) 410700 4) (integer) 1

    可以看到,consumer2 已讀取的消息的 ID 是 1654256265584-0。

    一旦消息 1654256265584-0 被 consumer2 處理了,consumer2 就可以使用 XACK 命令通知 Streams,然后這條消息就會(huì)被刪除。

    > XACK mymq group2 1654256265584-0(integer) 1

    當(dāng)我們?cè)偈褂?XPENDING 命令查看時(shí),就可以看到,consumer2 已經(jīng)沒有已讀取、但尚未確認(rèn)處理的消息了。

    > XPENDING mymq group2 – + 10 consumer2(empty array)

    好了,基于 Stream 實(shí)現(xiàn)的消息隊(duì)列就說到這里了,小結(jié)一下:

    • 消息保序:XADD/XREAD
    • 阻塞讀?。篨READ block
    • 重復(fù)消息處理:Stream 在使用 XADD 命令,會(huì)自動(dòng)生成全局唯一 ID;
    • 消息可靠性:內(nèi)部使用 PENDING List 自動(dòng)保存消息,使用 XPENDING 命令查看消費(fèi)組已經(jīng)讀取但是未被確認(rèn)的消息,消費(fèi)者使用 XACK 確認(rèn)消息;
    • 支持消費(fèi)組形式消費(fèi)數(shù)據(jù)

    Redis 基于 Stream 消息隊(duì)列與專業(yè)的消息隊(duì)列有哪些差距?

    一個(gè)專業(yè)的消息隊(duì)列,必須要做到兩大塊:

    • 消息不丟。
    • 消息可堆積。

    1、Redis Stream 消息會(huì)丟失嗎?

    使用一個(gè)消息隊(duì)列,其實(shí)就分為三大塊:生產(chǎn)者、隊(duì)列中間件、消費(fèi)者,所以要保證消息就是保證三個(gè)環(huán)節(jié)都不能丟失數(shù)據(jù)。

    Redis Stream 消息隊(duì)列能不能保證三個(gè)環(huán)節(jié)都不丟失數(shù)據(jù)?

    • Redis 生產(chǎn)者會(huì)不會(huì)丟消息?生產(chǎn)者會(huì)不會(huì)丟消息,取決于生產(chǎn)者對(duì)于異常情況的處理是否合理。從消息被生產(chǎn)出來,然后提交給 MQ 的過程中,只要能正常收到 ( MQ 中間件) 的 ack 確認(rèn)響應(yīng),就表示發(fā)送成功,所以只要處理好返回值和異常,如果返回異常則進(jìn)行消息重發(fā),那么這個(gè)階段是不會(huì)出現(xiàn)消息丟失的。
    • Redis 消費(fèi)者會(huì)不會(huì)丟消息?不會(huì),因?yàn)?Stream ( MQ 中間件)會(huì)自動(dòng)使用內(nèi)部隊(duì)列(也稱為 PENDING List)留存消費(fèi)組里每個(gè)消費(fèi)者讀取的消息,但是未被確認(rèn)的消息。消費(fèi)者可以在重啟后,用 XPENDING 命令查看已讀取、但尚未確認(rèn)處理完成的消息。等到消費(fèi)者執(zhí)行完業(yè)務(wù)邏輯后,再發(fā)送消費(fèi)確認(rèn) XACK 命令,也能保證消息的不丟失。
    • Redis 隊(duì)列中間件會(huì)不會(huì)丟消息?會(huì),Redis 在以下 2 個(gè)場(chǎng)景下,都會(huì)導(dǎo)致數(shù)據(jù)丟失:
      • AOF 持久化配置為每秒寫盤,但這個(gè)寫盤過程是異步的,Redis 宕機(jī)時(shí)會(huì)存在數(shù)據(jù)丟失的可能
      • 主從復(fù)制也是異步的,主從切換時(shí),也存在丟失數(shù)據(jù)的可能。

    可以看到,Redis 在隊(duì)列中間件環(huán)節(jié)無(wú)法保證消息不丟。像 RabbitMQ 或 Kafka 這類專業(yè)的隊(duì)列中間件,在使用時(shí)是部署一個(gè)集群,生產(chǎn)者在發(fā)布消息時(shí),隊(duì)列中間件通常會(huì)寫「多個(gè)節(jié)點(diǎn)」,也就是有多個(gè)副本,這樣一來,即便其中一個(gè)節(jié)點(diǎn)掛了,也能保證集群的數(shù)據(jù)不丟失。

    2、Redis Stream 消息可堆積嗎?

    Redis 的數(shù)據(jù)都存儲(chǔ)在內(nèi)存中,這就意味著一旦發(fā)生消息積壓,則會(huì)導(dǎo)致 Redis 的內(nèi)存持續(xù)增長(zhǎng),如果超過機(jī)器內(nèi)存上限,就會(huì)面臨被 OOM 的風(fēng)險(xiǎn)。所以 Redis 的 Stream 提供了可以指定隊(duì)列最大長(zhǎng)度的功能,就是為了避免這種情況發(fā)生。

    但 Kafka、RabbitMQ 專業(yè)的消息隊(duì)列它們的數(shù)據(jù)都是存儲(chǔ)在磁盤上,當(dāng)消息積壓時(shí),無(wú)非就是多占用一些磁盤空間。

    因此,把 Redis 當(dāng)作隊(duì)列來使用時(shí),會(huì)面臨的 2 個(gè)問題:

    • Redis 本身可能會(huì)丟數(shù)據(jù);
    • 面對(duì)消息擠壓,內(nèi)存資源會(huì)緊張;

    所以,能不能將 Redis 作為消息隊(duì)列來使用,關(guān)鍵看你的業(yè)務(wù)場(chǎng)景:

    • 如果你的業(yè)務(wù)場(chǎng)景足夠簡(jiǎn)單,對(duì)于數(shù)據(jù)丟失不敏感,而且消息積壓概率比較小的情況下,把 Redis 當(dāng)作隊(duì)列是完全可以的。
    • 如果你的業(yè)務(wù)有海量消息,消息積壓的概率比較大,并且不能接受數(shù)據(jù)丟失,那么還是用專業(yè)的消息隊(duì)列中間件吧。

    參考資料:

    • 《Redis 核心技術(shù)與實(shí)戰(zhàn)》
    • https://www.cnblogs.com/hunternet/p/12742390.html
    • https://www.cnblogs.com/qdhxhz/p/15669348.html
    • https://www.cnblogs.com/bbgs-xc/p/14376109.html
    • http://kaito-kidd.com/2021/04/19/can-redis-be-used-as-a-queue/

    總結(jié)

    Redis 常見的五種數(shù)據(jù)類型:**String(字符串),Hash(哈希),List(列表),Set(集合)及 Zset(sorted set:有序集合)**。

    這五種數(shù)據(jù)類型都由多種數(shù)據(jù)結(jié)構(gòu)實(shí)現(xiàn)的,主要是出于時(shí)間和空間的考慮,當(dāng)數(shù)據(jù)量小的時(shí)候使用更簡(jiǎn)單的數(shù)據(jù)結(jié)構(gòu),有利于節(jié)省內(nèi)存,提高性能。

    這五種數(shù)據(jù)類型與底層數(shù)據(jù)結(jié)構(gòu)對(duì)應(yīng)關(guān)系圖如下,左邊是 Redis 3.0版本的,也就是《Redis 設(shè)計(jì)與實(shí)現(xiàn)》這本書講解的版本,現(xiàn)在看還是有點(diǎn)過時(shí)了,右邊是現(xiàn)在 Github 最新的 Redis 代碼的。

    可以看到,Redis 數(shù)據(jù)類型的底層數(shù)據(jù)結(jié)構(gòu)隨著版本的更新也有所不同,比如:

    • 在 Redis 3.0 版本中 List 對(duì)象的底層數(shù)據(jù)結(jié)構(gòu)由「雙向鏈表」或「壓縮表列表」實(shí)現(xiàn),但是在 3.2 版本之后,List 數(shù)據(jù)類型底層數(shù)據(jù)結(jié)構(gòu)是由 quicklist 實(shí)現(xiàn)的;
    • 在最新的 Redis 代碼中,壓縮列表數(shù)據(jù)結(jié)構(gòu)已經(jīng)廢棄了,交由 listpack 數(shù)據(jù)結(jié)構(gòu)來實(shí)現(xiàn)了。

    Redis 五種數(shù)據(jù)類型的應(yīng)用場(chǎng)景:

    • String 類型的應(yīng)用場(chǎng)景:緩存對(duì)象、常規(guī)計(jì)數(shù)、分布式鎖等。
    • List 類型的應(yīng)用場(chǎng)景:消息隊(duì)列(有兩個(gè)問題:1. 生產(chǎn)者需要自行實(shí)現(xiàn)全局唯一 ID;2. 不能以消費(fèi)組形式消費(fèi)數(shù)據(jù))等。
    • Hash 類型:緩存對(duì)象、購(gòu)物車等。
    • Set 類型:聚合計(jì)算(并集、交集、差集)場(chǎng)景,比如點(diǎn)贊、共同關(guān)注、抽獎(jiǎng)活動(dòng)等。
    • Zset 類型:排序場(chǎng)景,比如排行榜、電話和姓名排序等。

    Redis 后續(xù)版本又支持四種數(shù)據(jù)類型,它們的應(yīng)用場(chǎng)景如下:

    • BitMap(2.2 版新增):二值狀態(tài)統(tǒng)計(jì)的場(chǎng)景,比如簽到、判斷用戶登陸狀態(tài)、連續(xù)簽到用戶總數(shù)等;
    • HyperLogLog(2.8 版新增):海量數(shù)據(jù)基數(shù)統(tǒng)計(jì)的場(chǎng)景,比如百萬(wàn)級(jí)網(wǎng)頁(yè) UV 計(jì)數(shù)等;
    • GEO(3.2 版新增):存儲(chǔ)地理位置信息的場(chǎng)景,比如滴滴叫車;
    • Stream(5.0 版新增):消息隊(duì)列,相比于基于 List 類型實(shí)現(xiàn)的消息隊(duì)列,有這兩個(gè)特有的特性:自動(dòng)生成全局唯一消息ID,支持以消費(fèi)組形式消費(fèi)數(shù)據(jù)。

    針對(duì) Redis 是否適合做消息隊(duì)列,關(guān)鍵看你的業(yè)務(wù)場(chǎng)景:

    • 如果你的業(yè)務(wù)場(chǎng)景足夠簡(jiǎn)單,對(duì)于數(shù)據(jù)丟失不敏感,而且消息積壓概率比較小的情況下,把 Redis 當(dāng)作隊(duì)列是完全可以的。
    • 如果你的業(yè)務(wù)有海量消息,消息積壓的概率比較大,并且不能接受數(shù)據(jù)丟失,那么還是用專業(yè)的消息隊(duì)列中間件吧。

    原文鏈接:https://mp.weixin.qq.com/s/r9_0xpRsp2ubgyvpiyMfuw

    鄭重聲明:本文內(nèi)容及圖片均整理自互聯(lián)網(wǎng),不代表本站立場(chǎng),版權(quán)歸原作者所有,如有侵權(quán)請(qǐng)聯(lián)系管理員(admin#wlmqw.com)刪除。
    上一篇 2022年6月16日 06:04
    下一篇 2022年6月16日 06:04

    相關(guān)推薦

    聯(lián)系我們

    聯(lián)系郵箱:admin#wlmqw.com
    工作時(shí)間:周一至周五,10:30-18:30,節(jié)假日休息