最全的 Redis 數據類型解析吞琐!

1. String(字符串)

1.1 簡單介紹

字符串類型是Redis最基礎的數據結構,字符串類型可以是JSON然爆、XML甚至是二進制的圖片等數據站粟,但是最大值不能超過512MB。在 Redis 中施蜜,String 是可以修改的卒蘸,稱為動態(tài)字符串(Simple Dynamic String簡稱SDS)雌隅,說是字符串但它的內部結構更像是一個ArrayList翻默,內部維護著一個字節(jié)數組,并且在其內部預分配了一定的空間恰起,以減少內存的頻繁分配修械。Redis的內存分配機制是這樣:

  • 當字符串的長度小于 1MB時,每次擴容都是加倍現(xiàn)有的空間检盼。
  • 如果字符串長度超過 1MB時肯污,每次擴容時只會擴展 1MB 的空間。

這樣既保證了內存空間夠用吨枉,還不至于造成內存的浪費蹦渣,字符串最大長度為512MB.

分析一下字符串的數據結構

struct SDS {
  T capacity;       //數組容量
  T len;            //實際長度
  byte flages;  //標志位,低三位表示類型
  byte[] content;   //數組內容
}

capacitylen兩個屬性都是泛型貌亭,為什么不直接用int類型柬唯?因為Redis內部有很多優(yōu)化方案,為更合理的使用內存圃庭,不同長度的字符串采用不同的數據類型表示(int锄奢、embstrraw)剧腻,且在創(chuàng)建字符串的時候len會和capacity一樣大拘央,不產生冗余的空間,所以String值可以是字符串书在、數字(整數灰伟、浮點數) 或者 二進制。

Redis會根據當前值的類型和長度決定使用哪種內部編碼來實現(xiàn)儒旬。字符串類型的內部編碼有3種:

  1. int:8個字節(jié)的長整型栏账。
  2. embstr:小于等于39個字節(jié)的字符串遏乔。
  3. raw:大于39個字節(jié)的字符串。

1.2 應用場景

1.2.1 緩存

在 web 服務中发笔,通常使用 MySQL 作為數據庫盟萨,Redis 作為緩存。由于 Redis 具有支撐高并發(fā)的特性了讨,通常能起到加速讀寫和降低后端壓力的作用捻激。web 端的大多數請求都是從 Redis 中獲取的數據,如果 Redis 中沒有需要的數據前计,則會從 MySQL 中去獲取胞谭,并將獲取到的數據寫入 Redis。

1.2.2 計數

Redis 中有一個字符串相關的命令incr key男杈,incr命令對值做自增操作丈屹,返回結果分為以下三種情況:

  • 值不是整數,返回錯誤
  • 值是整數伶棒,返回自增后的結果
  • key不存在旺垒,默認鍵為0,返回1

比如文章的閱讀量肤无,視頻的播放量等等都會使用 Redis 來計數先蒋,每播放一次,對應的播放量就會加1宛渐,同時將這些數據異步存儲到數據庫中達到持久化的目的竞漾。

1.2.3 共享 Session

在分布式系統(tǒng)中,用戶的每次請求會訪問到不同的服務器窥翩,這就會導致 session 不同步的問題业岁,假如一個用來獲取用戶信息的請求落在 A 服務器上,獲取到用戶信息后存入 session寇蚊。下一個請求落在 B 服務器上笔时,想要從 session 中獲取用戶信息就不能正常獲取了,因為用戶信息的 session 在服務器 A 上幔荒,為了解決這個問題糊闽,使用 Redis 集中管理這些 session,將 session 存入redis爹梁,使用的時候直接從 Redis 中獲取就可以了右犹。

1.2.4 限速

為了安全考慮,有些網站會對 IP 進行限制姚垃,限制同一 IP 在一定時間內訪問次數不能超過 n 次念链。

1.3 String 常用命令

set [key]  [value]   給指定key設置值(set 可覆蓋老的值)
get [key]   獲取指定key 的值
del [key]   刪除指定key
exists [key]  判斷是否存在指定key
mset [key1]  [value1]  [key2]  [value2] ...... 批量存鍵值對
mget [key1]  [key2] ......   批量取key
expire [key]  [time]    給指定key 設置過期時間  單位秒
setex [key]  [time]  [value]  等價于 set + expire 命令組合
setnx [key]  [value]   如果key不存在則set 創(chuàng)建,否則返回0
incr [key]           如果value為整數 可用 incr命令每次自增1
incrby [key] [number]  使用incrby命令對整數值 進行增加 number

2. List(列表)

2.1 簡單介紹

Redis 中的ListJava中的LinkedList很像,底層都是一種鏈表結構掂墓,List的插入和刪除操作非城矗快,時間復雜度為 O(1)君编,不像數組結構插入跨嘉、刪除操作需要移動數據。像歸像吃嘿,但是 Redis 中的List底層可不是一個雙向鏈表那么簡單祠乃。

當數據量較少的時候它的底層存儲結構為一塊連續(xù)內存,稱之為ziplist(壓縮列表)兑燥,它將所有的元素緊挨著一起存儲亮瓷,分配的是一塊連續(xù)的內存;當數據量較多的時候將會變成quicklist(快速鏈表)結構降瞳。

可單純的鏈表也是有缺陷的嘱支,鏈表的前后指針prevnext會占用較多的內存,會比較浪費空間挣饥,而且會加重內存的碎片化除师。在 Redis 3.2 之后就都改用ziplist+鏈表的混合結構,稱之為quicklist(快速鏈表)亮靴。ziplist的每個集合元素使用兩個緊挨在一起的壓縮列表節(jié)點來保存馍盟,第一個節(jié)點保存元素的成員于置,第二個元素保存元素的分值茧吊。

2.2 應用場景

由于List是一個按照插入順序排序的列表,所以應用場景相對還較多的八毯,例如:

2.2.1 消息隊列

列表用來存儲多個有序的字符串搓侄,既然是有序的,那么就滿足消息隊列的特點话速。使用lpush+rpop或者rpush+lpop實現(xiàn)消息隊列讶踪。除此之外,redis支持阻塞操作泊交,在彈出元素的時候使用阻塞命令來實現(xiàn)阻塞隊列乳讥。

Redis 雖然支持消息隊列的實現(xiàn),但是并不支持 ack廓俭。所以 Redis 實現(xiàn)的消息隊列不能保證消息的可靠性云石,除非自己實現(xiàn)消息確認機制,不過這非常麻煩研乒,所以如果是重要的消息還是推薦使用專門的消息隊列去做汹忠。

2.2.2 棧

由于列表存儲的是有序字符串,滿足隊列的特點,也就能滿足棧先進后出的特點宽菜,使用lpush+lpop或者rpush+rpop實現(xiàn)棧谣膳。

2.2.3 文章列表

因為列表的元素不但是有序的,而且還支持按照索引范圍獲取元素铅乡。lpush命令和lrange命令能實現(xiàn)最新列表的功能继谚,每次通過lpush命令往列表里插入新的元素,然后通過lrange命令讀取最新的元素列表阵幸。比如我們可以使用命令lrange key 0 9分頁獲取文章列表犬庇。

2.3 List 常用命令

rpush [key] [value1] [value2] ......    鏈表右側插入
rpop [key]  移除右側列表頭元素,并返回該元素
lpop   [key]    移除左側列表頭元素侨嘀,并返回該元素
llen  [key]     返回該列表的元素個數
lrem [key] [count] [value]  刪除列表中與value相等的元素臭挽,count是刪除的個數。 count>0 表示從左側開始查找咬腕,刪除count個元素欢峰,count<0 表示從右側開始查找,刪除count個相同元素涨共,count=0 表示刪除全部相同的元素
lindex [key] [index]  獲取list指定下標的元素 (需要遍歷纽帖,時間復雜度為O(n)) index 代表元素下標,index 可以為負數举反, index= 表示倒數第一個元素懊直,同理 index=-2 表示倒數第二 個元素。
lrange [key]  [start_index] [end_index]   獲取list 區(qū)間內的所有元素(時間復雜度為 O(n))
ltrim  [key]  [start_index] [end_index]   保留區(qū)間內的元素,其他元素刪除(時間復雜度為 O(n))

3. Hash(字典)

3.1 簡單介紹

Redis 中的Hash和 Java 的HashMap更加相似,是數組+鏈表的結構柿赊,當發(fā)生 hash 碰撞時將會把元素追加到鏈表上狮荔,值得注意的是在RedisHashvalue只能是字符串。

3.2 使用場景

3.2.1 購物車

hset [key] [field] [value]命令, 可以實現(xiàn)以用戶Id商品Idfield,商品數量為value尝偎,恰好構成了購物車的3個要素。

3.2.2 存儲對象

hash類型的(key, field, value)的結構與對象的(對象id, 屬性, 值)的結構相似鹏控,也可以用來存儲對象致扯,如:

key=JavaUser293847
value={
  “id”: 1,
  “name”: “SnailClimb”,
  “age”: 22,
  “l(fā)ocation”: “Wuhan, Hubei”
}

3.3 Hash 常用命令

hset  [key]  [field] [value]    新建字段信息
hget  [key]  [field]    獲取字段信息
hdel [key] [field]  刪除字段
hlen  [key]   保存的字段個數
hgetall  [key]  獲取指定key 字典里的所有字段和值 (字段信息過多,會導致慢查詢 慎用:親身經歷 曾經用過這個這個指令導致線上服務故障)
hmset  [key]  [field1] [value1] [field2] [value2] ......   批量創(chuàng)建
hincr  [key] [field]   對字段值自增
hincrby [key] [field] [number] 對字段值增加number

4. Set(集合)

4.1 簡單介紹

Redis 中的setJava中的HashSet有些類似,它內部的鍵值對是無序的当辐、唯一 的抖僵。它的內部實現(xiàn)相當于一個特殊的字典,字典中所有的 value 都是一個值 NULL瀑构。當集合中最后一個元素被移除之后裆针,數據結構被自動刪除刨摩,內存被回收。

4.2 應用場景

  • 比如:在在線討論社區(qū)中世吨,可以將一個用戶所有的關注人存在一個集合中澡刹,將其所有粉絲存在一個集合。Redis可以非常方便的實現(xiàn)如共同關注耘婚、共同粉絲罢浇、共同喜好等功能。這個過程也就是求交集的過程沐祷。
  • 好友嚷闭、關注、粉絲赖临、感興趣的人集合:
    1)sinter命令可以獲得A和B兩個用戶的共同好友胞锰;
    2)sismember命令可以判斷A是否是B的好友;
    3)scard命令可以獲取好友數量兢榨;
    1. 關注時嗅榕,smove命令可以將B從A的粉絲集合轉移到A的好友集合
  • 首頁展示隨機:美團首頁有很多推薦商家,但是并不能全部展示吵聪,set類型適合存放所有需要展示的內容凌那,而srandmember命令則可以從中隨機獲取幾個。
  • 抽獎功能:用戶點擊抽獎按鈕吟逝,參數抽獎帽蝶,將用戶編號放入集合,然后抽獎块攒,分別抽一等獎励稳、二等獎,如果已經抽中一等獎的用戶不能參數抽二等獎則使用spop key [count]局蚀,反之使用srandmember key [count]麦锯。

4.3 Set 常用命令

sadd [key]  [value]  向指定key的set中添加元素
smembers [key]    獲取指定key 集合中的所有元素
sismember [key] [value]   判斷集合中是否存在某個value
scard [key]    獲取集合的長度
spop [key]   彈出一個元素
srem [key] [value]  刪除指定元素

5. Zset(有序集合)

5.1 簡單介紹

Zset也叫SortedSet一方面它是個set,保證了內部 value 的唯一性琅绅,另一方面它可以給每個 value 賦予一個score,代表這個value的排序權重鹅巍。它的內部實現(xiàn)由兩種數據結構支持:ziplist 和 skiplist千扶。

5.1.1 ziplist(壓縮列表)

當 Zset 使用 ziplist 作為存儲結構的時候,每個集合元素使用兩個緊挨在一起的壓縮列表節(jié)點來保存骆捧,第一個節(jié)點保存元素的成員澎羞,第二個元素保存元素的分值。

5.1.2 skiplist(跳躍表)

當 Zset 使用 skiplist 作為存儲結構時敛苇,使用 skiplist 按序保存元素分值妆绞,使用 dict 來保存元素和分值的對應關系顺呕。

跳表是一種基于有序鏈表的擴展,它會維護多個索引鏈表和原鏈表括饶。跳表不斷提升新的關鍵節(jié)點形成新的有序鏈表株茶,是一種空間換時間的思想。在插入元素到跳表時图焰,只需對比新元素的值和關鍵節(jié)點鏈表启盛,這樣就能將對比次數縮減。因為在跳表中技羔,使用空間換時間僵闯,所以會有多層的索引鏈表,每一層索引鏈表都是上一層的一半藤滥,當數據量較大的時候鳖粟,就能夠很輕松地降低鏈表查詢消耗的性能。至于提取關鍵節(jié)點的極限拙绊,是同一層只有兩個節(jié)點牺弹。

跳表插入節(jié)點的流程:

  1. 新節(jié)點和各層索引節(jié)點逐一比較,確定原鏈表的插入位置时呀,O(logn)张漂。
  2. 把索引插入到原鏈表,O(1)谨娜。
  3. 使用拋硬幣的隨機方式航攒,決定新節(jié)點是否提升為上一級索引,O(logn)趴梢。

總體上跳表查找和插入的時間復雜度是 O(logn)漠畜,空間復雜度是 O(n)。

跳表刪除節(jié)點的流程:

  1. 在索引層找到關鍵節(jié)點進行刪除坞靶,刪除每一層的相同節(jié)點憔狞,O(logn)。
  2. 如果某一層在刪除節(jié)點之后只剩下一個節(jié)點彰阴,那么刪除這層的鏈表瘾敢,O(logn)。

5.2 Zset 應用場景

5.2.1 排行榜

list不同的是Zset它能夠實現(xiàn)動態(tài)的排序尿这。比如用來存儲粉絲列表簇抵,在線討論社區(qū)項目的關注模塊用到了Zset,value 為粉絲的用戶 ID射众,score 為關注時間碟摆,這樣我們可以對粉絲列表按關注時間進行排序。

Zset還可以用來存儲學生的成績叨橱,value值是學生的 ID典蜕,score是他的考試成績断盛。 我們對成績按分數進行排序就可以得到他的名次。

5.2.2 延遲消息隊列

在一個下單系統(tǒng)中愉舔,下單后需要在 15 分鐘內進行支付钢猛,如果 15 分鐘未支付則自動取消訂單。將下單后的 15 分鐘后時間作為 score屑宠,訂單作為 value 存入 Redis厢洞,消費者輪詢去消費,如果消費的大于等于這筆記錄的 score典奉,則將這筆記錄移除隊列躺翻,取消訂單。

5.3 Zset 常用命令

zadd [key] [score] [value] 向指定key的集合中增加元素
zrange [key] [start_index] [end_index] 獲取下標范圍內的元素列表卫玖,按score 排序輸出
zrevrange [key] [start_index] [end_index]  獲取范圍內的元素列表 公你,按score排序 逆序輸出
zcard [key]  獲取集合列表的元素個數
zrank [key] [value]  獲取元素再集合中的排名
zrangebyscore [key] [score1] [score2]  輸出score范圍內的元素列表
zrem [key] [value]  刪除元素
zscore [key] [value] 獲取元素的score

6. HyperLogLog

  • 常用于計數,它采用一種基數算法假瞬,用于完成獨立總數的統(tǒng)計陕靠。
  • 占據空間小,無論統(tǒng)計多少個數據脱茉,只占12K的內存空間剪芥。
  • 是一種不精確的統(tǒng)計算法,標準誤差為0.81%琴许。
  • 比如:在線討論社區(qū)中統(tǒng)計獨立訪客UV税肪,使用當前日期作為 key,IP地址作為 value 存入 HyperLoglog榜田。如果要統(tǒng)計指定日期范圍內的 UV益兄,那么整理該日期范圍內的 key 到一個列表中,然后對這個列表求一個 union箭券,然后統(tǒng)計 HyperLoglog 的 size 即可净捅。

7. Geo

用于支持存儲地理位置信息。


參考:

  1. 一口氣說出Redis 5種數據結構及對應使用場景辩块,面試要加分的
  2. redis五大數據類型使用場景
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末蛔六,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子庆捺,更是在濱河造成了極大的恐慌古今,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,252評論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件滔以,死亡現(xiàn)場離奇詭異,居然都是意外死亡氓拼,警方通過查閱死者的電腦和手機你画,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,886評論 3 399
  • 文/潘曉璐 我一進店門抵碟,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人坏匪,你說我怎么就攤上這事拟逮。” “怎么了适滓?”我有些...
    開封第一講書人閱讀 168,814評論 0 361
  • 文/不壞的土叔 我叫張陵敦迄,是天一觀的道長。 經常有香客問我凭迹,道長罚屋,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,869評論 1 299
  • 正文 為了忘掉前任嗅绸,我火速辦了婚禮脾猛,結果婚禮上,老公的妹妹穿的比我還像新娘鱼鸠。我一直安慰自己猛拴,他們只是感情好,可當我...
    茶點故事閱讀 68,888評論 6 398
  • 文/花漫 我一把揭開白布蚀狰。 她就那樣靜靜地躺著愉昆,像睡著了一般。 火紅的嫁衣襯著肌膚如雪麻蹋。 梳的紋絲不亂的頭發(fā)上跛溉,一...
    開封第一講書人閱讀 52,475評論 1 312
  • 那天,我揣著相機與錄音哥蔚,去河邊找鬼倒谷。 笑死,一個胖子當著我的面吹牛糙箍,可吹牛的內容都是我干的渤愁。 我是一名探鬼主播,決...
    沈念sama閱讀 41,010評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼深夯,長吁一口氣:“原來是場噩夢啊……” “哼抖格!你這毒婦竟也來了?” 一聲冷哼從身側響起咕晋,我...
    開封第一講書人閱讀 39,924評論 0 277
  • 序言:老撾萬榮一對情侶失蹤雹拄,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后掌呜,有當地人在樹林里發(fā)現(xiàn)了一具尸體滓玖,經...
    沈念sama閱讀 46,469評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 38,552評論 3 342
  • 正文 我和宋清朗相戀三年质蕉,在試婚紗的時候發(fā)現(xiàn)自己被綠了势篡。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片翩肌。...
    茶點故事閱讀 40,680評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖禁悠,靈堂內的尸體忽然破棺而出念祭,到底是詐尸還是另有隱情,我是刑警寧澤碍侦,帶...
    沈念sama閱讀 36,362評論 5 351
  • 正文 年R本政府宣布粱坤,位于F島的核電站,受9級特大地震影響瓷产,放射性物質發(fā)生泄漏站玄。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 42,037評論 3 335
  • 文/蒙蒙 一拦英、第九天 我趴在偏房一處隱蔽的房頂上張望蜒什。 院中可真熱鬧,春花似錦疤估、人聲如沸灾常。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,519評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽钞瀑。三九已至,卻和暖如春慷荔,著一層夾襖步出監(jiān)牢的瞬間雕什,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,621評論 1 274
  • 我被黑心中介騙來泰國打工显晶, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留贷岸,地道東北人。 一個月前我還...
    沈念sama閱讀 49,099評論 3 378
  • 正文 我出身青樓磷雇,卻偏偏與公主長得像偿警,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子唯笙,可洞房花燭夜當晚...
    茶點故事閱讀 45,691評論 2 361