文章目錄
- 前言
- 一 Hash 哈希
- 1. Hash 相關命令
- hset 和 hget
- hexists
- hdel
- hkeys
- hvals
- hgetall
- hmget
- hlen
- hsetnx
- hincrby
- incrbyfloat
- 2. Hash 命令小結
- 3. Hash 內部編碼
- Hash 在緩存中的應用
- 場景介紹
- 緩存方式對比
- 二、List 列表
- 1. LIST總體介紹
- 2. List 普通命令
- lpush
- lpushx
- rpush
- rpushx
- lrange
- lpop
- rpop
- linsert
- lindex
- llen
- lrem
- ltrim
- lset
- 3. List 阻塞版本命令
- blpop
- brpop
- 4. List 命令小結
- 5. List 內部編碼
- List 使用場景
- 1. 消息隊列
- 2. 微博timeline
- 三、Set 集合
- 1.Set 總體介紹
- 2. Set 普通命令
- sadd、 smembers、 sismember
- scard
- spop
- srandmember
- smove
- srem
- 3. 集合間操作
- sinter
- sinterstore
- sunion
- sunionstore
- sdiff
- sdiffstore
- 4. Set 命令小結
- 5. Set 內部編碼
- 6.應用場景
- 四、Zset 有序集合
- 1. Zset 總體認識
- 2. Zset 普通命令
- zadd
- zcard
- zcount
- zrange
- zrevrange
- zrangebyscore
- zpopmax 和 zpopmin
- bzpopmax 和 bzpopmin
- zrank 和 zrevrank
- zscore
- zrem
- zremrangebyrank
- zremrangebyscore
- zincrby
- Zset 集合間操作命令
- zinterstore
- zunionstore
- 4. 命令小結
- 5. 內部編碼
- 6. 應用場景---排行榜系統
- 五、其他類型
- 六、 補充
- 漸進式遍歷
- 數據庫操作
- select
- dbsize
- flushall 和 flushdb
前言
上篇中介紹到了Redis 的 String 類型,本文是下篇,將繼續介紹 Redis 常用數據類型中的 哈希(Hash)** 、列表(List) 、集合(Set) 、有序集合(Sorted Set) 和一些其他類型。
Redis常用數據類型(上)
一 Hash 哈希
Redis 本身就是一種 Hash 結構,同時也提供了 Hash 這種數據類型。
下圖闡述了二者的一些差別,同時也體現了 Hash 和 String 在使用時的差別
Redis 本身的鍵值對,官方稱之為 key-value,為了區分,對于 Redis 的 Hash 數據類型,其鍵值對稱為 field-value 。在這種嵌套結構中我們需要注意 value 的具體含義
1. Hash 相關命令
hset 和 hget
這是一對最核心的命令,分別用于設置鍵值對和獲取 field 對應的 value
# 支持一次設置多組值
HSET key field value [field value ...]# 注意這里先指定 key 再指定 field
HGET key field
返回值:hset 返回添加的對數,hget 返回指定 filed 對應的值
示例
127.0.0.1:6379> hset key f1 111
(integer) 1
127.0.0.1:6379> hset key f2 222 f3 333 f4 444
(integer) 3
127.0.0.1:6379> hget key f1
"111"
127.0.0.1:6379> hget key f4
"444"
hexists
判斷是否存在對應字段
hexists key filed
存在則返回 1,不存在返回 0
語法:
# 接著上一個例子,已經存在 f1, f2, f3, f4
127.0.0.1:6379> HEXISTS key f1
(integer) 1
127.0.0.1:6379> HEXISTS key f2
(integer) 1
127.0.0.1:6379> HEXISTS key f100
(integer) 0
hdel
刪除 hash 中指定的字段,支持刪除多個
返回刪除成功的個數
# 接著上一個例子,已經存在 f1, f2, f3, f4
127.0.0.1:6379> hdel key f1 f100
(integer) 1
127.0.0.1:6379> hget key f1
(nil)
hkeys
獲取指定 key 對應哈希表中 所有 field
hkey key
示例:
127.0.0.1:6379> hkeys key
1) "f2"
2) "f3"
3) "f4"
hkeys 有風險!當 key 對應的hash表數據過多,就會阻塞 redis 服務器
hvals
獲取指定 key 對應 hash 表中 所有 value
hvals key
示例:
127.0.0.1:6379> hvals key
1) "222"
2) "333"
3) "444"
hgetall
獲取指定 key 對應 hash 表中 所有 field-value 對
hgetall key
示例:
127.0.0.1:6379> hgetall key
1) "f2"
2) "222"
3) "f3"
4) "333"
5) "f4"
6) "444"
hmget
一次獲取多個 field 對應的值
返回值:返回對應字段的值或者nill
hmget key field [field ...]
示例:
127.0.0.1:6379> hmget key f1 f2 f3 f4
1) (nil)
2) "222"
3) "333"
4) "444"
在使? HGETALL 時,如果哈希元素個數?較多,會存在阻塞 Redis 的可能。如果開發?員只需要獲取部分 field,可以使? HMGET,如果?定要獲取全部 field,可以嘗試使? HSCAN命令,該命令采?漸進式遍歷哈希類型,HSCAN 會在后續章節介紹。
hlen
獲取 hash 中所有字段的個數
hlen key
示例:
127.0.0.1:6379> hlen key
(integer) 3
hsetnx
若 field 存在 設置鍵值對,否則 不設置
語法:
hsetnx key field value
返回 1 表示設置成功,返回 0 表示失敗
示例:
127.0.0.1:6379> HSETNX myhash field "Hello"
(integer) 1
127.0.0.1:6379> HSETNX myhash field "Hello"
(integer) 0
hincrby
Hash 中的 value 也可以當成整數
HINCRBY key field increment
對 Hash 中 field 對應的 value 值 加一
返回值:變化后的值,不是整數則報錯
示例:
redis> HSET myhash field 5
(integer) 1
redis> HINCRBY myhash field 1
(integer) 6
redis> HINCRBY myhash field -1
(integer) 5
redis> HINCRBY myhash field -10
(integer) -5
incrbyfloat
HINCRBY 的浮點數版本。
語法:
HINCRBYFLOAT key field increment
示例:
redis> HSET mykey field 10.50
(integer) 1
redis> HINCRBYFLOAT mykey field 0.1
"10.6"
redis> HINCRBYFLOAT mykey field -5
"5.6"
redis> HSET mykey field 5.0e3
(integer) 0
redis> HINCRBYFLOAT mykey field 2.0e2
"5200"
2. Hash 命令小結
命令 | 執行效果 | 時間復雜度 |
---|---|---|
hset key field value | 設置值 | O(1) |
hget key field | 獲取值 | O(1) |
hdel key field [field …] | 刪除 field | O(k), k 是 field 個數 |
hlen key | 計算 field 個數 | O(1) |
hgetall key | 獲取所有的 field - value | O(k), k 是 field 個數 |
hmget field [field …] | 批量獲取 field - value | |
O(k), k 是 field 個數 | ||
hmset field value [field value …] | 批量獲取 field - value | O(k), k 是 field 個數 |
hexists key field | 判斷 field 是否存在 | O(1) |
hkeys key | 獲取所有的 field | O(k), k 是 field 個數 |
hvals key | 獲取所有的 value | O(k), k 是 field 個數 |
hsetnx key field value | 設置值,但必須在 field 不存在時才能設置成功 | O(1) |
hincrby key field n | 對應 field - value +n | O(1) |
hincrbyfloat key field n | 對應 field - value +n | O(1) |
hstrlen key field | 計算 value 的字符串長度 | O(1) |
3. Hash 內部編碼
哈希的內部編碼有兩種:
- ziplist(壓縮列表):當哈希類型元素個數?于 hash-max-ziplist-entries 配置(默認 512 個)、同時所有值都?于 hash-max-ziplist-value 配置(默認 64 字節)時,Redis 會使? ziplist 作為哈希的內部實現,ziplist 使?更加緊湊的結構實現多個元素的連續存儲,所以在節省內存???hashtable 更加優秀。
- hashtable(哈希表):當哈希類型?法滿? ziplist 的條件時,Redis 會使? hashtable 作為哈希的內部實現,因為此時 ziplist 的讀寫效率會下降,? hashtable 的讀寫時間復雜度為 O(1)。
ziplist 的代價就是訪問速度較慢,所以僅在元素個數少且 value 較短時使用
示例:
(1)元素個數較少時,內部編碼采用 ziplist
127.0.0.1:6379> hmset hashkey f1 v1 f2 v2
OK
127.0.0.1:6379> object encoding hashkey
"ziplist"
(2)當有 value 大于 64 字節時,內部編碼會轉換成 hashtable:
127.0.0.1:6379> hset hashkey f3 "超過64字節 .. 省略"
OK
127.0.0.1:6379> object encoding hashkey
"hashtable
(3)當 field 個數超過 512 時,內部編碼也會轉換為 hashtable:
127.0.0.1:6379> hmset hashkey f1 v1 h2 v2 f3 v3 ... 省略 ... f513 v513
OK
127.0.0.1:6379> object encoding hashkey
"hashtable"
Hash 在緩存中的應用
場景介紹
前面提到 String 也可用作緩存場景中,但是在存儲結構化數據上,使用 Hash 更優優勢
使用關系型數據庫存儲:
使用redis 存儲
- 哈希類型是稀疏的,?關系型數據庫是完全結構化的,例如哈希類型每個鍵可以有不同的 field,?關系型數據庫?旦添加新的列,所有?都要為其設置值,即便沒有,也會默認設置nil,如下圖所示
- 關系數據庫可以做復雜的關系查詢,? Redis 去模擬關系型復雜查詢,例如聯表查詢、聚合查詢等基本不可能,維護成本?。
緩存方式對比
緩存方式 | 實現方式示例 | 核心優勢 | 主要不足 | 適用場景 |
---|---|---|---|---|
原生字符串類型 | 每個屬性單獨設鍵(如set user:1:name James ) | 實現極簡,單個屬性操作靈活 | 鍵數量過多,內存占用大,數據分散無內聚性 | 幾乎不推薦,僅臨時調試場景可能使用 |
序列化字符串類型(如JSON) | 整體序列化存儲(如set user:1 '{"name":"James"}' ) | 數據內聚性好,整體操作高效,內存利用率高 | 序列化/反序列化有性能開銷,局部操作不靈活 | 以整體讀寫為主的場景(如用戶信息完整展示) |
哈希類型 | 集中存儲鍵值對(如hmset user:1 name James age 23 ) | 兼顧簡單性與靈活性,局部操作高效 | 需控制內部編碼轉換,可能引發內存消耗波動 | 頻繁進行局部屬性讀寫的場景(推薦首選) |
2.3.5 緩存方式對比
目前,緩存用戶信息可采用以下三種方式,其實現方法及優缺點分析如下:
- 原生字符串類型——以字符串類型存儲,每個屬性對應一個鍵
set user:1:name James
set user:1:age 23
set user:1:city Beijing
優點:實現簡單,對單個屬性的變更操作靈活。
缺點:占用鍵數量過多,內存消耗較大;用戶信息在Redis中存儲分散,缺乏內聚性,實際實用性較低。
- 序列化字符串類型(如JSON格式)
set user:1 經過序列化 的用戶對象字符串
優點:適合整體操作的信息場景適配性好,編程實現簡單;若選擇合適的序列化方案,內存使用效率較高。
缺點:存在序列化與反序列化的性能開銷,對單個屬性的操作靈活性差。
- 哈希類型
hmset user:1 name James age 23 city Beijing
優點:實現簡單、直觀,靈活性強,尤其適合信息的局部變更或獲取操作。
缺點:需控制哈希在ziplist和hashtable兩種內部編碼間的轉換,可能導致較大的內存消耗。
二、List 列表
1. LIST總體介紹
- 列表類型用來存儲多個字符串,每個字符串可以稱為列表的一個元素,一個列表最多存儲 232?12^{32}-1232?1 個元素。
- 在 Redis 中,可以對列表兩端插?(push)和彈出(pop),還可以獲取指定范圍的元素列表、獲取指定索引下標的元素等(參考下圖)
- 由于列表的兩端插入這種靈活特性,它還可以充當棧和隊列的角色,在實際開發中有很多應用
列表的插入和彈出:
列表的獲取和刪除
列表元素特點:
- 列表元素的位置確定,支持通過下標直接獲取。例如我們可以通過
lindex key 4
獲取列表的第 4 個元素 - 如上圖,刪除第二個元素,后面的三個元素均會左移一位
- 元素允許重復
Redis 的列表在功能上可類比 c++ 中的雙端隊列 deque,左右插入都是O(1) 常量級復雜度;隨機訪問的話需要從頭遍歷值訪問的下標,是線性復雜度
2. List 普通命令
lpush
將一個或多個元素從左側放入列表中(頭插)
時間復雜度:O(k)(k 為插入元素個數,一般直接視為O(1))
LPUSH key element [element ...]
返回值:插入后 list 的長度
127.0.0.1:6379> lpush mylist "world"
(integer) 1
127.0.0.1:6379> lpush mylist "hello"
(integer) 2
127.0.0.1:6379> Lrange mylist 0 -1
1) "hello"
2) "world"
lpushx
當 key 存在時,將一個或多個元素從左側放入列表中(頭插),如果 key 不存在直接返回
返回值:插入 list 的長度
時間復雜度:O(k)(k 為插入元素個數,一般直接為O(1))
127.0.0.1:6379> lpush mylist1 world
(integer) 1
127.0.0.1:6379> lpushx mylist1 Hello
(integer) 2
127.0.0.1:6379> lpushx otherlist hello
(integer) 0
127.0.0.1:6379> lrange mylist 0 -1
1) "hello"
2) "world"
rpush
將一個或多個元素從右側放入列表中(尾插)
RPUSH key element [element ...]
返回值插入后列表的長度:
時間復雜度:O(k) (k 為插入元素個數,一般直接為O(1))
rpushx
在 key 存在時,將?個或者多個元素從右側放?(尾插)到 list 中。
語法:
RPUSHX key element [element ...]
返回值:插入后 list 的長度
時間復雜度:O(N) (N 為插入元素個數,一般插入元素較少,可視為O(1))
示例:
redis> RPUSH mylist "World"
(integer) 1
redis> RPUSHX mylist "Hello"
(integer) 2
redis> RPUSHX otherlist "Hello"
(integer) 0
redis> LRANGE mylist 0 -1
1) "World"
2) "Hello"
redis> LRANGE otherlist 0 -1
(empty array)
lrange
獲取從 start 到 end 區間的所有元素,左閉右閉。
語法:
LRANGE key start stop
最壞時間復雜度:O(N)
返回值:指定區間的元素。
示例:
127.0.0.1:6379> lrange mylist 0 0
1) "hello"
127.0.0.1:6379> lrange mylist -3 2
2) "hello"
3) "world"
127.0.0.1:6379> lrange mylist -100 100
4) "hello"
5) "world"
127.0.0.1:6379> lrange mylist 4 6
(empty array)
lpop
從 list 左側取出數據(頭刪)
語法:
lop key
返回值:取出的元素或者 nil
時間復雜度:O(1)
示例:
127.0.0.1:6379> rpush mylist one two three four five
(integer) 5
127.0.0.1:6379> lpop mylist
"one"
127.0.0.1:6379> lpop mylist
"two"
127.0.0.1:6379> lpop mylist
"three"
127.0.0.1:6379> lrange mylist 0 -1
1) "four"
2) "five"
rpop
從右側取出數據
語法:
rpop key
返回值:取出的元素或者 nil
時間復雜度: O(1)
示例:
127.0.0.1:6379> rpush mylist one two three four five
(integer) 5
127.0.0.1:6379> rpop mylist
"five"
127.0.0.1:6379> rpop mylist
"four"
127.0.0.1:6379> rpop mylist
"three"
linsert
根據提供的值查找位置,然后插入元素
LINSERT key <BEFORE | AFTER> pivot element
返回值:插?后的 list ?度。
時間復雜度:O(N)
示例:
redis> RPUSH mylist "Hello"
(integer) 1
redis> RPUSH mylist "World"
(integer) 2
redis> LINSERT mylist BEFORE "World" "There"
(integer) 3
redis> LRANGE mylist 0 -1
1) "Hello"
2) "There"
3) "World"
lindex
獲取指定下標 index 位置的下標
語法:
lindex key index
時間復雜度:O(N)
返回值:取出的元素或者nil
示例:
127.0.0.1:6379> rpush mylist one two three four five
(integer) 5
127.0.0.1:6379> lindex mylist 0
"one"
127.0.0.1:6379> lindex mylist 5
(nil)
127.0.0.1:6379> lindex mylist -1
"five"
127.0.0.1:6379> lindex mylist 4
"five"
127.0.0.1:6379> lindex mylist -4
"two"
127.0.0.1:6379> lindex mylist -5
"one"
llen
獲取 list 長度
語法:
llen key
返回值:list 的長度
127.0.0.1:6379> lpush mylist1 "world"
(integer) 1
127.0.0.1:6379> llen mylist1
(integer) 1
lrem
刪除 count 個 element 元素
LREM key count element
- count > 0: 從左邊開始找 count 個 等于 element 的元素
- count < 0: 從右邊開始找 ∣count∣|count|∣count∣ 個 等于 element 的元素
- count = 0 : 刪除整個 List 中等于 element 的元素
時間復雜度:O(N)
示例:
- 先構造一個列表,內容為 a b a b a b a b
127.0.0.1:6379> rpush mylist a b
(integer) 2
127.0.0.1:6379> rpush mylist a b
(integer) 4
127.0.0.1:6379> rpush mylist a b
(integer) 6
127.0.0.1:6379> rpush mylist a b
(integer) 8
127.0.0.1:6379> lrange mylist 0 -1
1) "a"
2) "b"
3) "a"
4) "b"
5) "a"
6) "b"
7) "a"
8) "b"
- 測試lrem 操作:
127.0.0.1:6379> lrem mylist 2 a
(integer) 2
127.0.0.1:6379> lrange mylist 0 -1
1) "b"
2) "b"
3) "a"
4) "b"
5) "a"
6) "b"
127.0.0.1:6379> lrem mylist -2 a
(integer) 2
127.0.0.1:6379> lrange mylist 0 -1
7) "b"
8) "b"
9) "b"
10) "b"
127.0.0.1:6379> lrem mylist 0 b
(integer) 4
127.0.0.1:6379> lrange mylist 0 -1
(empty array)
ltrim
LTRIM key start stop
僅保留 【start, stop】閉區間中的元素,其余直接刪除,注意下標從0開始
返回值:start, stop 區間內的元素
時間復雜度:O(N)
示例:
127.0.0.1:6379> rpush mylist1 a b c d e f g h
(integer) 8
127.0.0.1:6379> ltrim mylist1 2 5
OK
127.0.0.1:6379> lrange mylist1 0 -1
1) "c"
2) "d"
3) "e"
4) "f"
lset
lset key index element
修改 index 位置的元素為 element
時間復雜度:O(N)
返回值:成功返回 OK,下標超出范圍會報錯
1) "c"
2) "d"
3) "e"
4) "f"
127.0.0.1:6379> lset mylist1 2 "hello world"
OK
127.0.0.1:6379> lrange mylist1 0 -1
5) "c"
6) "d"
7) "hello world"
8) "f"
127.0.0.1:6379> lset mylist1 4 "1"
(error) ERR index out of range
3. List 阻塞版本命令
blpop 和 brpop 是 lpop 和 rpop 的阻塞版本:
- 在列表中有元素的情況下,阻塞和非阻塞表現是一致的。
- 如果列表中沒有元素,執行阻塞版本命令的客戶端會根據timeout 阻塞一段時間,期間redis 服務器能正常處理其他客戶端的命令
- 等待過程中,列表中一旦有元素立即返回
- 若 Redis 中設置了多個鍵,那么就會從左向右進行遍歷,一旦有一個鍵對應的列表中可以彈出元素,命令立即返回
blpop
lpop 的阻塞版本
語法:
blpop key [key ...] timeout
時間復雜度:O(1)
返回值:取出的數據或者nil
brpop
brpop 的 阻塞版本
語法:
brpop key [key ...] timeout
用法與 blpop 一致,此處不再贅述
brpop 和 blpop 適合用于阻塞隊列
4. List 命令小結
操作類型 | 命令 | 時間復雜度 |
---|---|---|
添加 | rpush key value [value ...] | O(k)O(k)O(k),kkk 是元素個數 |
添加 | lpush key value [value ...] | O(k)O(k)O(k),kkk 是元素個數 |
添加 | linsert key before | after pivot value | O(n)O(n)O(n),nnn 是 pivot 距離頭尾的距離 |
查找 | lrange key start end | O(s+n)O(s+n)O(s+n),sss 是 start 偏移量,nnn 是 start 到 end 的范圍 |
查找 | lindex key index | O(n)O(n)O(n),nnn 是索引的偏移量 |
查找 | llen key | O(1)O(1)O(1) |
刪除 | lpop key | O(1)O(1)O(1) |
刪除 | rpop key | O(1)O(1)O(1) |
刪除 | lrem key count value | O(k)O(k)O(k),kkk 是列表長度 |
刪除 | ltrim key start end | O(k)O(k)O(k),kkk 是列表長度 |
修改 | lset key index value | O(k)O(k)O(k),kkk 是索引的偏移量 |
阻塞操作 | blpop brpop | O(1)O(1)O(1) |
5. List 內部編碼
對于舊版本 Redis (Redis 3.2 之前):
- ziplist(壓縮列表):元素個數少時使用。這種結構更為緊湊,但訪問效率較低,所以僅個數少時使用。
- linkedlist(鏈表):當列表類型?法滿? ziplist 的條件時,Redis 會使? linkedlist 作為列表的內部實現。
Redis 3.2 之后,采用 quicklist 作為 List 的底部編碼
quicklist 時 ziplist 和 linkedlist 的結合,也就是 linkedlist 的每個節點是 ziplist 。
在 /etc/redis/redis.conf 文件中,存在一個配置項 list-max-ziplist-size 表示單個壓縮列表允許的最大字節數。
List 使用場景
普通消息隊列
1. 消息隊列
如下圖所示,生產通過 lpush 放入元素,多個消費者通過 brpop 排隊阻塞等待元素。這多個消費者客戶端就直接能保證負載均衡和高可用
分頻道消息隊列
由于 brpop 支持等待多個鍵的特性,我們可以直接基于此實現一個分頻道消息隊列,實現訂閱不同頻道的效果,如下圖:
2. 微博timeline
每個用戶都有自己的 timeline (微博列表),現需要按照分頁來展示文章列表。此時可考慮使用列表,列表順序固定,且支持按照索引獲取元素
- 每篇微博使用 hash 結構存儲
hmset mblog:1 title xx timestamp 1476536196 content xxxxx
...
hmset mblog:n title xx timestamp 1476536196 content xxxxx
- 向用戶的 timeline 中添加微博,user:uid:mblogs作為鍵
lpush user:1:mblogs mblog:1 mblog:3
...
lpush user:k:mblogs mblog:9
- 分頁獲取用戶的 timeline,例如獲取用戶1的前十篇微博
keylist = lrange user:1:mblogs 0 9
for key in keylist {hgetall key
}
這種方案存在兩個問題:
- 第 3)步中,每篇微博都會執行一次 hgetall,這需要多次網絡請求。更推薦的做法是通過 pipeline 流水線式 打包命令,批量提交,一頁只請求一次。
- 分頁獲取文章是,lrange 在兩端較快,若是在中間獲取較慢,可考慮將列表做拆分
三、Set 集合
1.Set 總體介紹
集合類型也是保存多個字符串類型的元素的,需要注意:
- Set 中元素無序
- Set 中元素不能重復
- 一個集合最多存 232?12^{32}-1232?1 個元素
Redis 對 Set 提供了增刪查改的基本操作,同時還支持取交集、并集、差集。
2. Set 普通命令
sadd、 smembers、 sismember
- sadd : 將一個或多個元素添加到set中。注意,重復的元素無法添加到 set 中
SADD key member [member ...]
- 時間復雜度:O(1)
- 返回值:本次成功添加的元素個數
示例:
redis> SADD myset "Hello"
(integer) 1
redis> SADD myset "World"
(integer) 1
redis> SADD myset "World"
(integer) 0
redis> SMEMBERS myset
1) "Hello"
2) "World"
- smembers : 獲取出集合中的所有元素
- 時間復雜度: O(1)
- 返回值: 所有元素
示例:
redis> SADD myset "Hello"
(integer) 1
redis> SADD myset "World"
(integer) 1
redis> SADD myset "World"
(integer) 0
redis> SMEMBERS myset
1) "Hello"
2) "World"
- sismember : 判斷一個元素在不在 set 中
SISMEMBER key member
- 時間復雜度:O(1)
- 返回值:1表示元素在 set 中,0表示不再
示例:
redis> SADD myset "one"
(integer) 1
redis> SISMEMBER myset "one"
(integer) 1
redis> SISMEMBER myset "two"
(integer) 0
scard
獲取 set 中的元素個數
SCARD key
- 時間復雜度:O(1)
- 返回值:元素個數
示例:
127.0.0.1:6379> sadd myset 1 2 3 4 5
(integer) 5
127.0.0.1:6379> scard myset
(integer) 5
spop
從 set 中隨機刪除并返回一個或多個元素。
spop key [count]
- 時間復雜度 : O(k) k 是count
- 返回值:取出的元素
示例:
127.0.0.1:6379> smembers myset
1) "1"
2) "2"
3) "3"
4) "4"
5) "5"
127.0.0.1:6379> spop myset 3
6) "2"
7) "3"
8) "1"
127.0.0.1:6379> smembers myset
9) "4"
10) "5"
127.0.0.1:6379> spop myset 1
11) "4"
srandmember
隨機取一個或多個元素
SRANDMEMBER key [count]
- 當 count 為正數時:
返回集合中不重復的隨機元素,數量為 count(若集合元素總數小于 count,則返回所有元素) - 當 count 為負數時:
返回的隨機元素可能重復,數量為 count 的絕對值(即使集合元素總數小于該絕對值,也會返回指定數量的元素,允許重復) - 當 count 未指定時:
默認返回 1 個隨機元素(不重復)
- 時間復雜度:O(N) N 為count
smove
將一個元素從 源 set中取出放入目標 set
smove source destination member
- 時間復雜度:O(1)
- 1表示移動成功,0表示失敗
如果 destination 集合中有指定元素,則不會認為移動失敗,效果相當于從source中刪除指定元素,當然如果 source中沒有member,則會返回0認為移動失敗
示例:
redis> SADD myset "one"
(integer) 1
redis> SADD myset "two"
(integer) 1
redis> SADD myotherset "three"
(integer) 1
redis> SMOVE myset myotherset "two"
(integer) 1
redis> SMEMBERS myset
1) "one"redis> SMEMBERS myotherset
1) "three"
2) "two
srem
將指定元素刪除
SREM key member [member ...]
- 時間復雜度 O(N) N為總元素個數
- 返回值:本次操作刪除的元素個數
示例:
redis> SADD myset "one"
(integer) 1
redis> SADD myset "two"
(integer) 1
redis> SADD myset "three"
(integer) 1
redis> SREM myset "one"
(integer) 1
redis> SREM myset "four"
(integer) 0
redis> SMEMBERS myset
1) "three"
2) "two"
3. 集合間操作
交集、并集、差集
sinter
獲取指定集合的交集
SINTER key [key ...]
- 時間復雜度:O(N * M), N 是最?的集合元素個數. M 是最?的集合元素個數.
- 返回值:交集的元素
127.0.0.1:6379> sadd key 1 2 3 4
(integer) 4
127.0.0.1:6379> sadd key2 3 4 5 6
(integer) 4
127.0.0.1:6379> sinter key key2
1) "3"
2) "4"
sinterstore
求交集,并把交集元素放入一個新創建的 集合中
SINTERSTORE destination key [key ...]
- 時間復雜度:O(N * M), N 是最?的集合元素個數. M 是最?的集合元素個數.
- 返回值:交集的元素個數
示例:
127.0.0.1:6379> sadd key 1 2 3 4
(integer) 4
127.0.0.1:6379> sadd key2 3 4 5 6
(integer) 4
127.0.0.1:6379> SINTERSTORE inter_set key key2
(integer) 2
127.0.0.1:6379> SMEMBERS inter_set
1) "3"
2) "4"
sunion
獲取指定集合的并集
SUNION key [key ...]
- 時間復雜度:O(N ), 總元素個數.
- 返回值:并集的元素
示例:
127.0.0.1:6379> sunion key key2
1) "1"
2) "2"
3) "3"
4) "4"
5) "5"
6) "6"
sunionstore
求并集,并把并集元素放入一個新創建的 集合中
SINTERSTORE destination key [key ...]
- 時間復雜度:O(N),總元素個數
- 返回值:并集的元素個數
127.0.0.1:6379> sunionstore union_key key key2
(integer) 6
127.0.0.1:6379> SMEMBERS union_key
1) "1"
2) "2"
3) "3"
4) "4"
5) "5"
6) "6"
sdiff
獲取給定 set 的元素個數
語法:
sdiff key [key ...]
時間復雜度:O(N),N給定的集合的總的元素個數
返回值:差集的元素
127.0.0.1:6379> sadd key2 3 4 5
(integer) 3
127.0.0.1:6379> sdiff key1 key2
1) "1"
2) "2"
127.0.0.1:6379> sdiff key2 key1
3) "5"
sdiffstore
求差集,并把差集元素放入一個新創建的 集合中
SDIFFSTORE destination key [key ...]
- O(N), N 給定的所有集合的總的元素個數
- 返回值:差集的元素個數。
4. Set 命令小結
以下是轉換后的 Markdown 格式:
命令 | 時間復雜度 |
---|---|
sadd key element [element …] | O(k),k 是元素個數 |
srem key element [element …] | O(k),k 是元素個數 |
scard key | O(1) |
sismember key element | O(1) |
srandmember key [count] | O(n),n 是 count |
spop key [count] | O(n), n 是 count |
smembers key | O(k),k 是元素個數 |
sinter key [key …] sitnerstore | O(m * k),k 是幾個集合中元素最小的個數,m 是鍵個數 |
sunion key [key …] sunionstore | O(k),k 是多個集合的元素個數總和 |
sdiff key [key …] sdiffstore | O(k),k 是多個集合的元素個數總和 |
5. Set 內部編碼
集合的編碼有兩種:
- intset (整數集合):當集合中的元素都是整數并且元素的個數?于 set-max-intset-entries 配置(默認 512 個)時,Redis 會選? intset 來作為集合的內部實現,從?減少內存的占用。
- hashtable(哈希表):當集合類型?法滿? intset 的條件時,Redis 會使? hashtable 作為集合的內部實現。
6.應用場景
用戶興趣標簽
場景:給用戶打興趣標簽(如 “籃球”“音樂”“閱讀”),用于個性化推薦。
實現:
- 用 Set 存儲用戶的興趣標簽:SADD user:10086:interests 籃球 音樂
推薦邏輯: - 計算用戶興趣交集:SINTER user:10086:interests user:10010:interests(找到與用戶 10086 興趣相似的用戶 10010)
- 還可以通過交集計算用戶群體的共同標簽
- 基于標簽推送內容:SUNION tag:籃球 tag:音樂(獲取用戶可能感興趣的內容 ID)
四、Zset 有序集合
1. Zset 總體認識
有序集合保留了 Set 的元素不能重復特性,并在此基礎上給每個元素引入了 分數(score),Zset 會基于分數對元素進行升序排序,這就是 Zset 的有序特性。
Zset 提供了獲取指定分數和元素范圍查找、計算成員排名等功能,合理利用 Zset 可以幫助我們在實際開發中解決很多問題
Zset 元素不能重復,但不同元素的分數可以一樣
關于有序概念在不同上下文中的理解
數據結構 | 是否允許重復元素 | 是否有序 | 有序依據 | 應用場景 |
---|---|---|---|---|
列表 | 是 | 是 | 索引下標 | 時間軸、消息隊列等 |
集合 | 否 | 否 | 標簽、社交等 | |
有序集合 | 否 | 是 | 分數 | 排行榜系統、社交等 |
2. Zset 普通命令
zadd
- 添加或者更新指定的元素以及關聯的分數到 zset 中,可添加多對。
- 分數應該符合 double 類型,+inf/-inf 作為正負極限也是合法的。
語法:
ZADD key [NX | XX] [GT | LT] [CH] [INCR] score member [score member ...]
選項介紹:
- XX 表示僅在member 存在的情況下更新元素的分數;
- NX 表示僅在 member 不存在的情況下添加新元素和分數
- CH:默認情況下,ZADD 返回的是本次添加的元素個數,但指定這個選項之后,就會還會包含本次更新的元素的個數。
- INCR:此時命令類似 ZINCRBY 的效果,將元素的分數加上指定的分數。此時只能指定?個元素和分數。
- LT : less than 的縮寫,表示僅在分數比原來小的時候更新分數
- GT : greater than 的縮寫,表示僅在分數比原來大的時候更新分數
時間復雜度:O(log(N)) N 是 Zset 的總元素個數
返回值:本次添加成功的元素個數
示例:
- 不加選項(需要注意的就是每一對元素中,分數在前,名稱在后)
127.0.0.1:6379> zadd my_zset 99 Tom 99 Jerry 97 David 95 Tim
(integer) 4
127.0.0.1:6379> zrange 0 -1
1) "Tim"
2) "David"
3) "Jerry"
4) "Tom"127.0.0.1:6379> zrange my_zset 0 -1 withscores
1) "Tim"
2) "95"
3) "David"
4) "97"
5) "Jerry"
6) "99"
7) "Tom"
8) "99"127.0.0.1:6379> zadd my_zset 90 Tom
(integer) 0
127.0.0.1:6379> zrange my_zset 0 -1
1) "Tom"
2) "Tim"
3) "David"
4) "Jerry"
- XX 和 NX
127.0.0.1:6379> zadd my_zset NX 0 Jerry
(integer) 0
127.0.0.1:6379> zrange my_zset 0 -1 withscores
1) "Tom"
2) "90"
3) "Tim"
4) "95"
5) "David"
6) "97"
7) "Jerry"
8) "99"
127.0.0.1:6379> zadd my_zset XX 0 Jerry
(integer) 0127.0.0.1:6379> zrange my_zset 0 -1 withscores
1) "Jerry"
2) "0"
3) "Tom"
4) "90"
5) "Tim"
6) "95"
7) "David"
8) "97"127.0.0.1:6379> zadd my_zset XX 100 zhangsan
(integer) 0
127.0.0.1:6379> zrange my_zset 0 -1 withscores
1) "Jerry"
2) "0"
3) "Tom"
4) "90"
5) "Tim"
6) "95"
7) "David"
8) "97"127.0.0.1:6379> zadd my_zset NX 100 zhangsan
(integer) 1
127.0.0.1:6379> zrange my_zset 0 -1 withscores1) "Jerry"2) "0"3) "Tom"4) "90"5) "Tim"6) "95"7) "David"8) "97"9) "zhangsan"
10) "100"
- CH選項
127.0.0.1:6379> zadd my_zset 100 Tom
(integer) 0
127.0.0.1:6379> zadd my_zset CH 100 Jerry
(integer) 1
- INCR
127.0.0.1:6379> zrange my_zset 0 -1 withscores1) "David"2) "97"3) "Jerry"4) "100"5) "Tim"6) "100"7) "Tom"8) "100"9) "zhangsan"
10) "100"
127.0.0.1:6379> zadd my_zset incr 100 Tim
"200"
127.0.0.1:6379> zrange my_zset 0 -1 withscores11) "David"12) "97"13) "Jerry"14) "100"15) "Tom"16) "100"17) "zhangsan"18) "100"19) "Tim"
20) "200"
zcard
獲取 zset 中的元素個數
ZCARD key
- 時間復雜度:O(1)
- 返回值:zset 內的元素個數。
127.0.0.1:6379> zrange my_zset 0 -1 withscores1) "David"2) "97"3) "Jerry"4) "100"5) "Tim"6) "100"7) "Tom"8) "100"9) "zhangsan"
10) "100"
127.0.0.1:6379> zcard my_zset
(integer) 5
zcount
返回分數在 min 和 max 之間的元素個數,默認情況下,min 和 max 都是包含的。通過 ( 可以改為開區間
ZCOUNT key min max
- 時間復雜度:O(log(N))
- 返回值:滿足條件的元素列表個數
示例:
127.0.0.1:6379> zrange my_zset 0 -1 withscores1) "David"2) "97"3) "Jerry"4) "100"5) "Tom"6) "100"7) "zhangsan"8) "100"9) "Tim"
10) "200"
127.0.0.1:6379> zcount my_zset 100 200
(integer) 4
127.0.0.1:6379> zcount my_zset (100 200
(integer) 1
127.0.0.1:6379> zcount my_zset (100 (200
(integer) 0
zrange
返回指定區間?的元素,分數按照升序。帶上 WITHSCORES 可以把分數也返回。
ZRANGE key start stop [WITHSCORES]
此處的 [start, stop] 為下標構成的閉區間. 從 0 開始, ?持負數.
時間復雜度:O(log(N)+M N 是 Zset 總元素個數,M是區間內元素個數
返回值:區間內的元素列表。
zrevrange
返回指定區間?的元素,分數按照降序。帶上 WITHSCORES 可以把分數也返回。
ZREVRANGE key start stop [WITHSCORES]
備注:這個命令在 6.2.0 之后廢棄,并且功能合并到 ZRANGE 中。本博客僅針對Redis 6.0 所以不做過多討論
時間復雜度:O(log(N)+M)
返回值:區間內的元素列表。
zrangebyscore
返回分數在 min 和 max 之間的元素,默認情況下,min 和 max 都是包含的,可以通過 ( 來改成開區間。
備注:這個命令在 6.2.0 之后廢棄,并且功能合并到 ZRANGE 中。
語法:
ZRANGEBYSCORE key min max [WITHSCORES]
時間復雜度:O(log(N)+M)
返回值:區間內的元素列表。
示例:
127.0.0.1:6379> zrangebyscore my_zset 0 100 withscores
1) "David"
2) "97"
3) "Jerry"
4) "100"
5) "Tom"
6) "100"
7) "zhangsan"
8) "100"
zpopmax 和 zpopmin
- zpopmax : 刪除并返回分數最?的 count 個元素。
ZPOPMAX key [count]
- 時間復雜度:O(log(N) * M)
- 返回值:分數和元素列表。
優先刪除字典序更低的元素
示例:
redis> ZADD myzset 1 "one"
(integer) 1
redis> ZADD myzset 2 "two"
(integer) 1
redis> ZADD myzset 3 "three"
(integer) 1
redis> ZPOPMAX myzset
1) "three"
- zpopmin :刪除并返回分數最低的 count 個元素。
ZPOPMIN key [count]
- 時間復雜度:O(log(N) * M)
- 返回值:分數和元素列表。
分數相同,優先刪除字典序低的
redis> ZADD myzset 1 "one"
(integer) 1
redis> ZADD myzset 2 "two"
(integer) 1
redis> ZADD myzset 3 "three"
(integer) 1
redis> ZPOPMIN myzset
1) "one"
2) "1"
bzpopmax 和 bzpopmin
這是 zpopmax 和 zpopmin 的阻塞版本((僅支持彈出一個元素,可阻塞等待多個 key ),可用于實現阻塞優先級隊列。
- BZPOPMAX
1 BZPOPMAX key [key ...] timeout
- 時間復雜度:O(log(N))
- 返回值:元素列表。
?例:
redis> DEL zset1 zset2
(integer) 0
redis> ZADD zset1 0 a 1 b 2 c
(integer) 3
redis> BZPOPMAX zset1 zset2 0
1) "zset1"
2) "c"
3) "2"
- ZPOPMIN
BZPOPMIN key [key ...] timeout
- 時間復雜度:O(log(N))
- 返回值:元素列表。
示例:
redis> ZADD myzset 1 "one"
(integer) 1
redis> ZADD myzset 2 "two"
(integer) 1
redis> ZADD myzset 3 "three"
(integer) 1
redis> ZPOPMIN myzset
1) "one"
2) "1
zrank 和 zrevrank
- zrank : 返回指定元素按照升序的排名
ZRANK key member
- 時間復雜度:O(log(N))
- 返回值:排名。
redis> ZADD myzset 1 "one"
(integer) 1
redis> ZADD myzset 2 "two"
(integer) 1
redis> ZADD myzset 3 "three"
(integer) 1
redis> ZRANK myzset "three"
(integer) 2
redis> ZRANK myzset "four"
(nil)
- zrevrank:返回指定元素的排名,降序。
zrevrank key member
- 時間復雜度:O(log(N))
- 返回值:排名。
redis> ZADD myzset 1 "one"
(integer) 1
redis> ZADD myzset 2 "two"
(integer) 1
redis> ZADD myzset 3 "three"
(integer) 1
redis> ZREVRANK myzset "one"
(integer) 2
redis> ZREVRANK myzset "four"
(nil)
zscore
返回指定元素的分數
ZSCORE key member
- 時間復雜度:O(1),redis對此做了特殊優化
- 返回值:分數。
示例:
redis> ZADD myzset 1 "one"
(integer) 1
redis> ZSCORE myzset "one"
"1"
zrem
刪除指定的元素
ZREM key member [member ...]
- 時間復雜度:O(M * log(N)) (M 是刪除的元素個數)
- 返回值:本次操作刪除的元素個數。
示例:
redis> ZADD myzset 1 "one"
(integer) 1
redis> ZADD myzset 2 "two"
(integer) 1
redis> ZADD myzset 3 "three"
(integer) 1
redis> ZREM myzset "two"
(integer) 1
redis> ZRANGE myzset 0 -1 WITHSCORES
1) "one"
2) "1"
3) "three"
4) "3"
zremrangebyrank
按照升序刪除指定范圍的元素,閉區間
ZREMRANGEBYRANK key start stop
- 時間復雜度:O(log(N)+M) M 是被刪除的元素個數
- 返回值:本次操作刪除的元素個數。
redis> ZADD myzset 1 "one"
(integer) 1
redis> ZADD myzset 2 "two"
(integer) 1
redis> ZADD myzset 3 "three"
(integer) 1
redis> ZREMRANGEBYRANK myzset 0 1
(integer) 2
redis> ZRANGE myzset 0 -1 WITHSCORES
1) "three"
2) "3"
zremrangebyscore
按照分數刪除指定范圍的元素,左閉右閉。
ZREMRANGEBYSCORE key min max
- 時間復雜度:O(log(N)+M)
- 返回值:本次操作刪除的元素個數。
redis> ZADD myzset 1 "one"
(integer) 1
redis> ZADD myzset 2 "two"
(integer) 1
redis> ZADD myzset 3 "three"
(integer) 1
redis> ZREMRANGEBYSCORE myzset -inf (2
(integer) 1
redis> ZRANGE myzset 0 -1 WITHSCORES
1) "two"
2) "2"
3) "three"
4) "3"
zincrby
為指定的元素的關聯分數加上指定的分數值。
ZINCRBY key increment member
- 時間復雜度:O(log(N))
- 返回值:增加后元素的分數
Zset 集合間操作命令
交集
zinterstore
求出給定有序集合中元素的交集并保存進?標有序集合中,在合并過程中以元素為單位進?合并,元素對應的分數按照不同的聚合?式和權重得到新的分數。
ZINTERSTORE destination numkeys key [key ...] [WEIGHTS weight [weight ...]] [AGGREGATE <SUM | MIN | MAX>]
選項介紹:
- numkeys : 必須指定要合并的 key 的數量
- WEIGHTS:可選,為待合并的key 分配權重
- AGGREGATE :可選 sum | min | max , 分別是按權重求和,乘以權重后取最小值,乘以權重后取最大值。
時間復雜度:O(N?K)+O(M?log(M))O(N*K)+O(M*log(M))O(N?K)+O(M?log(M)) N 是輸?的有序集合中, 最小的有序集合的元素個數; K 是輸入了幾個有序集合; M 是最終結果的有序集合的元素個數
返回值:最終結果的有序集合的元素個數
示例:
zunionstore
求出給定有序集合中元素的并集并保存進?標有序集合中,在合并過程中以元素為單位進?合并,元素對應的分數按照不同的聚合?式和權重得到新的分數。
ZUNIONSTORE destination numkeys key [key ...] [WEIGHTS weight[weight ...]] [AGGREGATE <SUM | MIN | MAX>]
- 時間復雜度:O(N)+O(M?log(M))O(N)+O(M*log(M))O(N)+O(M?log(M)) N 是輸?的有序集合總的元素個數; M 是最終結果的有序集合的元素個數.
- 返回值:?標集合中的元素個數
選項和 zinterstore 一樣
示例:
4. 命令小結
命令 | 時間復雜度 |
---|---|
zadd key score member [score member ...] | O(k?log?(n))O(k \cdot \log(n))O(k?log(n)),kkk 是添加成員的個數,nnn 是當前有序集合的元素個數 |
zcard key | O(1)O(1)O(1) |
zscore key member | O(1)O(1)O(1) |
zrank key member zrevrank key member | O(log?(n))O(\log(n))O(log(n)),nnn 是當前有序集合的元素個數 |
zrem key member [member ...] | O(k?log?(n))O(k \cdot \log(n))O(k?log(n)),kkk 是刪除成員的個數,nnn 是當前有序集合的元素個數 |
zincrby key increment member | O(log?(n))O(\log(n))O(log(n)),nnn 是當前有序集合的元素個數 |
zrange key start end [withscores] zrevrange key start end [withscores] | O(k+log?(n))O(k + \log(n))O(k+log(n)),kkk 是獲取成員的個數,nnn 是當前有序集合的元素個數 |
zrangebyscore key min max [withscores] zrevrangebyscore key max min [withscores] | O(k+log?(n))O(k + \log(n))O(k+log(n)),kkk 是獲取成員的個數,nnn 是當前有序集合的元素個數 |
zcount key min max | O(log?(n))O(\log(n))O(log(n)),nnn 是當前有序集合的元素個數 |
zremrangebyrank key start end | O(k+log?(n))O(k + \log(n))O(k+log(n)),kkk 是獲取成員的個數,nnn 是當前有序集合的元素個數 |
zremrangebyscore key min max | O(k+log?(n))O(k + \log(n))O(k+log(n)),kkk 是獲取成員的個數,nnn 是當前有序集合的元素個數 |
zinterstore destination numkeys key [key ...] | O(n?k)+O(m?log?(m))O(n \cdot k) + O(m \cdot \log(m))O(n?k)+O(m?log(m)),nnn 是輸入的集合最小的元素個數,kkk 是集合個數,mmm 是目標集合元素個數 |
zunionstore destination numkeys key [key ...] | O(n)+O(m?log?(m))O(n) + O(m \cdot \log(m))O(n)+O(m?log(m)),nnn 是輸入集合總元素個數,mmm 是目標集合元素個數 |
5. 內部編碼
有序集合類型的內部編碼有兩種:
- ziplist(壓縮列表):當有序集合的元素個數?于 zset-max-ziplist-entries 配置(默認 128 個),同時每個元素的值都?于 zset-max-ziplist-value 配置(默認 64 字節)時,Redis 會? ziplist 來作為有序集合的內部實現,ziplist 可以有效減少內存的使?。
- skiplist(跳表):當 ziplist 條件不滿?時,有序集合會使? skiplist 作為內部實現,因為此時ziplist 的操作效率會下降。
關于跳表將在后續介紹
6. 應用場景—排行榜系統
在使用 Redis 的有序集合(Zset)維護每天的熱榜(以點贊數為維度)這一場景中,Zset 的特性使其成為一個非常合適的選擇,以下是具體的介紹:
-
初始化熱榜:每天開始時,可以創建一個新的 Zset 來存儲當天的熱榜數據。例如,使用鍵名
hot_list:2024-10-01
來表示 2024年10月1日的熱榜,使用ZADD
命令向其中添加帖子的 ID 作為成員,初始點贊數(假設為0)作為分數。ZADD hot_list:2024-10-01 0 post_1 ZADD hot_list:2024-10-01 0 post_2
-
更新點贊數:當有用戶對帖子進行點贊操作時,通過
ZINCRBY
命令增加對應帖子在 Zset 中的點贊數(分數)。ZINCRBY hot_list:2024-10-01 1 post_1
-
獲取熱榜數據:要獲取當天點贊數排名前10的帖子,可以使用
ZREVRANGE
命令(因為點贊數越高越熱門,所以使用反向排序獲取高分元素)。ZREVRANGE hot_list:2024-10-01 0 9 WITHSCORES
其中
WITHSCORES
參數用于在返回成員的同時返回其對應的分數(點贊數),方便展示。 -
獲取特定帖子的排名:如果想知道某個帖子在熱榜中的排名,可以使用
ZREVRANK
命令。ZREVRANK hot_list:2024-10-01 post_1
該命令會返回帖子
post_1
在熱榜中的排名(從0開始計數)。
五、其他類型
Redis 數據類型
Redis 還提供了其他的一些數據類型,雖然這些類型使用率不高,但在特定場景下使用,還是方便且高效的。
- Streams : 類似于消息隊列(阻塞隊列),支持高效的消息發布、訂閱等功能
- Geospatial : 用于存儲地理坐標的(經緯度)。支持用戶在不同坐標減進行距離查詢等操作
- Hyperloglog : 以極低的空間代價,估算大集合元素的個數(單個 Hyperloglog 12 KB)
- Bitset :位圖,用一個bit表示對應位的數字是否存在
- BitFields :位域類型,位域可以指定每一個元素所占比特位,從而更方便的進行位操作。(類似 c 的位段)
六、 補充
漸進式遍歷
keys * 在生產環境中很危險。
通過漸進式遍歷,就可以做到,既能夠獲取所有的key,同時又不會卡死服務器
以漸進式的方式進行鍵的遍歷
SCAN cursor [MATCH pattern] [COUNT count] [TYPE type]
- 時間復雜度 O(1)
- 下一次scan的的光標(cursor)以及本次得到的鍵
選項: - pattern 同 keys 命令
- count :限制本次遍歷能獲取到多少元素,默認為 10 。count不是精確值,實際返回的個數可能略有出入
- type : 僅返回 value 是對應類型的key
count 并非下標,實際返回的數字我們是無法預測的,我們只知道 0 表示開始以及接著上一次scan 命令返回的count 繼續遍歷
示例:
127.0.0.1:6379> mset k1 1 k2 2 k3 3 k4 4 k5 5 k6 6 k7 7 k8 8 k9 9 k0 0
OK
127.0.0.1:6379> scan 0
1) "15"
2) 1) "k7"2) "k8"3) "k1"4) "k5"5) "k2"6) "k0"7) "k3"8) "k9"9) "k4"3) "k6"127.0.0.1:6379> scan 15
1) "0"
2) (empty array)127.0.0.1:6379> scan 0 count 5
1) "14"
2) 1) "k7"3) "k8"4) "k1"5) "k5"6) "k2"127.0.0.1:6379> scan 14 count 5
1) "15"
2) 1) "k0"3) "k3"4) "k9"5) "k4"6) "k6"127.0.0.1:6379> scan 15 count 5
1) "0"
2) (empty array)
除了 scan 以外,Redis ?向哈希類型、集合類型、有序集合類型分別提供了 hscan、sscan、zscan 命令,它們的?法和 scan 基本類似,感興趣的讀者可以??做擴展學習。
漸進性遍歷 scan 雖然解決了阻塞的問題,但如果在遍歷期間鍵有所變化(增加、修改、刪除),可能導致遍歷時鍵的重復遍歷或者遺漏,這點務必在實際開發中考慮。
數據庫操作
MySQL 中有 database 的概念,一個 MySQL服務器可以有多個 database
Redis 不能創建數據庫,而是本身就提供了16個數據庫(0 - 15),各個數據庫相互獨立。默認操作 0 號數據庫
Redis提供了數據庫相關的命令:dbsize、select、flushdb、flushall 命令。
select
切換數據庫
select dbIndex
各個數據庫之間相互獨立,dbIndex 取 0-15
示例:
127.0.0.1:6379> scan 0
1) "15"
2) 1) "k7"2) "k8"3) "k1"4) "k5"5) "k2"6) "k0"7) "k3"8) "k9"9) "k4"3) "k6"127.0.0.1:6379> select 1
OK
127.0.0.1:6379[1]> scan 0
1) "0"
2) (empty array)
? Redis 中雖然?持多數據庫,但隨著版本的升級,其實不是特別建議使?多數據庫特性。如果真的需要完全隔離的兩套鍵值對,更好的做法是維護多個 Redis 實例,?不是在?個Redis 實例中維護多數據庫。這是因為本? Redis 并沒有為多數據庫提供太多的特性,其次?論是否有多個數據庫,Redis 都是使?單線程模型,所以彼此之間還是需要排隊等待命令的執?。同時多數據庫還會讓開發、調試和運維?作變得復雜。所以實踐中,始終使?數據庫 0 其實是?個很好的選擇。
dbsize
返回當前數據庫鍵的個數
127.0.0.1:6379> dbsize
(integer) 10
127.0.0.1:6379> select 1
OK
127.0.0.1:6379[1]> dbsize
(integer) 0
flushall 和 flushdb
- flushdb : 清空當前數據庫
- flushall : 清空所有數據庫
? 永遠不要在線上環境執?清除數據的操作