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}
(開玩笑,大佬們才沒關注我,哈哈