Redis 八種常用數(shù)據(jù)類型常用命令和應(yīng)用場(chǎng)景
夯實(shí)基礎(chǔ),通過(guò)這篇文章帶著大家簡(jiǎn)單回顧一下 Redis 中的 8 種常用數(shù)據(jù)類型:
- 5 種基礎(chǔ)數(shù)據(jù)類型:String(字符串)、List(列表)、Set(集合)、Hash(散列)、Zset(有序集合)。
- 3 種特殊數(shù)據(jù)類型:HyperLogLog(基數(shù)統(tǒng)計(jì))、Bitmap (位圖)、Geospatial (地理位置)。
Redis 5 種基本數(shù)據(jù)類型
Redis 共有 5 種基本數(shù)據(jù)類型:String(字符串)、List(列表)、Set(集合)、Hash(散列)、Zset(有序集合)。
這 5 種數(shù)據(jù)類型是直接提供給用戶使用的,是數(shù)據(jù)的保存形式,其底層實(shí)現(xiàn)主要依賴這 8 種數(shù)據(jù)結(jié)構(gòu):簡(jiǎn)單動(dòng)態(tài)字符串(SDS)、LinkedList(雙向鏈表)、Dict(哈希表/字典)、SkipList(跳躍表)、Intset(整數(shù)集合)、ZipList(壓縮列表)、QuickList(快速列表)。
Redis 5 種基本數(shù)據(jù)類型對(duì)應(yīng)的底層數(shù)據(jù)結(jié)構(gòu)實(shí)現(xiàn)如下表所示:
String | List | Hash | Set | Zset |
SDS | LinkedList/ZipList/QuickList | Dict、ZipList | Dict、Intset | ZipList、SkipList |
Redis 3.2 之前,List 底層實(shí)現(xiàn)是 LinkedList 或者 ZipList。Redis 3.2 之后,引入了 LinkedList 和 ZipList 的結(jié)合 QuickList,List 的底層實(shí)現(xiàn)變?yōu)?QuickList。從 Redis 7.0 開始, ZipList 被 ListPack 取代。
你可以在 Redis 官網(wǎng)上找到 Redis 數(shù)據(jù)類型/結(jié)構(gòu)非常詳細(xì)的介紹:
- Redis Data Structures[1]
- Redis Data types tutorial[2]
未來(lái)隨著 Redis 新版本的發(fā)布,可能會(huì)有新的數(shù)據(jù)結(jié)構(gòu)出現(xiàn),通過(guò)查閱 Redis 官網(wǎng)對(duì)應(yīng)的介紹,你總能獲取到最靠譜的信息。
圖片
String(字符串)
介紹
String 是 Redis 中最簡(jiǎn)單同時(shí)也是最常用的一個(gè)數(shù)據(jù)類型。
String 是一種二進(jìn)制安全的數(shù)據(jù)類型,可以用來(lái)存儲(chǔ)任何類型的數(shù)據(jù)比如字符串、整數(shù)、浮點(diǎn)數(shù)、圖片(圖片的 base64 編碼或者解碼或者圖片的路徑)、序列化后的對(duì)象。
圖片
雖然 Redis 是用 C 語(yǔ)言寫的,但是 Redis 并沒有使用 C 的字符串表示,而是自己構(gòu)建了一種 簡(jiǎn)單動(dòng)態(tài)字符串(Simple Dynamic String,SDS)。相比于 C 的原生字符串,Redis 的 SDS 不光可以保存文本數(shù)據(jù)還可以保存二進(jìn)制數(shù)據(jù),并且獲取字符串長(zhǎng)度復(fù)雜度為 O(1)(C 字符串為 O(N)),除此之外,Redis 的 SDS API 是安全的,不會(huì)造成緩沖區(qū)溢出。
常用命令
命令 | 介紹 |
SET key value | 設(shè)置指定 key 的值 |
SETNX key value | 只有在 key 不存在時(shí)設(shè)置 key 的值 |
GET key | 獲取指定 key 的值 |
MSET key1 value1 key2 value2 …… | 設(shè)置一個(gè)或多個(gè)指定 key 的值 |
MGET key1 key2 ... | 獲取一個(gè)或多個(gè)指定 key 的值 |
STRLEN key | 返回 key 所儲(chǔ)存的字符串值的長(zhǎng)度 |
INCR key | 將 key 中儲(chǔ)存的數(shù)字值增一 |
DECR key | 將 key 中儲(chǔ)存的數(shù)字值減一 |
EXISTS key | 判斷指定 key 是否存在 |
DEL key(通用) | 刪除指定的 key |
EXPIRE key seconds(通用) | 給指定 key 設(shè)置過(guò)期時(shí)間 |
更多 Redis String 命令以及詳細(xì)使用指南,請(qǐng)查看 Redis 官網(wǎng)對(duì)應(yīng)的介紹:https://redis.io/commands/?group=string 。
基本操作:
> SET key value
OK
> GET key
"value"
> EXISTS key
(integer) 1
> STRLEN key
(integer) 5
> DEL key
(integer) 1
> GET key
(nil)
批量設(shè)置:
> MSET key1 value1 key2 value2
OK
> MGET key1 key2 # 批量獲取多個(gè) key 對(duì)應(yīng)的 value
1) "value1"
2) "value2"
計(jì)數(shù)器(字符串的內(nèi)容為整數(shù)的時(shí)候可以使用):
> SET number 1
OK
> INCR number # 將 key 中儲(chǔ)存的數(shù)字值增一
(integer) 2
> GET number
"2"
> DECR number # 將 key 中儲(chǔ)存的數(shù)字值減一
(integer) 1
> GET number
"1"
設(shè)置過(guò)期時(shí)間(默認(rèn)為永不過(guò)期):
> EXPIRE key 60
(integer) 1
> SETEX key 60 value # 設(shè)置值并設(shè)置過(guò)期時(shí)間
OK
> TTL key
(integer) 56
應(yīng)用場(chǎng)景
需要存儲(chǔ)常規(guī)數(shù)據(jù)的場(chǎng)景
- 舉例:緩存 Session、Token、圖片地址、序列化后的對(duì)象(相比較于 Hash 存儲(chǔ)更節(jié)省內(nèi)存)。
- 相關(guān)命令:SET、GET。
需要計(jì)數(shù)的場(chǎng)景
- 舉例:用戶單位時(shí)間的請(qǐng)求數(shù)(簡(jiǎn)單限流可以用到)、頁(yè)面單位時(shí)間的訪問(wèn)數(shù)。
- 相關(guān)命令:SET、GET、 INCR、DECR 。
分布式鎖
利用 SETNX key value 命令可以實(shí)現(xiàn)一個(gè)最簡(jiǎn)易的分布式鎖(存在一些缺陷,通常不建議這樣實(shí)現(xiàn)分布式鎖)。
List(列表)
介紹
Redis 中的 List 其實(shí)就是鏈表數(shù)據(jù)結(jié)構(gòu)的實(shí)現(xiàn)。我在 線性數(shù)據(jù)結(jié)構(gòu) :數(shù)組、鏈表、棧、隊(duì)列[3] 這篇文章中詳細(xì)介紹了鏈表這種數(shù)據(jù)結(jié)構(gòu),我這里就不多做介紹了。
許多高級(jí)編程語(yǔ)言都內(nèi)置了鏈表的實(shí)現(xiàn)比如 Java 中的 LinkedList,但是 C 語(yǔ)言并沒有實(shí)現(xiàn)鏈表,所以 Redis 實(shí)現(xiàn)了自己的鏈表數(shù)據(jù)結(jié)構(gòu)。Redis 的 List 的實(shí)現(xiàn)為一個(gè) 雙向鏈表,即可以支持反向查找和遍歷,更方便操作,不過(guò)帶來(lái)了部分額外的內(nèi)存開銷。
圖片
常用命令
命令 | 介紹 |
RPUSH key value1 value2 ... | 在指定列表的尾部(右邊)添加一個(gè)或多個(gè)元素 |
LPUSH key value1 value2 ... | 在指定列表的頭部(左邊)添加一個(gè)或多個(gè)元素 |
LSET key index value | 將指定列表索引 index 位置的值設(shè)置為 value |
LPOP key | 移除并獲取指定列表的第一個(gè)元素(最左邊) |
RPOP key | 移除并獲取指定列表的最后一個(gè)元素(最右邊) |
LLEN key | 獲取列表元素?cái)?shù)量 |
LRANGE key start end | 獲取列表 start 和 end 之間 的元素 |
更多 Redis List 命令以及詳細(xì)使用指南,請(qǐng)查看 Redis 官網(wǎng)對(duì)應(yīng)的介紹:https://redis.io/commands/?group=list 。
通過(guò) RPUSH/LPOP 或者 LPUSH/RPOP實(shí)現(xiàn)隊(duì)列:
> RPUSH myList value1
(integer) 1
> RPUSH myList value2 value3
(integer) 3
> LPOP myList
"value1"
> LRANGE myList 0 1
1) "value2"
2) "value3"
> LRANGE myList 0 -1
1) "value2"
2) "value3"
通過(guò) RPUSH/RPOP或者LPUSH/LPOP 實(shí)現(xiàn)棧:
> RPUSH myList2 value1 value2 value3
(integer) 3
> RPOP myList2 # 將 list的最右邊的元素取出
"value3"
我專門畫了一個(gè)圖方便大家理解 RPUSH , LPOP , lpush , RPOP 命令:
圖片
通過(guò) LRANGE 查看對(duì)應(yīng)下標(biāo)范圍的列表元素:
> RPUSH myList value1 value2 value3
(integer) 3
> LRANGE myList 0 1
1) "value1"
2) "value2"
> LRANGE myList 0 -1
1) "value1"
2) "value2"
3) "value3"
通過(guò) LRANGE 命令,你可以基于 List 實(shí)現(xiàn)分頁(yè)查詢,性能非常高!
通過(guò) LLEN 查看鏈表長(zhǎng)度:
> LLEN myList
(integer) 3
應(yīng)用場(chǎng)景
信息流展示
- 舉例:最新文章、最新動(dòng)態(tài)。
- 相關(guān)命令:LPUSH、LRANGE。
消息隊(duì)列
List 可以用來(lái)做消息隊(duì)列,只是功能過(guò)于簡(jiǎn)單且存在很多缺陷,不建議這樣做。
相對(duì)來(lái)說(shuō),Redis 5.0 新增加的一個(gè)數(shù)據(jù)結(jié)構(gòu) Stream 更適合做消息隊(duì)列一些,只是功能依然非常簡(jiǎn)陋。和專業(yè)的消息隊(duì)列相比,還是有很多欠缺的地方比如消息丟失和堆積問(wèn)題不好解決。
Hash(哈希)
介紹
Redis 中的 Hash 是一個(gè) String 類型的 field-value(鍵值對(duì)) 的映射表,特別適合用于存儲(chǔ)對(duì)象,后續(xù)操作的時(shí)候,你可以直接修改這個(gè)對(duì)象中的某些字段的值。
Hash 類似于 JDK1.8 前的 HashMap,內(nèi)部實(shí)現(xiàn)也差不多(數(shù)組 + 鏈表)。不過(guò),Redis 的 Hash 做了更多優(yōu)化。
圖片
常用命令
命令 | 介紹 |
HSET key field value | 設(shè)置指定哈希表中指定字段的值 |
HSETNX key field value | 只有指定字段不存在時(shí)設(shè)置指定字段的值 |
HMSET key field1 value1 field2 value2 ... | 同時(shí)將一個(gè)或多個(gè) field-value (域-值)對(duì)設(shè)置到指定哈希表中 |
HGET key field | 獲取指定哈希表中指定字段的值 |
HMGET key field1 field2 ... | 獲取指定哈希表中一個(gè)或者多個(gè)指定字段的值 |
HGETALL key | 獲取指定哈希表中所有的鍵值對(duì) |
HEXISTS key field | 查看指定哈希表中指定的字段是否存在 |
HDEL key field1 field2 ... | 刪除一個(gè)或多個(gè)哈希表字段 |
HLEN key | 獲取指定哈希表中字段的數(shù)量 |
HINCRBY key field increment | 對(duì)指定哈希中的指定字段做運(yùn)算操作(正數(shù)為加,負(fù)數(shù)為減) |
更多 Redis Hash 命令以及詳細(xì)使用指南,請(qǐng)查看 Redis 官網(wǎng)對(duì)應(yīng)的介紹:https://redis.io/commands/?group=hash 。
模擬對(duì)象數(shù)據(jù)存儲(chǔ):
> HMSET userInfoKey name "guide" description "dev" age 24
OK
> HEXISTS userInfoKey name # 查看 key 對(duì)應(yīng)的 value中指定的字段是否存在。
(integer) 1
> HGET userInfoKey name # 獲取存儲(chǔ)在哈希表中指定字段的值。
"guide"
> HGET userInfoKey age
"24"
> HGETALL userInfoKey # 獲取在哈希表中指定 key 的所有字段和值
1) "name"
2) "guide"
3) "description"
4) "dev"
5) "age"
6) "24"
> HSET userInfoKey name "GuideGeGe"
> HGET userInfoKey name
"GuideGeGe"
> HINCRBY userInfoKey age 2
(integer) 26
應(yīng)用場(chǎng)景
對(duì)象數(shù)據(jù)存儲(chǔ)場(chǎng)景
- 舉例:用戶信息、商品信息、文章信息、購(gòu)物車信息。
- 相關(guān)命令:HSET (設(shè)置單個(gè)字段的值)、HMSET(設(shè)置多個(gè)字段的值)、HGET(獲取單個(gè)字段的值)、HMGET(獲取多個(gè)字段的值)。
Set(集合)
介紹
Redis 中的 Set 類型是一種無(wú)序集合,集合中的元素沒有先后順序但都唯一,有點(diǎn)類似于 Java 中的 HashSet 。當(dāng)你需要存儲(chǔ)一個(gè)列表數(shù)據(jù),又不希望出現(xiàn)重復(fù)數(shù)據(jù)時(shí),Set 是一個(gè)很好的選擇,并且 Set 提供了判斷某個(gè)元素是否在一個(gè) Set 集合內(nèi)的重要接口,這個(gè)也是 List 所不能提供的。
你可以基于 Set 輕易實(shí)現(xiàn)交集、并集、差集的操作,比如你可以將一個(gè)用戶所有的關(guān)注人存在一個(gè)集合中,將其所有粉絲存在一個(gè)集合。這樣的話,Set 可以非常方便的實(shí)現(xiàn)如共同關(guān)注、共同粉絲、共同喜好等功能。這個(gè)過(guò)程也就是求交集的過(guò)程。
圖片
常用命令
命令 | 介紹 |
SADD key member1 member2 ... | 向指定集合添加一個(gè)或多個(gè)元素 |
SMEMBERS key | 獲取指定集合中的所有元素 |
SCARD key | 獲取指定集合的元素?cái)?shù)量 |
SISMEMBER key member | 判斷指定元素是否在指定集合中 |
SINTER key1 key2 ... | 獲取給定所有集合的交集 |
SINTERSTORE destination key1 key2 ... | 將給定所有集合的交集存儲(chǔ)在 destination 中 |
SUNION key1 key2 ... | 獲取給定所有集合的并集 |
SUNIONSTORE destination key1 key2 ... | 將給定所有集合的并集存儲(chǔ)在 destination 中 |
SDIFF key1 key2 ... | 獲取給定所有集合的差集 |
SDIFFSTORE destination key1 key2 ... | 將給定所有集合的差集存儲(chǔ)在 destination 中 |
SPOP key count | 隨機(jī)移除并獲取指定集合中一個(gè)或多個(gè)元素 |
SRANDMEMBER key count | 隨機(jī)獲取指定集合中指定數(shù)量的元素 |
更多 Redis Set 命令以及詳細(xì)使用指南,請(qǐng)查看 Redis 官網(wǎng)對(duì)應(yīng)的介紹:https://redis.io/commands/?group=set 。
基本操作:
> SADD mySet value1 value2
(integer) 2
> SADD mySet value1 # 不允許有重復(fù)元素,因此添加失敗
(integer) 0
> SMEMBERS mySet
1) "value1"
2) "value2"
> SCARD mySet
(integer) 2
> SISMEMBER mySet value1
(integer) 1
> SADD mySet2 value2 value3
(integer) 2
- mySet : value1、value2 。
- mySet2:value2、value3 。
求交集:
> SINTERSTORE mySet3 mySet mySet2
(integer) 1
> SMEMBERS mySet3
1) "value2"
求并集:
> SUNION mySet mySet2
1) "value3"
2) "value2"
3) "value1"
求差集:
> SDIFF mySet mySet2 # 差集是由所有屬于 mySet 但不屬于 A 的元素組成的集合
1) "value1"
應(yīng)用場(chǎng)景
需要存放的數(shù)據(jù)不能重復(fù)的場(chǎng)景
- 舉例:網(wǎng)站 UV 統(tǒng)計(jì)(數(shù)據(jù)量巨大的場(chǎng)景還是 HyperLogLog更適合一些)、文章點(diǎn)贊、動(dòng)態(tài)點(diǎn)贊等場(chǎng)景。
- 相關(guān)命令:SCARD(獲取集合數(shù)量) 。
圖片
需要獲取多個(gè)數(shù)據(jù)源交集、并集和差集的場(chǎng)景
- 舉例:共同好友(交集)、共同粉絲(交集)、共同關(guān)注(交集)、好友推薦(差集)、音樂(lè)推薦(差集)、訂閱號(hào)推薦(差集+交集) 等場(chǎng)景。
- 相關(guān)命令:SINTER(交集)、SINTERSTORE (交集)、SUNION (并集)、SUNIONSTORE(并集)、SDIFF(差集)、SDIFFSTORE (差集)。
圖片
需要隨機(jī)獲取數(shù)據(jù)源中的元素的場(chǎng)景
- 舉例:抽獎(jiǎng)系統(tǒng)、隨機(jī)點(diǎn)名等場(chǎng)景。
- 相關(guān)命令:SPOP(隨機(jī)獲取集合中的元素并移除,適合不允許重復(fù)中獎(jiǎng)的場(chǎng)景)、SRANDMEMBER(隨機(jī)獲取集合中的元素,適合允許重復(fù)中獎(jiǎng)的場(chǎng)景)。
Sorted Set(有序集合)
介紹
Sorted Set 類似于 Set,但和 Set 相比,Sorted Set 增加了一個(gè)權(quán)重參數(shù) score,使得集合中的元素能夠按 score 進(jìn)行有序排列,還可以通過(guò) score 的范圍來(lái)獲取元素的列表。有點(diǎn)像是 Java 中 HashMap 和 TreeSet 的結(jié)合體。
圖片
常用命令
命令 | 介紹 |
ZADD key score1 member1 score2 member2 ... | 向指定有序集合添加一個(gè)或多個(gè)元素 |
ZCARD KEY | 獲取指定有序集合的元素?cái)?shù)量 |
ZSCORE key member | 獲取指定有序集合中指定元素的 score 值 |
ZINTERSTORE destination numkeys key1 key2 ... | 將給定所有有序集合的交集存儲(chǔ)在 destination 中,對(duì)相同元素對(duì)應(yīng)的 score 值進(jìn)行 SUM 聚合操作,numkeys 為集合數(shù)量 |
ZUNIONSTORE destination numkeys key1 key2 ... | 求并集,其它和 ZINTERSTORE 類似 |
ZDIFFSTORE destination numkeys key1 key2 ... | 求差集,其它和 ZINTERSTORE 類似 |
ZRANGE key start end | 獲取指定有序集合 start 和 end 之間的元素(score 從低到高) |
ZREVRANGE key start end | 獲取指定有序集合 start 和 end 之間的元素(score 從高到底) |
ZREVRANK key member | 獲取指定有序集合中指定元素的排名(score 從大到小排序) |
更多 Redis Sorted Set 命令以及詳細(xì)使用指南,請(qǐng)查看 Redis 官網(wǎng)對(duì)應(yīng)的介紹:https://redis.io/commands/?group=sorted-set 。
基本操作:
> ZADD myZset 2.0 value1 1.0 value2
(integer) 2
> ZCARD myZset
2
> ZSCORE myZset value1
2.0
> ZRANGE myZset 0 1
1) "value2"
2) "value1"
> ZREVRANGE myZset 0 1
1) "value1"
2) "value2"
> ZADD myZset2 4.0 value2 3.0 value3
(integer) 2
- myZset : value1(2.0)、value2(1.0) 。
- myZset2:value2 (4.0)、value3(3.0) 。
獲取指定元素的排名:
> ZREVRANK myZset value1
0
> ZREVRANK myZset value2
1
求交集:
> ZINTERSTORE myZset3 2 myZset myZset2
1
> ZRANGE myZset3 0 1 WITHSCORES
value2
5
求并集:
> ZUNIONSTORE myZset4 2 myZset myZset2
3
> ZRANGE myZset4 0 2 WITHSCORES
value1
2
value3
3
value2
5
求差集:
> ZDIFF 2 myZset myZset2 WITHSCORES
value1
2
應(yīng)用場(chǎng)景
需要隨機(jī)獲取數(shù)據(jù)源中的元素根據(jù)某個(gè)權(quán)重進(jìn)行排序的場(chǎng)景
- 舉例:各種排行榜比如直播間送禮物的排行榜、朋友圈的微信步數(shù)排行榜、王者榮耀中的段位排行榜、話題熱度排行榜等等。
- 相關(guān)命令:ZRANGE (從小到大排序)、 ZREVRANGE (從大到小排序)、ZREVRANK (指定元素排名)。
圖片
需要存儲(chǔ)的數(shù)據(jù)有優(yōu)先級(jí)或者重要程度的場(chǎng)景 比如優(yōu)先級(jí)任務(wù)隊(duì)列。
- 舉例:優(yōu)先級(jí)任務(wù)隊(duì)列。
- 相關(guān)命令:ZRANGE (從小到大排序)、 ZREVRANGE (從大到小排序)、ZREVRANK (指定元素排名)。
總結(jié)
數(shù)據(jù)類型 | 說(shuō)明 |
String | 一種二進(jìn)制安全的數(shù)據(jù)類型,可以用來(lái)存儲(chǔ)任何類型的數(shù)據(jù)比如字符串、整數(shù)、浮點(diǎn)數(shù)、圖片(圖片的 base64 編碼或者解碼或者圖片的路徑)、序列化后的對(duì)象。 |
List | Redis 的 List 的實(shí)現(xiàn)為一個(gè)雙向鏈表,即可以支持反向查找和遍歷,更方便操作,不過(guò)帶來(lái)了部分額外的內(nèi)存開銷。 |
Hash | 一個(gè) String 類型的 field-value(鍵值對(duì)) 的映射表,特別適合用于存儲(chǔ)對(duì)象,后續(xù)操作的時(shí)候,你可以直接修改這個(gè)對(duì)象中的某些字段的值。 |
Set | 無(wú)序集合,集合中的元素沒有先后順序但都唯一,有點(diǎn)類似于 Java 中的 |
Zset | 和 Set 相比,Sorted Set 增加了一個(gè)權(quán)重參數(shù) |
Redis 3 種特殊的數(shù)據(jù)類型
除了 5 種基本的數(shù)據(jù)類型之外,Redis 還支持 3 種特殊的數(shù)據(jù)類型:Bitmap、HyperLogLog、GEO。
Bitmap (位圖)
介紹
根據(jù)官網(wǎng)介紹:
Bitmaps are not an actual data type, but a set of bit-oriented operations defined on the String type which is treated like a bit vector. Since strings are binary safe blobs and their maximum length is 512 MB, they are suitable to set up to 2^32 different bits.
Bitmap 不是 Redis 中的實(shí)際數(shù)據(jù)類型,而是在 String 類型上定義的一組面向位的操作,將其視為位向量。由于字符串是二進(jìn)制安全的塊,且最大長(zhǎng)度為 512 MB,它們適合用于設(shè)置最多 2^32 個(gè)不同的位。
Bitmap 存儲(chǔ)的是連續(xù)的二進(jìn)制數(shù)字(0 和 1),通過(guò) Bitmap, 只需要一個(gè) bit 位來(lái)表示某個(gè)元素對(duì)應(yīng)的值或者狀態(tài),key 就是對(duì)應(yīng)元素本身 。我們知道 8 個(gè) bit 可以組成一個(gè) byte,所以 Bitmap 本身會(huì)極大的節(jié)省儲(chǔ)存空間。
你可以將 Bitmap 看作是一個(gè)存儲(chǔ)二進(jìn)制數(shù)字(0 和 1)的數(shù)組,數(shù)組中每個(gè)元素的下標(biāo)叫做 offset(偏移量)。
圖片
常用命令
命令 | 介紹 |
SETBIT key offset value | 設(shè)置指定 offset 位置的值 |
GETBIT key offset | 獲取指定 offset 位置的值 |
BITCOUNT key start end | 獲取 start 和 end 之前值為 1 的元素個(gè)數(shù) |
BITOP operation destkey key1 key2 ... | 對(duì)一個(gè)或多個(gè) Bitmap 進(jìn)行運(yùn)算,可用運(yùn)算符有 AND, OR, XOR 以及 NOT |
Bitmap 基本操作演示:
# SETBIT 會(huì)返回之前位的值(默認(rèn)是 0)這里會(huì)生成 7 個(gè)位
> SETBIT mykey 7 1
(integer) 0
> SETBIT mykey 7 0
(integer) 1
> GETBIT mykey 7
(integer) 0
> SETBIT mykey 6 1
(integer) 0
> SETBIT mykey 8 1
(integer) 0
# 通過(guò) bitcount 統(tǒng)計(jì)被被設(shè)置為 1 的位的數(shù)量。
> BITCOUNT mykey
(integer) 2
應(yīng)用場(chǎng)景
需要保存狀態(tài)信息(0/1 即可表示)的場(chǎng)景
- 舉例:用戶簽到情況、活躍用戶情況、用戶行為統(tǒng)計(jì)(比如是否點(diǎn)贊過(guò)某個(gè)視頻)。
- 相關(guān)命令:SETBIT、GETBIT、BITCOUNT、BITOP。
HyperLogLog(基數(shù)統(tǒng)計(jì))
介紹
HyperLogLog 是一種有名的基數(shù)計(jì)數(shù)概率算法 ,基于 LogLog Counting(LLC)優(yōu)化改進(jìn)得來(lái),并不是 Redis 特有的,Redis 只是實(shí)現(xiàn)了這個(gè)算法并提供了一些開箱即用的 API。
Redis 提供的 HyperLogLog 占用空間非常非常小,只需要 12k 的空間就能存儲(chǔ)接近2^64個(gè)不同元素。這是真的厲害,這就是數(shù)學(xué)的魅力么!并且,Redis 對(duì) HyperLogLog 的存儲(chǔ)結(jié)構(gòu)做了優(yōu)化,采用兩種方式計(jì)數(shù):
- 稀疏矩陣:計(jì)數(shù)較少的時(shí)候,占用空間很小。
- 稠密矩陣:計(jì)數(shù)達(dá)到某個(gè)閾值的時(shí)候,占用 12k 的空間。
Redis 官方文檔中有對(duì)應(yīng)的詳細(xì)說(shuō)明:
圖片
基數(shù)計(jì)數(shù)概率算法為了節(jié)省內(nèi)存并不會(huì)直接存儲(chǔ)元數(shù)據(jù),而是通過(guò)一定的概率統(tǒng)計(jì)方法預(yù)估基數(shù)值(集合中包含元素的個(gè)數(shù))。因此, HyperLogLog 的計(jì)數(shù)結(jié)果并不是一個(gè)精確值,存在一定的誤差(標(biāo)準(zhǔn)誤差為 0.81% )。
圖片
HyperLogLog 的使用非常簡(jiǎn)單,但原理非常復(fù)雜。HyperLogLog 的原理以及在 Redis 中的實(shí)現(xiàn)可以看這篇文章:HyperLogLog 算法的原理講解以及 Redis 是如何應(yīng)用它的[4] 。
再推薦一個(gè)可以幫助理解 HyperLogLog 原理的工具:Sketch of the Day: HyperLogLog — Cornerstone of a Big Data Infrastructure[5] 。
除了 HyperLogLog 之外,Redis 還提供了其他的概率數(shù)據(jù)結(jié)構(gòu),對(duì)應(yīng)的官方文檔地址:https://redis.io/docs/data-types/probabilistic/ 。
常用命令
HyperLogLog 相關(guān)的命令非常少,最常用的也就 3 個(gè)。
命令 | 介紹 |
PFADD key element1 element2 ... | 添加一個(gè)或多個(gè)元素到 HyperLogLog 中 |
PFCOUNT key1 key2 | 獲取一個(gè)或者多個(gè) HyperLogLog 的唯一計(jì)數(shù)。 |
PFMERGE destkey sourcekey1 sourcekey2 ... | 將多個(gè) HyperLogLog 合并到 destkey 中,destkey 會(huì)結(jié)合多個(gè)源,算出對(duì)應(yīng)的唯一計(jì)數(shù)。 |
HyperLogLog 基本操作演示:
> PFADD hll foo bar zap
(integer) 1
> PFADD hll zap zap zap
(integer) 0
> PFADD hll foo bar
(integer) 0
> PFCOUNT hll
(integer) 3
> PFADD some-other-hll 1 2 3
(integer) 1
> PFCOUNT hll some-other-hll
(integer) 6
> PFMERGE desthll hll some-other-hll
"OK"
> PFCOUNT desthll
(integer) 6
應(yīng)用場(chǎng)景
數(shù)量量巨大(百萬(wàn)、千萬(wàn)級(jí)別以上)的計(jì)數(shù)場(chǎng)景
- 舉例:熱門網(wǎng)站每日/每周/每月訪問(wèn) ip 數(shù)統(tǒng)計(jì)、熱門帖子 uv 統(tǒng)計(jì)、
- 相關(guān)命令:PFADD、PFCOUNT 。
Geospatial (地理位置)
介紹
Geospatial index(地理空間索引,簡(jiǎn)稱 GEO) 主要用于存儲(chǔ)地理位置信息,基于 Sorted Set 實(shí)現(xiàn)。
通過(guò) GEO 我們可以輕松實(shí)現(xiàn)兩個(gè)位置距離的計(jì)算、獲取指定位置附近的元素等功能。
圖片
常用命令
命令 | 介紹 |
GEOADD key longitude1 latitude1 member1 ... | 添加一個(gè)或多個(gè)元素對(duì)應(yīng)的經(jīng)緯度信息到 GEO 中 |
GEOPOS key member1 member2 ... | 返回給定元素的經(jīng)緯度信息 |
GEODIST key member1 member2 M/KM/FT/MI | 返回兩個(gè)給定元素之間的距離 |
GEORADIUS key longitude latitude radius distance | 獲取指定位置附近 distance 范圍內(nèi)的其他元素,支持 ASC(由近到遠(yuǎn))、DESC(由遠(yuǎn)到近)、Count(數(shù)量) 等參數(shù) |
GEORADIUSBYMEMBER key member radius distance | 類似于 GEORADIUS 命令,只是參照的中心點(diǎn)是 GEO 中的元素 |
基本操作:
> GEOADD personLocation 116.33 39.89 user1 116.34 39.90 user2 116.35 39.88 user3
3
> GEOPOS personLocation user1
116.3299986720085144
39.89000061669732844
> GEODIST personLocation user1 user2 km
1.4018
通過(guò) Redis 可視化工具查看 personLocation ,果不其然,底層就是 Sorted Set。
GEO 中存儲(chǔ)的地理位置信息的經(jīng)緯度數(shù)據(jù)通過(guò) GeoHash 算法轉(zhuǎn)換成了一個(gè)整數(shù),這個(gè)整數(shù)作為 Sorted Set 的 score(權(quán)重參數(shù))使用。
圖片
獲取指定位置范圍內(nèi)的其他元素:
> GEORADIUS personLocation 116.33 39.87 3 km
user3
user1
> GEORADIUS personLocation 116.33 39.87 2 km
> GEORADIUS personLocation 116.33 39.87 5 km
user3
user1
user2
> GEORADIUSBYMEMBER personLocation user1 5 km
user3
user1
user2
> GEORADIUSBYMEMBER personLocation user1 2 km
user1
user2
GEORADIUS 命令的底層原理解析可以看看阿里的這篇文章:Redis 到底是怎么實(shí)現(xiàn)“附近的人”這個(gè)功能的呢?[6] 。
移除元素:
GEO 底層是 Sorted Set ,你可以對(duì) GEO 使用 Sorted Set 相關(guān)的命令。
> ZREM personLocation user1
1
> ZRANGE personLocation 0 -1
user3
user2
> ZSCORE personLocation user2
4069879562983946
應(yīng)用場(chǎng)景
需要管理使用地理空間數(shù)據(jù)的場(chǎng)景
- 舉例:附近的人。
- 相關(guān)命令: GEOADD、GEORADIUS、GEORADIUSBYMEMBER 。
總結(jié)
數(shù)據(jù)類型 | 說(shuō)明 |
Bitmap | 你可以將 Bitmap 看作是一個(gè)存儲(chǔ)二進(jìn)制數(shù)字(0 和 1)的數(shù)組,數(shù)組中每個(gè)元素的下標(biāo)叫做 offset(偏移量)。通過(guò) Bitmap, 只需要一個(gè) bit 位來(lái)表示某個(gè)元素對(duì)應(yīng)的值或者狀態(tài),key 就是對(duì)應(yīng)元素本身 。我們知道 8 個(gè) bit 可以組成一個(gè) byte,所以 Bitmap 本身會(huì)極大的節(jié)省儲(chǔ)存空間。 |
HyperLogLog | Redis 提供的 HyperLogLog 占用空間非常非常小,只需要 12k 的空間就能存儲(chǔ)接近 |
Geospatial index | Geospatial index(地理空間索引,簡(jiǎn)稱 GEO) 主要用于存儲(chǔ)地理位置信息,基于 Sorted Set 實(shí)現(xiàn)。 |
參考資料
[1]Redis Data Structures: https://redis.com/redis-enterprise/data-structures/
[2]Redis Data types tutorial: https://redis.io/docs/manual/data-types/data-types-tutorial/
[3]線性數(shù)據(jù)結(jié)構(gòu) :數(shù)組、鏈表、棧、隊(duì)列: https://javaguide.cn/cs-basics/data-structure/linear-data-structure.html
[4]HyperLogLog 算法的原理講解以及 Redis 是如何應(yīng)用它的: https://juejin.cn/post/6844903785744056333
[5]Sketch of the Day: HyperLogLog — Cornerstone of a Big Data Infrastructure: http://content.research.neustar.biz/blog/hll.html
[6]Redis 到底是怎么實(shí)現(xiàn)“附近的人”這個(gè)功能的呢?: https://juejin.cn/post/6844903966061363207