一. 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類型的還有很多指令,詳細請查看指令手冊:
補充兩個指令:info 和 scan
(1). info: 查看redis服務的運行信息,一堆內容,分為9部分
(2). scan指令:前面說到,查詢redis中的key不要用 keys *指令,那么可以用 scan指令替代。
參考文章:https://www.cnblogs.com/yaopengfei/p/11912930.html
二. Hash
1. 數據類型介紹
一個key,對應一個Key-Value集合,即:hashid → {key:value;key:value;key:value;},相當於value又是一個“鍵值對集合” 或者值是另外一個 Dictionary。
2. 應用場景
(1). 購物車
以用戶id作為hashid,商品id作為key,商品數量作為value,利用自增和自減功能來實現增加商品數量和減少商品數量功能。也可以刪除商品,獲取商品總數,獲取購物車中所有商品。
(2). 群聊
臨時存儲消息(根據實際場景考慮是否合適),比如:群名為hashid, 用戶id當做key,內容作為value。 這樣存儲可以,但是取數據的時候必須一下全部取出來,不能根據時間取前n條。
(3). 對象緩存
可以把一個對象中的每個字段當做一個key,存放到hash中。
3. 相關指令
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
其它指令詳見指令文檔:
4. 優缺點
優點:
(1). 同類數據歸類整合存儲,方便數據管理
(2). 相比string操作消耗內存與cpu更小
(3). 相比string存儲更節省空間
缺點:
(1).過期功能不能使用在內層的field上,只能用在最外層的key上
(2).Redis集群架構下不適合大規模使用
參考文章:https://www.cnblogs.com/yaopengfei/p/11912930.html
三. List
1. 數據類型介紹
它是一個雙向鏈表,支持左進、左出、右進、右出,所以它即可以充當隊列使用,也可以充當棧使用。
(1). 隊列:先進先出, 可以利用List左進右出,或者右進左出
(2). 棧:先進后出,可以利用List左進左出,或者右進右出
(3). 阻塞隊列:數據的生產者可以通過LPUSH命令從左邊插入數據,多個數據消費者,可以使用BRPOP命令阻塞的“搶”列表尾部的數據
2. 應用場景
(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字)。
3. 相關指令
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,一直阻塞等待
其它指令詳見指令文檔:
參考文章:https://www.cnblogs.com/yaopengfei/p/11940420.html
四. Set
1. 數據類型介紹
1個key→多個value,value的值不重復! (無序集合,自動去重)
Set一種無序且元素內容不重復的集合,不用做重復性判斷了,和我們數學中的集合概念相同,可以對多個集合求交集、並集、差集,key可以理解成集合的名字。
注:set 用哈希表來保持字符串的唯一性,沒有先后順序,是按照自己的一個存儲方式來遍歷,因為沒有保存插入順序。
2. 應用場景
(一).抽獎
(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)

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 }
(二). 微信或微博中消息的點贊(或者某篇文章的收藏)
(1). 背景
微信朋友圈用戶A的某條消息的點贊功能,要實現點贊、取消點贊、獲取點贊列表、獲取點贊用戶數量、判斷某用戶是否點贊過。
(2). 技術分析
利用Set結構, 以用戶Id-消息id作為key,點贊過該消息的用戶id作為value。
A:點贊 SetAdd方法
B:取消點贊 SetRemove方法
C:獲取點贊列表 SetMembers方法
D:獲取點贊用戶數量 SetLength方法
E:判斷某用戶是否點贊過 SetContains方法
對應redis指令:
(三). 關注模型
(1).背景
比如微博關注或者共同好友的問題,以微博關注為例,要實現:共同關注、關注的和、關注A的用戶中也關注B的、當A進入B頁面,求可能認識的人。
(2). 技術分析
利用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命令:
(3). 代碼分享

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
同上
(五). 集合操作實現電商網站的篩選
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
其它指令詳見指令文檔:
參考文章:https://www.cnblogs.com/yaopengfei/p/11936144.html
五. SortedSet
1. 數據類型介紹
將Set中的元素增加了一個權重參數 score,使得集合中的元素能夠按 score 進行有序排列(有序集合,自動去重), 三個字段:key-member-score, key代表鍵,member代表值,score代表權重或者打分值
2. 應用場景
(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
其它指令詳見文檔:
參考文章:https://www.cnblogs.com/yaopengfei/p/11936144.html
六. 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 ...]
如圖:
參考: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 提供了消息的持久化和主備復制功能,可以讓任何客戶端訪問任何時刻的數據,並且能記住每一個客戶端的訪問位置,還能保證消息不丟失。
參考:https://www.runoob.com/redis/redis-stream.html
4. Bitmap
位圖是支持按 bit 位來存儲信息,可以用來實現 布隆過濾器(BloomFilter)
!
- 作 者 : Yaopengfei(姚鵬飛)
- 博客地址 : http://www.cnblogs.com/yaopengfei/
- 聲 明1 : 如有錯誤,歡迎討論,請勿謾罵^_^。
- 聲 明2 : 原創博客請在轉載時保留原文鏈接或在文章開頭加上本人博客地址,否則保留追究法律責任的權利。