Redis的各種數據類型到底能玩出什么花兒?


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的自增來實現

  

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的關系就變成了下圖這樣

 

 

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如何實現

 

 

 Hash結構優缺點

優點

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

相比String操作,對內存與cpu的消耗更小

相比String儲存更節省空間

缺點

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

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

 

List結構

List常用操作


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

 

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、微信抽獎小程序

想必大家都用過微信里的抽獎小程序吧,如下圖,我們可以點擊立即參與進行抽獎,還可以查看所有參與人員,最后就是開獎的功能,一共三個關鍵點

 

 我們看一下這三個關鍵點用set數據類型怎么實現:

1)點擊參與抽獎,則將用戶ID加入集合

SADD key {userlD}

 

2)查看參與抽獎所有用戶

SMEMBERS key

 

3)抽取count名中獎者

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

 

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

 

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

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

 

  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實現~)

在上架商品時維護商品,添加商品的同時把對應的商品添加到對應的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集合,通過傳入的分值進行排序

  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咋實現。

 

  

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技術分享,希望在技術的道路上與你並肩同行!
————————————————


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM