Redis的各種資料型別到底能玩出什麼花兒?

為何不是夢發表於2020-07-26

https://mp.weixin.qq.com/s/ZSQ9vCkWXYuLrKS0UJ4RIg

兩個星期終於肝了出來,Redis相關問題腦圖,終於整理完了!!!

文末無套路分享~~附獲取方式

 

 


 

Redis作為一款NoSQL記憶體資料庫,其豐富的資料型別、簡單易用的命令、單機可達10萬的高併發(官方資料),從面世以來就深受廣大使用者的喜愛。Redis的五種資料型別,是我們學習Redis時的必修課,但是大多數人都只是去學它的命令、API,卻不知道這些資料型別都能應用在哪些場景,那這些命令學起來也就會很快就忘,終究只是“紙上談兵”。

用好這五種資料型別將給你的開發帶來很大的便利,給你的程式帶來很大的效能提升,同時這五種資料型別能玩出很多花樣。

不過大多數同學,在實際的開發過程中,大多隻用到了Redis五種資料型別中的1-3種,甚至有的只用過一種String型別。要麼是業務場景簡單用string足矣,要麼就是根本不知道或想不到用別的資料型別更合適,那麼即使是有些場景更適合用別的資料型別,可能自己也發覺不到。所以今天就來聊聊Redis的各種資料型別以及各自適用於什麼場景。

其實這個問題也是面試中問到Redis時的一個“開篇詞”,問這個問題主要有兩個原因:

第一,看看你到底有沒有全面的瞭解Redis有哪些功能,一般怎麼來用,什麼場景用什麼資料型別,就怕你只會最簡單的kv操作

第二,看看你在實際專案裡都怎麼玩兒過Redis,經驗是否豐富

要是你回答的不好,沒說出幾種資料型別,也沒說什麼場景,你完了,面試官對你印象肯定不好,覺得你平時就是做個簡單的set和get,沒思考過。

廢話不多說,進入正題吧。Redis一共提供了5種資料型別,分別是String,Hash,List,Set,sorted set(Zset),下面就從各個資料型別的基本常用命令和使用場景分別說說吧。

String字串

String字串結構的常用命令

#字串常用操作
SET key value //存入字串鍵值對
MSET key value [key value ...] //批量儲存字串鍵值對
SETNX key value //存入一個不存在的字串鍵值對
GET key //獲取一個字串鍵值
MGET key [key ...] //批量獲取字串鍵值
DEL key [key ...] //刪除一個鍵
EXPIRE key seconds //設定一個鍵的過期時間(秒)
#原子加減
INCR key //將key中儲存的數字值加1
DECR key //將key中儲存的數字值減1
INCRBY key increment //將key所儲存的值加上increment
DECRBY key decrement //將key所儲存的值減去decrement

 這裡列出了一些String常用命令,我們看一下這些String型別的這些命令可以應用到哪些場景。

 

應用場景

1、單值快取

即最簡單的key-value的set和get,比如快取個標識,開關等

SET key value
GET key

 

2、物件快取

除了單值快取我們還可以用String型別快取物件,如下兩種方式:

#1
SET user:1 value(json串)
GET user:1
#2
MSET user:1:name 程式設計大道 user:1:sex 1
MGET user:1:name user:1:sex

 第一種直接將物件轉換成json串作為value儲存到redis,這種獲取物件就比較簡單了,直接get key拿到value轉成物件即可,但有個缺點就是如果你要是修改物件的某一個欄位,也得把整個物件的json串拿出來反序列化成物件,這將帶來不必要的網路開銷(即便是redis存在記憶體中,但實際我們的應用伺服器和redis是隔離的,網路傳輸的開銷也不容小覷),同樣,頻繁的序列化反序列化也將會帶來不小的效能開銷,如果對於效能要求比較高的系統來說這將是一個災難。

而第二種儲存物件的方式則對於這種頻繁修改物件某一個欄位的場景就比較友好了,每個欄位與值都是一個kv對,修改直接set k v覆蓋就好了,但是儲存多個欄位時就沒那麼容易了,好在有mset批量操作的命令,網路開銷由多次變為1次。

 

3、分散式鎖

如下setnx命令是set if not exit的縮寫,意思就是這個key不存在時才執行set。多個執行緒執行這條命令時只有一個執行緒會執行成功,則視為拿到鎖。然後拿到鎖的執行緒執行業務操作,執行完畢刪除這個鎖,釋放鎖。

#setnx key value
SETNX product:10001 true //返回1代表獲取鎖成功
SETNX product:10001 true //返回0代表獲取鎖失敗
//執行業務操作
DEL product:10001 //執行完業務釋放鎖

 上述方式存在問題:程式意外終止可能會導致鎖沒辦法釋放,造成死鎖。可以使用如下命令,既設定分散式鎖又設定了key的過期時間

SET product:10001 true ex 10 nx //防止程式意外終止導致死鎖

 分Redis布式鎖的詳細實現可以參考我之前寫的Redis分散式鎖實戰

 

4、計數器

INCR article:readcount:{文章id}
GET article:readcount:{文章id}

 

基於Redis原子自增命令incr可以實現諸如計數器的功能,我們都知道公眾號文章,微博,部落格都有一個閱讀量的概念,我們就可以用這個計數器來實現,而且效能很高。

例如下圖中的閱讀數就可以用redis的自增來實現

Redis的各種資料型別到底能玩出什麼花兒?

  

5、Web叢集session共享解決方案

系統叢集部署情況下首先要考慮的問題就是session共享問題,我們可以通過將原本儲存在記憶體中由tomcat管理的session轉移到由Redis來儲存,實現分散式session的功能。spring框架提供了session共享的解決方案,即spring session + redis實現分散式session。

 

6、分散式系統全域性序列號

分散式系統中要保證全域性序列號的唯一性,可以使用Redis來維護一個自增的序列。

通過如下命令從Redis獲取自增ID:

INCR orderId//INCR是一個原子自增命令

 分散式系統環境下通過Redis保證ID的自增性和唯一性,通過該命令獲取ID每次都要和Redis進行互動,如果業務量很大,那麼這將會很頻繁。

所以可以一次性獲取一定量的ID儲存在JVM記憶體中,用完了再從Redis獲取。這樣減少了頻繁的網路開銷,但是缺點是可能會丟失(浪費)一部分ID,因為獲取後服務可能掛了還沒用完的ID可能就浪費了(當然你可以使用一些手段去保證不浪費,但沒必要,浪費一點也是無所謂的)。

如下,每次獲取1000個

INCRBY orderId 1000//redis批量生成序列號提升效能

  

HASH結構

Hash常用操作

HSET key field value//儲存一個雜湊表key的鍵值
HSETNX key field value//儲存一個不存在的雜湊表key的鍵值
HMSET key field value [field value ...] //在一個雜湊表key中儲存多個鍵值對
HGET key field//獲取雜湊表key對應的field鍵值
HMGET key field [field ...]//批量獲取雜湊表key中多個field鍵值
HDEL key field [field ...]//刪除雜湊表key中的field鍵值
HLEN key//返回雜湊表key中field的數量
HGETALL key//返回雜湊表key中所有的鍵值
HINCRBY key field increment//為雜湊表key中field鍵的值加上增量increment

  

應用場景


1、物件快取

結合HASH結構的key-field-value的特性,類似於Java中的HashMap,內部也是“key-value”的形式,field剛好可以存物件的屬性名,假設有如下資料,

 

我們可以用HMSET命令批量設定field-value,前面拼接使用者的ID保證存多個使用者的資料不會重複;HMGET批量獲取field;MSET修改某一個field。

HMSET achievement {userId}:name 小明 {userId}:score 89
HMSET achievement 1:name 小明 1:score 89
HMSET achievement 2:name 小華 2:score 92
HMGET achievement 1:name 1:score

 物件與HSAH的關係就變成了下圖這樣

 Redis的各種資料型別到底能玩出什麼花兒?

 

2、電商購物車

以使用者id為key,商品id為field,商品數量為value可以實現購物車的常規操作。

購物車操作:

#新增商品
hset cart:10001 50005 1
#給某一個商品增加數量
hincrby cart:10001 50005 1
#購物車中商品總個數
hlen cart:10001
#刪除商品
hdel cart:10001 50005
#獲取購物車所有商品
hgetall cart:10001

 

​​​​​​對應購物車的幾個常用操作可以想象使用Redis如何實現

 Redis的各種資料型別到底能玩出什麼花兒?

 

 Hash結構優缺點

優點

將同類資料歸類整合儲存(同一個key),方便資料管理

相比String操作,對記憶體與cpu的消耗更小

相比String儲存更節省空間

缺點

過期功能不能使用在field上,只能用在key上

Redis叢集架構下不適合大規模使用

 

List結構

List常用操作


我們可以認為列表的左邊叫頭,右邊叫尾

 Redis的各種資料型別到底能玩出什麼花兒?

List結構的操作示意圖


常用命令

LPUSH key value [value ...] //將一個或多個值value插入到key列表的表頭(最左邊)
RPUSH key value [value ...]//將一個或多個值value插入到key列表的表尾(最右邊)
LPOP key//移除並返回key列表的頭元素
RPOP key//移除並返回key列表的尾元素
LRANGE key start stop//返回列表key中指定區間內的元素,區間以偏移量start和stop指定

BLPOP key [key ...] timeout//從key列表表頭彈出一個元素,若列表中沒有元素,阻塞等待timeout秒,如果timeout=0,一直阻塞等待
BRPOP key [key ...] timeout //從key列表表尾彈出一個元素,若列表中沒有元素,阻塞等待timeout秒,如果timeout=0,一直阻塞等待
LLEN key //list的長度

 

應用場景


1、實現常見的資料結構

基於List的特性及豐富的命令可以實現常用的集中資料結構:

1)Stack(棧) = LPUSH + LPOP ,FILO先入後出

結合LPUSH和LPOP命令實現棧的先進後出的特性,LPUSH從左邊入棧,LPOP從左邊出棧,先進入的後出來。相當於入口出口是一個。

 

2)Queue(佇列)= LPUSH + RPOP,FIFO先進先出

結合LPUSH和RPOP命令實現佇列的先進先出的特性,LPUSH從左邊入隊,RPOP從右邊出隊,先進來的先出來。相當於入口出口各在兩邊。

 

3)Blocking MQ(阻塞佇列)= LPUSH + BRPOP

結合LPUSH和BRPOP實現阻塞佇列,BRPOP比RPOP多了一個timeout的引數,是一個等待的最大時間,如果在這個時間內拿不到資料則返回空。

 

2、微博訊息和微信公號訊息

例如,walking本人關注了人民網、華為中國、京港地鐵等大V,假設人民網發了一條微博,ID為30033,我關注了他,那麼就會往我的msg這個佇列裡push這個微博ID,我在開啟我的微博時,就會從這個我專屬的msg佇列裡取前幾個微博ID展示給我看,所以這個就牽涉到了幾個關鍵點:

1)人民網發了一條微博,ID為30033,訊息ID入隊

LPUSH msg:{walking-ID} 30033

 

2)華為中國發微博,ID為30055,訊息入隊

LPUSH msg:{walking-ID} 30055

 

 3)我登入進去,會給我展示最新微博訊息,那麼就從我的訊息佇列裡取最新的前5條顯示在首頁

LRANGE msg:{walking-ID} 0 5

 

 

 SET結構

Set常用操作

SADD key member [member ...]//往集合key中存入元素,元素存在則忽略,若key不存在則新建
SREM key member [member ...]//從集合key中刪除元素
SMEMBERS key //獲取集合key中所有元素
SCARD key//獲取集合key的元素個數
SISMEMBER key member//判斷member元素是否存在於集合key中
SRANDMEMBER key [count]//從集合key中選出count個元素,元素不從key中刪除
SPOP key [count]//從集合key中選出count個元素,元素從key中刪除
#set運算操作
SINTER key [key ...] //交集運算
SINTERSTORE destination key [key ..]//將交集結果存入新集合destination中
SUNION key [key ..] //並集運算
SUNIONSTORE destination key [key ...]//將並集結果存入新集合destination中
SDIFF key [key ...] //差集運算
SDIFFSTORE destination key [key ...]//將差集結果存入新集合destination中

 應用場景


1、微信抽獎小程式

想必大家都用過微信裡的抽獎小程式吧,如下圖,我們可以點選立即參與進行抽獎,還可以檢視所有參與人員,最後就是開獎的功能,一共三個關鍵點

 Redis的各種資料型別到底能玩出什麼花兒?

 我們看一下這三個關鍵點用set資料型別怎麼實現:

1)點選參與抽獎,則將使用者ID加入集合

SADD key {userlD}

 

2)檢視參與抽獎所有使用者

SMEMBERS key

 

3)抽取count名中獎者

SRANDMEMBER key [count]//返回但不從set中剔除
SPOP key [count]//返回並剔除

 

如果設定了一等獎二等獎三等獎...,並且每人只能得一種,則可以用SPOP key count

 

2、微信微博點贊,收藏,標籤

比如walking發了一條朋友圈,有人點贊

 Redis的各種資料型別到底能玩出什麼花兒?

  1) 點贊 點贊就把點贊這個人的ID加到這個點讚的集合中

SADD like:{訊息ID} {使用者ID}

 

2) 取消點贊 從集合中移除使用者ID

SREM like:{訊息ID} {使用者ID}

 

3) 檢查使用者是否點過贊

SISMEMBER like:{訊息ID} {使用者ID}

 

4) 獲取點讚的使用者列表

SMEMBERS like:{訊息ID}

 

 5) 獲取點贊使用者數

SCARD like:{訊息ID}

 

Set集合運算操作的應用場景

基於Redisset集合提供的豐富的命令,我們可以對集合輕鬆的實現交併差的運算。例如,現有集合set1,set12,set3,元素如下:

set1:{a,b,c}
set2:{a,c,e}
set3:{c,d,f}

 對集合進行交、並、差的運算​​​​​​​

SINTER set1 set2 set3 //交集--> { c } 
SUNION set1 set2 set3 //並集--> { a,b,c,d,e,f } 
SDIFF set1 set2 set3 //差集--> { b }

 通過這些基本操作我們看可以實現什麼樣的業務需求。

 

3、集合操作實現社交軟體關注模型

社交軟體的使用者關注模型,如QQ的好友,微博的關注,抖音、快手的關注、微信的公眾號關注,這些社交軟體都會做一個這樣的功能,那就是使用者關係的關注模型推薦,包括共同關注的人、可能認識的人、

首先看一下walking、chenmowanger、Hollis關注的人,如下:

1)walking關注的人:

walkingSet-->{chenmowanger, ImportNew, Hollis}

 

2) chenmowanger關注的人:

chenmowangerSet-->{walking, ImportNew, Hollis, JavaGuide}

 

3) Hollis關注的人:

HollisSet--> {waking, ImportNew, JavaGuide, feichao, CodeSheep}

 (開玩笑,大佬們才沒關注我,哈哈?)

每個人的關注列表都是一個Redis的set集合,然後當walking點到chenmowanger的主頁,就會有個區域專門展示我和二哥的一些關注情況:

 

4) walking和chenmowanger共同關注:

也就是看哪些人在我的集合裡也在二哥的集合裡​​​​​​​

//兩個集合求並集
SINTER walkingSet zhangyixingSet--> {ImportNew, Hollis}

 

5) 我關注的人也關注他(chenmowanger):

看我關注的人的關注列表裡是不是有某個人,比如我進入chenmowanger的主頁,可以展示我關注的人裡還有誰也關注了chenmowanger​​​​​​​

SISMEMBER ImportNewSet chenmowanger
SISMEMBER HollisSet chenmowanger

 

6) 我可能認識的人:

求差集,以前面這個集合為準,看二哥關注的那些人有哪些我還沒關注,於是我就趕緊關注了JavaGuide(Guide哥)

SDIFF chenmowangerSet walkingSet->{walking, JavaGuide}

 

4、集合操作實現電商商品篩選

先看一下這個圖是不是很熟悉,選購手機時,有一個篩選的功能

 Redis的各種資料型別到底能玩出什麼花兒?

 如上圖,電商網站買手機,進到這個頁面根據各種條件搜手機,我們想一想用Redis如何實現呢?(當然了,這裡並不是說人家就完全用Redis實現這一套搜尋,其實主要還是用搜尋引擎那些中介軟體,這裡只是說明可以用Redis實現~)

在上架商品時維護商品,新增商品的同時把對應的商品新增到對應的set集合裡即可,如下舉例

//品牌-華為
SADD brand:huawei P30 Mate30 榮耀Play4 nova7
//品牌-小米
SADD brand:xiaomi mi6 mi8 mi9 mi10
//品牌-iPhone
SADD brand:iPhone iphone8 iphone8plus iphoneX iphone11
//作業系統-Android
SADD os:android P30 Mate30 榮耀Play4 nova7 mi6 mi8 mi9 mi10
//CPU品牌-驍龍
SADD cpu:brand:xiaolong iphone8 iphone8plus iphoneX iphone11 mi6 mi8 mi9 mi10
//CPU品牌-麒麟
SADD cpu:brand:qilin P30 Mate30 榮耀Play4 nova7
//執行記憶體-8G
SADD ram:8G P30 Mate30 榮耀Play4 nova7 mi6 mi8 mi9 mi10 iphone8 iphone8plus iphoneX iphone11
//多條件查詢 作業系統Android,CPU品牌驍龍,執行記憶體8G
SINTER os:android cpu:brand:xiaolong ram:8G -->{mi6 mi8 mi9 mi10}

 截圖更容易看:

 

 假設我們維護了各種品牌,手機所屬的作業系統,CPU品牌,執行記憶體等,那麼我們在勾選條件查詢時就可以用勾選的各個集合求他的交集就行了。

 

ZSet有序集合


zset是有序的set集合,通過傳入的分值進行排序

Redis的各種資料型別到底能玩出什麼花兒?

  ZSet常用操作

ZADD key score member [[score member]…]//往有序集合key中加入帶分值元素
ZREM key member [member …] //從有序集合key中刪除元素
ZSCORE key member //返回有序集合key中元素member的分值
ZINCRBY key increment member//為有序集合key中元素member的分值加上increment 
ZCARD key//返回有序集合key中元素個數
ZRANGE key start stop [WITHSCORES]//正序獲取有序集合key從start下標到stop下標的元素
ZREVRANGE key start stop [WITHSCORES]//倒序獲取有序集合key從start下標到stop下標的元素
 

#Zset集合操作
ZUNIONSTORE destkey numkeys key [key ...] //並集計算 
ZINTERSTORE destkey numkeys key [key …]//交集計算

 應用場景


1、Zset集合操作實現排行榜

我們都知道微博熱點,新聞熱榜,投票排行榜等都有一個排名的概念,如下圖百度熱榜,展示的是實時的點選量比較高的新聞(假設這些新聞的ID為1001-1010),每個新聞都有一個熱點值,一般按點選量,1001這個新聞熱點是484W,1002這個是467W,實時的,可能等會再看就不一樣了,那麼我們看下用Redis咋實現。

 Redis的各種資料型別到底能玩出什麼花兒?

  

1)點選新聞

每次有人點選這個新聞,那麼久ius給他的分值加1

ZINCRBY hotNews:20200722 1 1001 //新聞ID為1001的新聞分值加一

 

2)展示當日排行前十

取集合中的前10個元素

ZREVRANGE hotNews:20200722 0 10 WITHSCORES

 

3)七日熱點榜單計算

ZUNIONSTORE hotNews:20200715-20200721 7 hotNews:20200715 hotNews:20200716... hotNews:20200721

 

4)展示七日排行前十

ZREVRANGE hotNews:20190813-20190819 0 10 WITHSCORES

  

更多應用場景

  • 微信<搖一搖><搶紅包>
  • 滴滴叫車、摩拜單車<附近的車>
  • 美團和餓了麼<附近的餐館>
  • 搜尋自動補全
  • 布隆過濾器

​​​​​​​結語

本篇文章主要講了Redis五中資料型別可以使用對應的命令實現的業務場景,通過我們生活中常見的業務場景來幫助理解Redis的各種資料型別的用法,結合場景也能方便我們更加形象的去理解和學習Redis資料型別的各種操作命令。

同時,walking也希望通過本文的引導能夠激發讀者朋友對Redis資料型別應用場景的靈感,能夠做做需求的時候系統設計的時候想一想是不是用redis實現更好一點呢,也算是對各位的一個拋磚引玉吧。

再次宣告,本文中列舉的應用場景有些可能使用redis並不是最合適的,有的只是在說明可以用redis去實現,希望各位能夠依據自己的實際業務場景去斟酌具體怎麼設計。如有問題請幫忙指出哦。

最後希望本文的講解能夠對大家有所幫助,如果有幫助請給walking一點鼓勵吧,記得 點贊❤、收藏✨、轉發 ?哦~

 原文:https://mp.weixin.qq.com/s/ZSQ9vCkWXYuLrKS0UJ4RIg

別走開,有彩蛋 ↓ ↓ ↓

walking個人維護的這個公眾號已經有兩年了吧,起初是為了自己總結用的,後來有人關注了,我覺得通過我的文章和分享能夠幫助更多的人那就是很值得。感謝一直關注這個公眾號的朋友們❤~

雖然產量不高,我也一直在寫下去,也寫了很多文章,每篇文章都是經過幾天甚至是一個星期的打磨、推敲才敢發出來。但是一直是不瘟不火,即便是沒有人關注,沒有人點贊,我也是會堅持下去的,因為這裡是我的積累,是我留下的痕跡~

大家有什麼建議或意見歡迎留言或私信哦~下面是我整理了兩個星期的Redis知識點,做成思維導圖

方便大家複習,已經轉成各種格式,歡迎關注公眾號:程式設計大道,回覆Redis免費獲取

 

 

 

 

參考:

https://www.bilibili.com/video/BV1if4y1R7ns

 

程式設計大道,一個熱愛技術的程式設計師,愛分享、愛寫作。專注Java技術分享,希望在技術的道路上與你並肩同行!
————————————————

相關文章