天天看點

第二節:Redis資料類型、使用場景、常用指令剖析

一. String

1. 資料類型介紹 

  典型的Key-Value集合,不少人喜歡用來存實體,需要序列化成字元串,擷取的時候需要反序列化一下。(還是建議合理選擇redis的資料結構,而不是萬物都用string)

2. 應用場景

(1). 緩存

 string類型最簡單的一個應用就是Key-value緩存,value可以是簡單string、int,也可以是序列化後的實體,可以替代Session進行存儲,和其它緩存一樣,也可以設定緩存的過期時間

 (常用的鍵設計:表名_id  ,如: UserInfor_001 )

 經典例子:秒殺将庫存放到redis的string中存儲,進而應對高并發。

(2). 計數器(利用自增自減)

 A. 單個網站或單條消息的點選量(點贊量、通路量等等)

 B. 上面秒殺庫存的扣減(後面會介紹使用Lua腳本扣減庫存)

(3). 程序外共享Session 

  解決了IIS重新開機Session丢失的問題,解決了Session空間有限容易被擠爆的問題,但不能解決浏覽器重新開機找不到Session的問題!

  詳見:https://www.cnblogs.com/yaopengfei/p/9583168.html      https://www.cnblogs.com/yaopengfei/p/11270816.html

3. 相關指令 

通用指令

1. 檢視所有的鍵
#非常危險,容易造成用戶端卡頓,線上禁止适用
keys *  
2. 檢視鍵的總數
#dbsize指令在計算鍵總數的時候不是周遊所有鍵,而是直接擷取redis内置鍵的數量
dbsize
3.檢查鍵是否存在 (存在傳回1,不存在傳回0)
exists key
4. 删除鍵(無論什麼類型,都可以用這個指令删除)
del key
5. 檢視key的資料類型
type key
6. 設定key的過期時間(超過過期時間會自動删除)
expire key seconds
7. 檢視key剩餘過期時間( 大于0的時候,顯示的剩餘過期時間;1代表沒有設定過期時間;2代表key不存在)
ttl key 
8. 删除所有key
flushdb      

string相關

1. 存儲: set key value
2. 擷取: get key
3. 删除: del key
4. 自增1: incr key
   自增n: incrby key n
5. 自減1:decr key
   自減n:decr key n      

 PS:關于key和string類型的還有很多指令,詳細請檢視指令手冊:

第二節:Redis資料類型、使用場景、常用指令剖析
第二節:Redis資料類型、使用場景、常用指令剖析

 補充兩個指令:info 和 scan

(1).  info: 檢視redis服務的運作資訊,一堆内容,分為9部分

第二節:Redis資料類型、使用場景、常用指令剖析

(2). scan指令:前面說到,查詢redis中的key不要用 keys *指令,那麼可以用 scan指令替代。

第二節:Redis資料類型、使用場景、常用指令剖析
第二節:Redis資料類型、使用場景、常用指令剖析

參考文章:https://www.cnblogs.com/yaopengfei/p/11912930.html 

二. Hash

  一個key,對應一個Key-Value集合,即:hashid → {key:value;key:value;key:value;},相當于value又是一個“鍵值對集合” 或者值是另外一個 Dictionary。

(1). 購物車

 以使用者id作為hashid,商品id作為key,商品數量作為value,利用自增和自減功能來實作增加商品數量和減少商品數量功能。也可以删除商品,擷取商品總數,擷取購物車中所有商品。

  

第二節:Redis資料類型、使用場景、常用指令剖析

(2). 群聊 

 臨時存儲消息(根據實際場景考慮是否合适),比如:群名為hashid, 使用者id當做key,内容作為value。 這樣存儲可以,但是取資料的時候必須一下全部取出來,不能根據時間取前n條。

(3). 對象緩存

 可以把一個對象中的每個字段當做一個key,存放到hash中。

第二節:Redis資料類型、使用場景、常用指令剖析
1. 存儲: hset key field value
         127.0.0.1:6379> hset myhash username lisi
         (integer) 1
         127.0.0.1:6379> hset myhash password 123
         (integer) 1
2. 擷取: hget key field: 擷取指定的field對應的值
         127.0.0.1:6379> hget myhash username
         "lisi"
         hgetall key:擷取所有的field和value
                127.0.0.1:6379> hgetall myhash
                1) "username"
                2) "lisi"
                3) "password"
                4) "123"              
3. 删除: hdel key field
         127.0.0.1:6379> hdel myhash username
         (integer) 1      

 其它指令詳見指令文檔:

第二節:Redis資料類型、使用場景、常用指令剖析

4. 優缺點

優點:

 (1). 同類資料歸類整合存儲,友善資料管理

 (2). 相比string操作消耗記憶體與cpu更小

 (3). 相比string存儲更節省空間

缺點:

 (1).過期功能不能使用在内層的field上,隻能用在最外層的key上

 (2).Redis叢集架構下不适合大規模使用

參考文章:https://www.cnblogs.com/yaopengfei/p/11912930.html

三. List

 它是一個雙向連結清單,支援左進、左出、右進、右出,是以它即可以充當隊列使用,也可以充當棧使用。

(1). 隊列:先進先出, 可以利用List左進右出,或者右進左出

(2). 棧:先進後出,可以利用List左進左出,或者右進右出

(3). 阻塞隊列:資料的生産者可以通過LPUSH指令從左邊插入資料,多個資料消費者,可以使用BRPOP指令阻塞的“搶”清單尾部的資料

第二節:Redis資料類型、使用場景、常用指令剖析

(1). 消息隊列

 A. 流量削峰:秒殺服務,使用者下單請求加到消息隊列中,然後開啟另外一個線程從隊列中讀取進行業務處理(拉模式) 。

 B. 應用解耦(異步):比如登入成功後,要增加積分或者發送郵件,可以引入消息隊列進行解耦,異步處理增加積分 或者 發送郵件的請求。

(2). 高性能分頁(棧的特性)

 解決查詢緩慢的問題

 比如發帖網站,會有非常多的文章,而且數量每日俱增,首頁顯示的是最新釋出的10條文章,顯示的是:發帖人名稱 和 發帖标題,如果從資料庫中查詢可能會非常慢,這時候可以把發帖人名稱和發帖标題(包括文章id),存到Redis隊列中,這個時候利用 棧 的特性,LRANGE:擷取key指定索引的值(從左往右算), 擷取前10條資料,還支援按照範圍内擷取元素,可以完美解決分頁查詢功能。大大提高查詢效率。

 檢視詳情的時候,再根據文章的id到資料庫中查。

(3). 模拟釋出訂閱模式

 要求:以微網誌為例(或者微信的訂閱号),部落客A,部落客B都關注了部落客C、部落客D,在部落客A(或B)的版面,應該顯示的是部落客C和部落客D釋出的最新博文(最新釋出的在最上面),換句話說部落客C或者部落客D每發一篇博文,都要推送給關注他們的部落客A和部落客B。

技術分析

 ①. 資料結構的設計:一個部落客對應一個List連結清單,用來存儲該部落客應該顯示的博文消息。 以部落客的使用者id作為key,博文消息的id作為value。

 ②. 部落客C每發一條博文,就需要向關注他的粉絲(A和B)對應的連結清單中分别存儲一下 該博文消息的id。

 ③. 部落客D每發一條博文,就需要向關注他的粉絲(A和B)對應的連結清單中分别存儲一下 該博文消息的id。

 ④.  部落客A就可以到自己對應的連結清單中利用棧的特性,擷取最新的n條博文消息id。

PS: 拿到博文消息id了,剩下的就容易了,根據id去關系型資料中查内容就很快了,或者也可以将标題或者内容的前100字也存儲到Redis中,便于頁面顯示(這樣value的格式就是:博文id-博文标題-博文内容前100字)。

1. 添加:
 (1). lpush key value: 将元素加入清單左表           
 (2). rpush key value:将元素加入清單右邊    
     127.0.0.1:6379> lpush myList a
     (integer) 1
     127.0.0.1:6379> lpush myList b
     (integer) 2
     127.0.0.1:6379> rpush myList c
     (integer) 3
2. 擷取:
     lrange key start end :範圍擷取
     127.0.0.1:6379> lrange myList 0 -1        (-1代表清單最後一個元素)
     1) "b"
     2) "a"
     3) "c"
3. 删除:
     lpop key: 删除清單最左邊的元素,并将元素傳回
     rpop key: 删除清單最右邊的元素,并将元素傳回
4. 阻塞擷取
   blpop key timeout:從key清單左側彈出一個元素,并删除。如果沒有元素,阻塞等待timeout秒,如果timeout=0,一直阻塞等待
   brpop key timeout:從key清單右側彈出一個元素,并删除。如果沒有元素,阻塞等待timeout秒,如果timeout=0,一直阻塞等待      
第二節:Redis資料類型、使用場景、常用指令剖析

參考文章:https://www.cnblogs.com/yaopengfei/p/11940420.html

四. Set

 1個key→多個value,value的值不重複!  (無序集合,自動去重)

 Set一種無序且元素内容不重複的集合,不用做重複性判斷了,和我們數學中的集合概念相同,可以對多個集合求交集、并集、差集,key可以了解成集合的名字。

注:set 用哈希表來保持字元串的唯一性,沒有先後順序,是按照自己的一個存儲方式來周遊,因為沒有儲存插入順序。

(一).抽獎

(1) 背景:使用者參與抽獎,抽獎大緻分兩類:

 A:隻抽1次,1次抽n個人。

 B:抽多次,比如三等獎抽3名,二等獎抽2名,一等獎抽1名。

(2)  技術分析:

 主要利用Set結構元素的不重複性和擷取随機數的方法來實作,以“specialPrize”為key,參與使用者的id當做value

 A:使用者點選參與抽獎則執行SADD方法。

 B:可以擷取所有參與使用者SMEMBERS 和 判斷某個使用者是否參與抽獎了sismember

 C:随機抽一次獎用srandmember, 不删除

     如果需要删除抽獎用spop

(3) 代碼分析(基于stackexchange.redis)

第二節:Redis資料類型、使用場景、常用指令剖析
第二節:Redis資料類型、使用場景、常用指令剖析
1           //1.模拟使用者參與抽獎
 2             for (int i = 100; i <= 115; i++)
 3             {
 4                 db.SetAdd("specialPrize", i.ToString());
 5             }
 6             //2. 擷取所有的參與抽獎的使用者
 7             var data = db.SetMembers("specialPrize").Select(u => (string)u).ToList();
 8             string idList = "";
 9             foreach (var item in data)
10             {
11                 idList = idList + "," + item;
12             }
13             Console.WriteLine($"參與抽獎的使用者有:{idList}");
14             //3. 判斷使用者103是否參與抽獎了
15             var data2 = db.SetContains("specialPrize", "103");
16             if (data2 == true)
17             {
18                 Console.WriteLine($"使用者103參與了抽獎");
19             }
20             else
21             {
22                 Console.WriteLine($"使用者103沒有參與抽獎");
23             }
24             //4. 抽獎
25             //4.1 隻抽一次獎,抽獎人數為兩名
26             {
27                 var d1 = db.SetRandomMembers("specialPrize", 2).Select(u => (string)u).ToList();
28                 foreach (var item in d1)
29                 {
30                     Console.WriteLine($"獲獎使用者為:{item}");
31                 }
32             }
33             //4.2 抽三次獎
34             {
35                 var d1 = db.SetPop("specialPrize", 3).Select(u => (string)u).ToList();
36                 foreach (var item in d1)
37                 {
38                     Console.WriteLine($"三等獎使用者為:{item}");
39                 }
40                 var d2 = db.SetPop("specialPrize", 2).Select(u => (string)u).ToList();
41                 foreach (var item in d2)
42                 {
43                     Console.WriteLine($"二等獎使用者為:{item}");
44                 }
45                 var d3 = db.SetPop("specialPrize", 1).Select(u => (string)u).ToList();
46                 foreach (var item in d3)
47                 {
48                     Console.WriteLine($"一等獎使用者為:{item}");
49                 }
50             }      

View Code

(二). 微信或微網誌中消息的點贊(或者某篇文章的收藏)

(1). 背景

 微信朋友圈使用者A的某條消息的點贊功能,要實作點贊、取消點贊、擷取點贊清單、擷取點贊使用者數量、判斷某使用者是否點贊過。

(2). 技術分析

 利用Set結構, 以使用者Id-消息id作為key,點贊過該消息的使用者id作為value。

 A:點贊 SetAdd方法

 B:取消點贊 SetRemove方法

 C:擷取點贊清單 SetMembers方法

 D:擷取點贊使用者數量 SetLength方法

 E:判斷某使用者是否點贊過 SetContains方法

對應redis指令:

第二節:Redis資料類型、使用場景、常用指令剖析

(三). 關注模型

(1).背景

 比如微網誌關注或者共同好友的問題,以微網誌關注為例,要實作:共同關注、關注的和、關注A的使用者中也關注B的、當A進入B頁面,求可能認識的人。

 利用Set結構,一個部落客對應一個Set結構,部落客的id作為key,關注該部落客的使用者id作為value。

 A:關注和取消關注: SetAdd方法 和 SetRemove方法

 B:共同關注:求交集

 C:關注的和:求并集

 D:關注A的使用者中也關注B的:周遊A中的使用者,利用SetContains判斷是否B中也存在

 E:當A進入B頁面,求可能認識的人:這裡指的是關注B中的使用者 扣去 裡面也關注A的使用者,就是A可能認識的人。求差集:B-A

對應redis指令:

第二節:Redis資料類型、使用場景、常用指令剖析

(3). 代碼分享

第二節:Redis資料類型、使用場景、常用指令剖析
第二節:Redis資料類型、使用場景、常用指令剖析
1             //關注lmr的使用者有:
 2             db.SetAdd("lmr", "小1");
 3             db.SetAdd("lmr", "小2");
 4             db.SetAdd("lmr", "小3");
 5             db.SetAdd("lmr", "小4");
 6             db.SetAdd("lmr", "小5");
 7             db.SetAdd("lmr", "rbp");
 8 
 9             //關注ypf的使用者有:
10             db.SetAdd("ypf", "小4");
11             db.SetAdd("ypf", "小5");
12             db.SetAdd("ypf", "小6");
13             db.SetAdd("ypf", "小7");
14             db.SetAdd("ypf", "小8");
15             db.SetAdd("ypf", "rbp");
16 
17             //同時關注lmr和ypf的使用者有:
18             string[] arry1 = { "lmr", "ypf" };
19             RedisKey[] keyList1 = arry1.Select(u => (RedisKey)u).ToArray();
20             var d1 = db.SetCombine(SetOperation.Intersect, keyList1).Select(u => (string)u).ToList();  //交集
21             foreach (var item in d1)
22             {
23                 Console.WriteLine("同時關注lmr和ypf的使用者有:" + item);
24             }
25 
26             //關注lmr和ypf的使用者有(需要去重):
27             string[] arry2 = { "lmr", "ypf" };
28             RedisKey[] keyList2 = arry2.Select(u => (RedisKey)u).ToArray();
29             var d2 = db.SetCombine(SetOperation.Union, keyList2).Select(u => (string)u).ToList();  //并集
30             foreach (var item in d2)
31             {
32                 Console.WriteLine("關注lmr和ypf的使用者有:" + item);
33             }
34 
35             //關注lmr的人中也關注ypf的有:
36             var d3 = db.SetMembers("lmr").Select(u => (string)u).ToList();
37             foreach (var item in d3)
38             {
39                 var isExist = db.SetContains("ypf", item);
40                 if (isExist)
41                 {
42                     Console.WriteLine("關注lmr的人中也關注ypf的有:" + item);
43                 }
44             }
45 
46             //當ypf進入lmr的頁面,顯示可能認識的人(應該顯示:小1,小2,小3)
47             string[] arry4 = { "lmr", "ypf" };  // lmr-ypf
48             RedisKey[] keyList4 = arry4.Select(u => (RedisKey)u).ToArray();
49             var d4 = db.SetCombine(SetOperation.Difference, keyList4).Select(u => (string)u).ToList();  //差集 lmr-ypf
50             foreach (var item in d4)
51             {
52                 Console.WriteLine("當ypf進入lmr的頁面,顯示可能認識的人:" + item);
53             }       

(四). 利用唯一性,可以統計通路網站的所有IP

 同上

(五). 集合操作實作電商網站的篩選

第二節:Redis資料類型、使用場景、常用指令剖析

3. 相關指令

1. 存儲:sadd key value
        127.0.0.1:6379> sadd myset a
       (integer) 1
        127.0.0.1:6379> sadd myset a
       (integer) 0
2. 擷取:smembers key:擷取set集合中所有元素
        127.0.0.1:6379> smembers myset
        1) "a"
3. 删除:srem key value:删除set集合中的某個元素    
        127.0.0.1:6379> srem myset a
        (integer) 1      

其它指令詳見指令文檔:

第二節:Redis資料類型、使用場景、常用指令剖析

參考文章:https://www.cnblogs.com/yaopengfei/p/11936144.html 

五. SortedSet

  将Set中的元素增加了一個權重參數 score,使得集合中的元素能夠按 score 進行有序排列(有序集合,自動去重), 三個字段:key-member-score, key代表鍵,member代表值,score代表權重或者打分值

(1). 熱搜

 A. 需求:統計某個網站熱詞搜尋,并實時顯示前5名的詞及其搜尋次數。

 PS:實時顯示無非就是每隔幾秒查詢一次,大部分情況我們不建議直接去Redis裡查排名,可以把前五名的相關資料存儲到Redis的String結構中,設定5分鐘過期。

 B. 技術分析: 利用Redis中SortedSet(key-member-score)這一資料結構, 利用SortedSetIncrement方法的原子性,每搜尋一個詞, Score值加1(member不存在則執行的是增加操作), 然後再利用SortedSetRangeByRankWithScores方法擷取Score值前五的memeber和Score資料。

(2). 使用者投票

 分析:和上面的熱詞原理一樣,利用SortedSet的1個key對應多個不重複的 member-score,key用來存儲一個标記,比如叫做“AllChildren”,代表該标記下存儲寶寶的投票情況,member可以存儲寶寶的标記id,score存儲該寶寶的投票數量.

 同樣原理:利用SortedSetIncrement進行自增存儲,利用SortedSetRangeByRankWithScores擷取排名情況。

(3). 排行榜

  類似案例:主播-粉絲刷禮物的排行榜(同時看主播的排行和每個主播下面粉絲的排行), 原理和上面都一樣。

(4). 特殊要求:最近3天的熱點新聞排行榜前十?

思路分析:

A. key以天來劃分,比如每天産生一個key,格式為:hotNews:20201228,member為新聞id,每點選一次,增加1.

 對應redis指令:  zincrby hotNews20201228 1 newsId    (newId為新聞編号)

B. 展示當天排行前十的新聞

   對應redis指令:zrevrange hotNews:20201228 0 10 withscores , 拿到前十的newsId後,再去MySQL中查詢其他資訊,然後顯示。

C. 把最近3天的新聞合并彙總到一個新的key中( hotNews:20201228-20201226 ),相同的member的score預設是相加的。

 對應redis指令: zunionstore hotNews:20201228-20201226 3 hotNews:20201228 hotNews:20201227 hotNews:20201226

D. 展示三日排行前十

    對應redis指令:zrevrange hotNews:20201228-20201226 0 10 withscores

總結: 

 SortedSet和String利用其自增自減(并傳回目前值)原子性均可以實作計數器的作用,String是針對單個,Sorted是針對某個類别下的多個或每一個,并且實作排序功能。 Hash類型也能實作某個類别下多個物品的計數,但它不具有排序功能。

3. 對應指令 

1. 存儲:zadd key score value
        127.0.0.1:6379> zadd mysort 60 zhangsan
        (integer) 1
        127.0.0.1:6379> zadd mysort 50 lisi
        (integer) 1
        127.0.0.1:6379> zadd mysort 80 wangwu
        (integer) 1
2. 擷取:zrange key start end [withscores]
        127.0.0.1:6379> zrange mysort 0 -1
        1) "lisi"
        2) "zhangsan"
        3) "wangwu"

        127.0.0.1:6379> zrange mysort 0 -1 withscores
        1) "zhangsan"
        2) "60"
        3) "wangwu"
        4) "80"
        5) "lisi"
        6) "500"
3. 删除:zrem key value
        127.0.0.1:6379> zrem mysort lisi
        (integer) 1      

其它指令詳見文檔: 

第二節:Redis資料類型、使用場景、常用指令剖析

六. Geo、HyperLogLog、Stream、Bitmap

1.Geo

(1). 含義:可以用來儲存地理位置,并作位置距離計算或者根據半徑計算位置等,該功能在 Redis 3.2 版本新增。

(2). 案例:有沒有想過用Redis來實作附近的人,或者計算最優地圖路徑。

(3). 常用指令如下

geoadd:添加地理位置的坐标。
geopos:擷取地理位置的坐标。
geodist:計算兩個位置之間的距離。
georadius:根據使用者給定的經緯度坐标來擷取指定範圍内的地理位置集合。
georadiusbymember:根據儲存在位置集合裡面的某個地點擷取指定範圍内的地理位置集合。
geohash:傳回一個或多個位置對象的 geohash 值。      

參考:https://www.runoob.com/redis/redis-geo.html

2.HyperLogLog

(1). 什麼是基數?

 比如資料集 {1, 3, 5, 7, 5, 7, 8}, 那麼這個資料集的基數集為 {1, 3, 5 ,7, 8}, 基數即不重複元素個數為5。

 基數估計就是在誤差可接受的範圍内,快速計算基數。

(2). 含義:供不精确的去重計數功能,比較适合用來做大規模資料的去重統計,例如統計 UV; Redis 在 2.8.9 版本添加了 HyperLogLog 結構。

  Redis HyperLogLog 是用來做基數統計的算法,HyperLogLog 的優點是,在輸入元素的數量或者體積非常非常大時,計算基數所需的空間總是固定 的、并且是很小的。在 Redis 裡面,每個 HyperLogLog 鍵隻需要花費 12 KB 記憶體,就可以計算接近 2^64 個不同元素的基 數。這和計算基數時,元素越多耗費記憶體就越多的集合形成鮮明對比。但是,因為 HyperLogLog 隻會根據輸入元素來計算基數,而不會儲存輸入元素本身,是以 HyperLogLog 不能像集合那樣,傳回輸入的各個元素。

(3). 指令

1.添加指定元素到 HyperLogLog 中。
   pfadd key element [element ...]
2.傳回給定 HyperLogLog 的基數估算值。
   pfcount key [key ...]
3.将多個 HyperLogLog 合并為一個 HyperLogLog
   pfmerge destkey sourcekey [sourcekey ...]      

如圖:

第二節:Redis資料類型、使用場景、常用指令剖析

參考:https://www.runoob.com/redis/redis-hyperloglog.html

3.Stream

 Redis Stream 是 Redis 5.0 版本新增加的資料結構。

 Redis Stream 主要用于消息隊列(MQ,Message Queue),Redis 本身是有一個 Redis 釋出訂閱 (pub/sub) 來實作消息隊列的功能,但它有個缺點就是消息無法持久化,如果出現網絡斷開、Redis 當機等,消息就會被丢棄。

 簡單來說釋出訂閱 (pub/sub) 可以分發消息,但無法記錄曆史消息。而 Redis Stream 提供了消息的持久化和主備複制功能,可以讓任何用戶端通路任何時刻的資料,并且能記住每一個用戶端的通路位置,還能保證消息不丢失。

第二節:Redis資料類型、使用場景、常用指令剖析

參考:https://www.runoob.com/redis/redis-stream.html

4. Bitmap 

 位圖是支援按 bit 位來存儲資訊,可以用來實作 布隆過濾器(BloomFilter)

!

  • 作       者 : Yaopengfei(姚鵬飛)
  • 部落格位址 : http://www.cnblogs.com/yaopengfei/
  • 聲     明1 : 如有錯誤,歡迎讨論,請勿謾罵^_^。
  • 聲     明2 : 原創部落格請在轉載時保留原文連結或在文章開頭加上本人部落格位址,否則保留追究法律責任的權利。