Redis 有 5 種基礎(chǔ)數(shù)據(jù)結(jié)構(gòu),分別為:string (字符串)、list (列表)、set (集合)、hash (哈希) 和 zset (有序集合)。
Redis 所有的數(shù)據(jù)結(jié)構(gòu)都是一個key對應(yīng)一個value,不同類型的數(shù)據(jù)結(jié)構(gòu)之間的差異就在于value的結(jié)構(gòu)不同,例如string數(shù)據(jù)類型,他的value就是一個字符串,list數(shù)據(jù)類型,他的value是一個鏈表。
1.String
字符串 String 是 Redis 最簡單的數(shù)據(jù)結(jié)構(gòu),可以存儲字符串、整數(shù)或者浮點(diǎn)數(shù)。最常見的應(yīng)用場景就是對象緩存,例如緩存用戶信息,key是"userInfo"+#{用戶ID},value是用戶信息對象的JSON字符串。
案例:
key:userInfo123
value:{"gender":1,"nickname":"java程序魚","userId":123}
Redis String基本操作
127.0.0.1:6379> set name hzy # 設(shè)置
OK
127.0.0.1:6379> get name # 獲取
"hzy"
127.0.0.1:6379> exists name # 判斷是否存在
(integer) 1
127.0.0.1:6379> del name # 刪除
(integer) 1
127.0.0.1:6379> get key
(nil)
Redis String批量操作
可以批量對多個字符串進(jìn)行讀寫,節(jié)省網(wǎng)絡(luò)耗時(shí)開銷。
127.0.0.1:6379> mset name1 xiaoming name2 xiaohong # 批量設(shè)置
OK
127.0.0.1:6379> mget name1 name2 # 批量獲取
1) "xiaoming"
2) "xiaohong"
Redis String計(jì)數(shù)操作
如果 value 值是一個整數(shù),我們可以對它進(jìn)行自增長操作。自增長是有范圍的,它的范圍是 signed long 的最大最小值,超過了這個值,Redis 會報(bào)錯。
127.0.0.1:6379> incr likenum # 自增1
(integer) 1
127.0.0.1:6379> get likenum
"1"
127.0.0.1:6379> decr likenum # 減1
(integer) 0
127.0.0.1:6379> get number
"0"
Redis String過期操作
127.0.0.1:6379> expire name 60 # 設(shè)置超時(shí)時(shí)間
(integer) 1
127.0.0.1:6379> setex name 60 value # 等價(jià)于 setex + expire
OK
127.0.0.1:6379> ttl key # 查看數(shù)據(jù)還有多久過期
(integer) 11
字符串是由多個字節(jié)組成,每個字節(jié)又是由 8 個 bit 組成
Redis String使用場景
緩存:像我們平時(shí)開發(fā),經(jīng)常會把一個對象轉(zhuǎn)成json字符串,然后放到redis里緩存
計(jì)數(shù)器:像博客文章的閱讀量、評論數(shù)、點(diǎn)贊數(shù)等等
分布式系統(tǒng)生成自增長ID
2. List
Redis 的列表相當(dāng)于 Java 語言里面的 LinkedList。
LinkedList優(yōu)點(diǎn):插入性能高,不管是從末尾插入還是中間插入
LinkedList缺點(diǎn):隨機(jī)讀性能差,例如LinkedList.get(10),這種操作,性能就很低,因?yàn)樗枰闅v這個鏈表,從頭開始遍歷這個鏈表,直到找到index = 10的這個元素為止。
Redis List 數(shù)據(jù)類型如何實(shí)現(xiàn)隊(duì)列?
右邊進(jìn)左邊出
127.0.0.1:6379> rpush apps qq # 將元素插入到列表的尾部(最右邊)
(integer) 1
127.0.0.1:6379> rpush apps wechat taobao # 將多個元素插入到列表的尾部(最右邊)
(integer) 3
127.0.0.1:6379> lpop apps # 移除并返回列表的第一個元素(最左邊)
"qq"
127.0.0.1:6379> lrange apps 0 1 # 返回列表中指定區(qū)間內(nèi)的元素,0表示第一個,1表示第二個,-1表示最后一個
1) "wechat"
2) "taobao"
127.0.0.1:6379> lrange apps 0 -1 # -1表示倒數(shù)第一
1) "wechat"
2) "taobao"
注意: 當(dāng)列表彈出了最后一個元素之后,該數(shù)據(jù)結(jié)構(gòu)自動被刪除,內(nèi)存被回收
Redis List 數(shù)據(jù)類型如何實(shí)現(xiàn)棧?
先進(jìn)先出,右邊進(jìn)右邊出
127.0.0.1:6379> rpush apps qq wechat taobao
(integer) 3
127.0.0.1:6379> rpop apps # 移除列表的最后一個元素,返回值為移除的元素
"taobao"
Redis List 使用場景
異步隊(duì)列
任務(wù)輪詢(RPOPLPUSH)
文章列表(lrange key 0 9)
3.Hash
Redis的Hash結(jié)構(gòu)相當(dāng)于Java語言的HashMap,內(nèi)部實(shí)現(xiàn)結(jié)構(gòu)上與JDK1.7的HashMap一致,底層通過數(shù)據(jù)+鏈表實(shí)現(xiàn)。
Redis Hash 常用命令
127.0.0.1:6379> hmset userInfo name "hzy" age "24" sex "1"
OK
127.0.0.1:6379> hexists userInfo name # 相當(dāng)于HashMap的containsKey()
(integer) 1
127.0.0.1:6379> hget userInfo name # 相當(dāng)于HashMap的get()
"hzy"
127.0.0.1:6379> hget userInfo age
"24"
127.0.0.1:6379> hgetall userInfo # 數(shù)據(jù)量大時(shí),謹(jǐn)慎使用!獲取在哈希表中指定 key 的所有字段和值
1) "name"
2) "hzy"
3) "age"
4) "24"
5) "sex"
6) "1"
127.0.0.1:6379> hkeys userInfo # 數(shù)據(jù)量大時(shí),謹(jǐn)慎使用!獲取 key 列表
1) "name"
2) "age"
3) "sex"
127.0.0.1:6379> hvals userInfo # 獲取 value 列表
1) "hzy"
2) "24"
3) "1"
127.0.0.1:6379> hset userInfo name "test" # 修改某個字段對應(yīng)的值
127.0.0.1:6379> hget userInfo name
"test"
Redis Hash 使用場景
記錄整個博客的訪問人數(shù)(數(shù)據(jù)量大會考慮HyperLogLog,但是這個數(shù)據(jù)結(jié)構(gòu)存在很小的誤差,如果不能接受誤差,可以考慮別的方案)
記錄博客中某個博主的主頁訪問量、博主的姓名、聯(lián)系方式、住址
4.Set
Redis的set集合相當(dāng)于Java的HashSet。Redis 中的 set 類型是一種無序集合,集合中的元素沒有先后順序。
補(bǔ)充:HashSet就是基于HashMap來實(shí)現(xiàn)的,HashSet,他其實(shí)就是說一個集合,里面的元素是無序的,他里面的元素不能重復(fù)的,HashMap的key是無順序的,你插入進(jìn)去的順序,跟你迭代遍歷的順序是不一樣的,而且HashMap的key是沒有重復(fù)的,HashSet直接基于HashMap實(shí)現(xiàn)的。
Redis Set 常用命令
127.0.0.1:6379> sadd apps wechat qq # 添加元素
(integer) 2
127.0.0.1:6379> sadd apps qq # 重復(fù)
(integer) 0
127.0.0.1:6379> smembers apps # 注意:查詢順序和插入的并不一致,因?yàn)?set 是無序的
1) "qq"
2) "wechat"
127.0.0.1:6379> scard apps # 獲取長度
(integer) 2
127.0.0.1:6379> sismember apps qq # 謹(jǐn)慎使用!檢查某個元素是否存在set中,只能接收單個元素
(integer) 1
127.0.0.1:6379> sadd apps2 wechat qq
(integer) 2
127.0.0.1:6379> sinterstore apps apps apps2 # 獲取 apps 和 apps 的交集并存放在 apps3 中
(integer) 1
127.0.0.1:6379> smembers app3
1) "qq"
2) "wechat"
注意: 當(dāng)集合中最后一個元素移除之后,數(shù)據(jù)結(jié)構(gòu)自動刪除,內(nèi)存被回收
Redis Set 使用場景
微博抽獎:如果數(shù)據(jù)量不是特別大的時(shí)候,可以使用spop(移除并返回集合中的一個隨機(jī)元素)或srandmember(返回集合中一個或多個隨機(jī)數(shù))
QQ標(biāo)簽:一個用戶多個便簽
共同關(guān)注(交集)
共同好友(交集)
5.sorted set
sorted set 有序集合,sorted set 增加了一個權(quán)重參數(shù) score,使得集合中的元素能夠按 score 進(jìn)行有序排列,還可以通過 score 的范圍來獲取元素的列表。使得它類似于Java的TreeSet和HashMap的結(jié)合體。
Sorted set 常用命令
127.0.0.1:6379> zadd apps 3.0 qq # 添加元素到 sorted set 中,3.0是score的值
(integer) 1
127.0.0.1:6379> zadd apps 2.0 wechat 1.0 aliyun # 一次添加多個元素
(integer) 2
127.0.0.1:6379> zcard apps # 查看 sorted set 中的元素?cái)?shù)量
(integer) 3
127.0.0.1:6379> zscore apps wechat # 查看某個 value 的權(quán)重
"2.0"
127.0.0.1:6379> zrange apps 0 -1 # 通過索引區(qū)間返回有序集合指定區(qū)間內(nèi)的成員,0 -1 表示輸出所有元素
1) "aliyun"
2) "wechat"
3) "qq"
127.0.0.1:6379> zrange apps 0 1 # 通過索引區(qū)間返回有序集合指定區(qū)間內(nèi)的成員
1) "aliyun"
2) "wechat"
127.0.0.1:6379> zrevrange apps 0 1 # 相當(dāng)于zrange的反轉(zhuǎn)
1) "qq"
2) "wechat"
注意: sorted set 中最后一個 value 被移除后,數(shù)據(jù)結(jié)構(gòu)自動刪除,內(nèi)存被回收
Sorted set 使用場景
排行榜
訂單支付超時(shí)(下單時(shí)插入,member為訂單號,score為訂單超時(shí)時(shí)間戳,然后寫個定時(shí)任務(wù)每隔一段時(shí)間執(zhí)行zrange)