Redis 避不開的五種數據結構

Redis 中有 5 種數據結構,分別是字符串(String)、哈希(Hash)、列表(List)、集合(Set)和有序集合(Sorted Set),由於使用 Redis 場景的開發中確定是沒法避開這些基礎結構的,因此熟練掌握它們也就成了一項必不可少的能力。本文章精要地介紹了 Redis 的這幾種數據結構,主要覆蓋了它們各自的定義、基本用法與相關要點。前端

字符串類型

字符串是 Redis 中的最基礎的數據結構,咱們保存到 Redis 中的 key,也就是鍵,就是字符串結構的。除此以外,Redis 中其它數據結構也是在字符串的基礎上設計的,可見字符串結構對於 Redis 是多麼重要。python

Redis 中的字符串結構能夠保存多種數據類型,如:簡單的字符串、JSON、XML、二進制等,但有一點要特別注意:在 Redis 中字符串類型的值最大隻能保存 512 MB。redis

命令

下面經過命令瞭解一下對字符串類型的操做:數據庫

1.設置值

set key value [EX seconds] [PX milliseconds] [NX|XX]

set 命令有幾個非必須的選項,下面咱們看一下它們的具體說明:編程

  • EX seconds:爲鍵設置秒級過時時間
  • PX milliseconds:爲鍵設置毫秒級過時時間
  • NX:鍵必須不存在,才能夠設置成功,用於添加
  • XX:鍵必須存在,才能夠設置成功,用於更新

set 命令帶上可選參數 NX 和 XX 在實際開發中的做用與 setnx 和 setxx 命令相同。咱們知道 setnx 命令只有當 key 不存在的時候才能設置成功,換句話說,也就是同一個 key 在執行 setnx 命令時,只能成功一次,而且因爲 Redis 的單線程命令處理機制,即便多個客戶端同時執行 setnx 命令,也只有一個客戶端執行成功。因此,基於 setnx 這種特性,setnx 命令能夠做爲分佈式鎖的一種解決方案安全

而 setxx 命令則能夠在安全性比較高的場景中使用,由於 set 命令執行時,會執行覆蓋的操做,而 setxx 在更新 key 時能夠確保該 key 已經存在了,因此爲了保證 key 中數據類型的正確性,可使用 setxx 命令。網絡

2.獲取值

get key

3.批量設置值

mset key value

4.批量獲取值

mget key

若是有些鍵不存在,那麼它的值將爲 nil,也就是空,而且返回結果的順序與傳入時相同。數據結構

5.計數

incr key

incr 命令用於對值作自增操做,返回的結果分爲 3 種狀況:架構

  • 若是值不是整數,那麼返回的必定是錯誤
  • 若是值是整數,那麼返回自增後的結果
  • 若是鍵不存在,那麼就會建立此鍵,而後按照值爲 0 自增, 就是返回 1

除了有 incr 自增命令外,Redis 中還提供了其它對數字處理的命令。例如:app

decr key 自減
incrby kek increment 自增指定數字
decrby key decrement 自減指定數字
incrbyfloat key increment 自增浮點數

6.追加值

append key value

append 命令能夠向字符串尾部追加值。

7.字符串長度

strlen key

因爲每一箇中文佔用 3 個字節,因此 jilinwula 這個鍵,返回是字符串長度爲 12,而不是 4。

8.設置並返回原值

getset key value

9.設置指定位置的字符

setrange key offeset value

10.獲取部分字符串

getrange key start end

時間複雜度

在 Redis 中執行任何命令時,都有相應的時間複雜度,複雜度越高也就越費時間,因此在執行 Redis 中的命令時,若是要執行的命令複雜度越高,就越要慎重。下面是字符串命令時間複雜度類型表:

命令

時間複雜度

set key value

O(1)

get key

O(1)

del key

O(k) k是鍵的個數

mset key value

O(k) k是鍵的個數

mget key

O(k) k是鍵的個數

incr key

O(1)

decr key

O(1)

incrby key increment

O(1)

decrby keky increment

O(1)

incrbyfloat key iincrement

O(1)

append key value

O(1)

strlen key

O(1)

setrange key offset value

O(1)

getrange key start end

O(n) n是字符串長度

內部編碼

在 Redis 中字符串類型的內部編碼有 3 種:

  • int:8 個字節的長整型
  • embstr:小於等於 39 個字節的字符串
  • raw:大於 39 個字節的字符串

哈希類型

大部分語言基本都提供了哈希類型,如 Java 語言中的 Map 類型及 Python 語言中的字典類型等等。雖然語言不一樣,但它們基本使用都是同樣的,也就是都是鍵值對結構的。例如:

value={{field1, value1}

經過下圖能夠直觀感覺一下字符串類型和哈希類型的區別:

Redis 中哈希類型都是鍵值對結構的,因此要特別注意這裏的 value 並非指 Redis 中 key 的 value,而是哈希類型中的 field 所對應的 value。

命令

下面咱們仍是和介紹字符串類型同樣,瞭解一下 Redis 中哈希類型的相關命令。

1.設置值

hset key field value

咱們看上圖執行的命令知道,hset 命令也是有返回值的。若是 hset 命令設置成功,則返回 1,不然返回 0。除此以外 Redis 也爲哈希類型提供了 hsetnx 命令。在前文對字符串的介紹中,咱們知道 nx 命令只有當 key 不存在的時候,才能設置成功,一樣的,hsetnx 命令在 field 不存在的時候,才能設置成功。

2.獲取值

hget key field

咱們看 hget 命令和 get 有很大的不一樣,get 命令在獲取的時候,只要寫一個名字就能夠了,而 hget 命令則要寫兩個名字,第一個名字是 key,第二個名字是 field。固然 key 或者 field 不存在時,返回的結果都是 nil。

3.刪除 field

hdel key field [field ...]

hdel 命令刪除的時候,也會有返回值,而且這個返回就是成功刪除 field 的個數。當 field 不存在時,並不會報錯,而是直接返回 0。

4.計算 field 個數

hlen key

hlen 命令返回的就是當前 key 中 field 的個數,若是 key 不存在,則返回 0。

5.批量設置或獲取 field-value

hmget key field [field ...]
hmset key field value [field value ...]

hmset 命令和 hmget 命令分別是批量設置和獲取值的,hmset 命令沒有什麼要注意的,但 hmget 命令要特別注意,當咱們獲取一個不存在的 key 或者不存在的 field 時,Redis 並不會報錯,而是返回 nil。而且有幾個 field 不存在,則 Redis 返回幾個 nil。

6.判斷 field 是否存在

hexists key field

當執行 hexists 命令時,若是當前 key 包括 field,則返回 1,不然返回 0。

7.獲取全部 field

hkeys key

8.獲取全部 value

hvals key

9.獲取全部的 field-value

hgetall key

hgetall 命令會返回當前 key 中的全部 field-value,並按照順序依次返回。

10.計數

hincrby key field increment
hincrbyfloat key field increment

hincrby 命令和 incrby 命令的使用功能基本同樣,都是對值進行增量操做的,惟一不一樣的就是 incrby 命令的做用域是 key,而 hincrby 命令的做用域則是 field。

11.計算 value 的字符串長度

hstrlen key field

hstrlen 命令返回的是當前 key 中 field 中字符串的長度,若是當前 key 中沒有 field 則返回 0。

時間複雜度

命令

時間複雜度

hset key field value

O(1)

hget key field

O(1)

hdel key field [field ...]

O(k) ,k是field個數

hlen key

O(1)

hgetall key

O(n) ,n是field總數

hmget key field [field ...]

O(k) ,k是field個數

hmset key field value [field value ...]

O(k) ,k是field個數

hexists key field

O(1)

hkeys key

O(n) ,n是field總數

hvals key

O(n) ,n是field總數

hsetnx key field value

O(1)

hincrby key field increment

O(1)

hincrbyfloat key field increment

O(1)

hstrlen key field

O(1)

內部編碼

Redis 哈希類型的內部編碼有兩種,它們分別是:

  • ziplist(壓縮列表):當哈希類型中元素個數小於 hash-max-ziplist-entries 配置(默認 512 個),同時全部值都小於 hash-max-ziplist-value 配置(默認 64 字節)時,Redis 會使用 ziplist 做爲哈希的內部實現。
  • hashtable(哈希表):當上述條件不知足時,Redis 則會採用 hashtable 做爲哈希的內部實現。

下面咱們經過如下命令來演示一下 ziplist 和 hashtable 這兩種內部編碼。

當 field 個數比較少而且 value 也不是很大時候 Redis 哈希類型的內部編碼爲 ziplist:

當 value 中的字節數大於 64 字節時(能夠經過 hash-max-ziplist-value 設置),內部編碼會由 ziplist 變成 hashtable。

當 field 個數超過 512(能夠經過 hash-max-ziplist-entries 參數設置),內部編碼也會由 ziplist 變成 hashtable。

因爲直接手動建立 512 個 field 不方便,爲了更好的驗證該功能,我將用程序的方式,動態建立 512 個 field 來驗證此功能,下面爲具體的代碼:

import redis
r = redis.Redis(host='127.0.0.1', port=6379)
print('Key爲【userinfo】的字節編碼爲【%s】' % r.object('encoding', 'userinfo').decode('utf-8'))
for i in range(1,513):    
    r.hset('userinfo', i, '吉林烏拉')
print('Key爲【userinfo】的字節編碼爲【%s】' % r.object('encoding', 'userinfo').decode('utf-8'))
Key爲【userinfo】的字節編碼爲【ziplist】
Key爲【userinfo】的字節編碼爲【hashtable】

列表類型

Redis 中列表類型能夠簡單地理解爲存儲多個有序字符串的一種新類型,這種類型除了字符串類型中已有的功能外,還提供了其它功能,如能夠對列表的兩端插入和彈出元素(在列表中的字符串均可以稱之爲元素),除此以外還能夠獲取指定的元素列表,而且還能夠經過索引下標獲取指定元素等等。下面咱們經過下圖來看一下 Redis 中列表類型的插入和彈出操做:

下面咱們看一下 Redis 中列表類型的獲取與刪除操做:

Redis 列表類型的特色以下:

  • 列表中全部的元素都是有序的,因此它們是能夠經過索引獲取的,也就是上圖中的 lindex 命令。而且在 Redis 中列表類型的索引是從 0 開始的。
  • 列表中的元素是能夠重複的,也就是說在 Redis 列表類型中,能夠保存同名元素,以下圖所示:

命令

下面咱們仍是和學習其它數據類型同樣,咱們仍是先學習一下 Redis 列表類型的命令。

1.添加操做

  • 從右邊插入元素
rpush key value [value ...]

咱們看 rpush 命令在插入時,是有返回值的,返回值的數量就是當前列表中全部元素的個數。

咱們也能夠用下面的命令從左到右獲取當前列表中的全部的元素,也就是如上圖所示中那樣。

lrange 0 -1
  • 從左邊插入元素
lpush key value [value ...]

lpush 命令的返回值及用法和 rpush 命令同樣。經過上面的事例證實了咱們前面說的,rpush 命令和 lpush 命令的返回值並非當前插入元素的個數,而是當前 key 中所有元素的個數,由於當前 key 中已經有了 3 個元素,因此咱們在執行插入命令時,返回的就是 6 而不是 3,。

  • 向某個元素前或者後插入元素
linsert key BEFORE|AFTER pivot value

linsert 命令在執行的時候首先會從當前列表中查找到 pivot 元素,其次再將這個新元素插入到 pivot 元素的前面或者後面。而且咱們經過上圖能夠知道 linsert 命令在執行成功後也是會有返回值的,返回的結果就是當前列表中元素的個數。

2.查找

  • 獲取指定範圍內的元素列表
lrange key start stop

lrange 命令會獲取列表中指定索引範圍的全部元素。

經過索引獲取列表主要有兩個特色:

  • 索引下標從左到右分別是 0 到 N-1,從右到左是 -1 到 -N。
  • lrange 命令中的 stop 參數在執行時會包括當前元素,並非全部的語言都是這樣的。咱們要獲取列表中前兩個元素則能夠以下圖所示:

  • 獲取列表中指定索引下標的元素
lindex key index

  • 獲取列表長度
llen key

3.刪除

  • 從列表左側彈出元素
lpop key

lpop 命令執行成功後會返回當前被刪除的元素名稱。

  • 從列表右側彈出元素
rpop key

rpop 命令和 lpop 命令的使用方式同樣。

  • 刪除指定元素
lrem key count value

lrem 命令會將列表中等於 value 的元素刪除掉,而且會根據 count 參數來決定刪除 value 的元素個數。

下面咱們看一下 count 參數的使用說明:

count > 0:表示從左到右,最多刪除 count 個元素。也就是以下圖所示:

咱們看上圖中的命令中,雖然咱們將 count 參數指定的是 5,將 value 參數指定的是 2,但因爲當前列表中只有一個 2,因此,當前 lrem 命令最多隻能刪除 1 個元素,而且 lrem 命令也是有返回值的,也就是當前成功刪除元素的個數。

count < 0:從右到左,最多刪除 count 個元素。

count = 0:刪除全部元素。

  • 按照索引範圍修剪列表
ltrim key start stop

ltrim 命令會直接保留 start 索引到 stop 索引的之間的元素,幷包括當前元素,而以外的元素則都會刪除掉,因此該命令也叫修剪列表。

而且有一點要注意,ltrim 命令不會返回當前的列表中元素的個數,而是返回改命令是否成功的狀態。

4.修改

  • 修改指定索引下標的元素
lset key index value

5.阻塞操做

blpop key [key ...] timeout
brpop key [key ...] timeout

blpop 和 brpop 命令是 lpop 和 rpop 命令的阻塞版本,它們除了彈出方向不一樣之外,使用方法基本相同。

  • key [key ...]:能夠指定多個列表的鍵。
  • timeout:阻塞時間(單位:秒)

下面咱們看一下該命令的詳細使用。

列表爲空:若是 timeout=3,則表示客戶端等待 3 秒後才能返回結果,若是 timeout=0,則表示客戶端會一直等待,也就是會阻塞。

因爲我期間向列表中插入了元素,不然上述命令將一直阻塞下去。

列表不爲空:若是 timeout=0,而且列表不爲空時,則 blpop 和 brpop 命令會當即返回結果,不會阻塞。

下面咱們看一下 blpop 和 brpop 命令的注意事項。

若是使用 blpop 和 brpop 命令指定多個鍵時,blpop 和 brpop 命令會從左到右遍歷鍵,而且一旦有一個鍵能返回元素,則客戶端會當即返回。

當列表爲空時,上述命令會阻塞,若是向上述中的任何一個鍵中插入元素,則上述命令會直接返回該鍵的元素。

若是多個客戶端都對同一個鍵執行 blpop 或者 brpop 命令,則最早執行該命令的客戶端會獲取到該鍵的元素。

我同時啓動了 3 個客戶端,由於當前列表爲空,因此上述命令執行後會阻塞。若是此時我向該列表中插入元素,則只有第一個客戶端會有返回結果,由於第一個客戶端是第一個執行上述命令的。

時間複雜度

下面咱們看一下列表中命令的相關時間複雜度。

操做類型

命令

時間複雜度

添加

rpush key value [value ...]

O(k),k是元素的個數

添加

lpush key value [value ...]

O(k),k是元素的個數

添加

linsert key BEFORE/AFTER pivot value

O(n),n是pivot距離列表頭或者尾的距離

查找

lrange key start stop

O(s + n),s是start偏移量,n是start到stop的範圍

查找

lindex key index

O(n),n是索引的偏移量

查找

llen key

O(1)

刪除

lpop key

O(1)

刪除

rpop key

O(1)

刪除

lrem key count value

O(n),n是列表長度

刪除

ltrim key start stop

O(n),n是要裁剪的元素個數

修改

lset key index value

O(n),n是索引的偏移量

阻塞操做

blpop/brpop key [key ...] timeout

O(1)

內部編碼

列表中的內部編碼有兩種,它們分別是:

  • ziplist(壓縮列表):當列表中元素個數小於 512(默認)個,而且列表中每一個元素的值都小於 64(默認)個字節時,Redis 會選擇用 ziplist 來做爲列表的內部實現以減小內存的使用。固然上述默認值也能夠經過相關參數修改:list-max-ziplist-entried(元素個數)、list-max-ziplist-value(元素值)。
  • linkedlist(鏈表):當列表類型沒法知足 ziplist 條件時,Redis 會選擇用 linkedlist 做爲列表的內部實現。

集合類型

Redis 中的集合類型,也就是 set。在 Redis 中 set 也是能夠保存多個字符串的,常常有人會分不清 list 與 set,下面咱們重點介紹一下它們之間的不一樣:

  • set 中的元素是不能夠重複的,而 list 是能夠保存重複元素的。
  • set 中的元素是無序的,而 list 中的元素是有序的。
  • set 中的元素不能經過索引下標獲取元素,而 list 中的元素則能夠經過索引下標獲取元素。
  • 除此以外 set 還支持更高級的功能,例如多個 set 取交集、並集、差集等。

命令

下面咱們介紹一下 set 中的相關命令。

1.集合內操做

  • 添加元素
sadd key member [member ...]

sadd 命令也是有返回值的,它的返回值就是當前執行 sadd 命令成功添加元素的個數,由於 set 中不能保存重複元素,因此在執行 sadd setkey c d 命令時,返回的是 1,而不是 2。由於元素 c 已經成功保存到 set 中,不能再保存了,只能將 d 保存到 set 中。

  • 刪除元素
srem key member [member ...]

srem 命令和 sadd 命令同樣也是有返回值的,返回值就是當前刪除元素的個數。

  • 計算元素個數
scard key

scard 命令的時間複雜度爲O(1),scard 命令不會遍歷 set 中的全部元素,而是直接使用 Redis 中的內部變量。

  • 判讀元素是否在集合中
sismember key member

sismember 命令也有返回值,若是返回值爲1則表示當前元素在當前 set 中,若是返回 0 則表示當前元素不在 set 中。

  • 隨機從 set 中返回指定個數元素
srandmember key [count]

srandmember 命令中有一個可選參數 count,count 參數指的是返回元素的個數,若是當前 set 中的元素個數小於 count,則 srandmember 命令返回當前 set 中的全部元素,若是 count 參數等於 0,則不返回任何數據,若是 count 參數小於 0,則隨機返回當前 count 個數的元素。

  • 從集合中隨機彈出元素
spop key [count]

spop 命令也是隨機從 set 中彈出元素,而且也支持 count 可選參數,但有一點和 srandmember 命令不一樣。spop 命令在隨機彈出元素以後,會將彈出的元素從 set 中刪除。

  • 獲取全部元素
smembers key

smembers 命令雖然能獲取當前 set 中全部的元素,但返回元素的順序與 sadd 添加元素的順序不必定相同,這也就是前面提到過的保存在 set 中的元素是無序的。

2.集合間操做

  • 集合的交集
sinter key [key ...]

  • 集合的並集
sunion key [key ...]

  • 集合的差集
sdiff key [key ...]

  • 將集合的交集、並集、差集的結果保存
sinterstore destination key [key ...]
sunionstore destination key [key ...]
sdiffstore destination key [key ...]

爲何 Redis 要提供 sinterstore、sunionstore、sdiffstore 命令來將集合的交集、並集、差集的結果保存起來呢?這是由於 Redis 在進行上述比較時,會比較耗費時間,因此爲了提升性能能夠將交集、並集、差集的結果提早保存起來,這樣在須要使用時,能夠直接經過 smembers 命令獲取。

時間複雜度

下面咱們看一下 set 中相關命令的時間複雜度。

命令

時間複雜度

sadd key member [member ...]

O(k),k是元素的個數

srem key member [member ...]

O(k),k是元素的個數

scard key

O(1)

sismember key member

O(1)

srandmember key [count]

O(count)

spop key [count]

O(1)

smembers key

O(n),n是元素的總數

sinter key [key ...]

O(m * k),k是多個集合中元素最少的個數,m是鍵個數

sunion key [key ...]

O(k),k是多個元素個數和

sdiff key [key ...]

O(k),k是多個元素個數和

sinterstore destination key [key ...]

O(m * k),k是多個集合中元素最少的個數,m是鍵個數

sunionstore destination key [key ...]

O(k),k是多個元素個數和

sdiffstore destination key [key ...]

O(k),k是多個元素個數和

內部編碼

  • intset(整數集合):當集合中的元素都是整數,而且集合中的元素個數小於 512 個時,Redis 會選用 intset 做爲底層內部實現。
  • hashtable(哈希表):當上述條件不知足時,Redis 會採用 hashtable 做爲底層實現。

備註:咱們能夠經過 set-max-intset-entries 參數來設置上述中的默認參數。

下面咱們看一下具體的事例,來驗證咱們上面提到的內部編碼。

當元素個數較少而且都是整數時,內部編碼爲 intset。

當元素不全是整數時,內部編碼爲 hashtable。

當元素個數超過 512 個時,內部編碼爲 hashtable。

import redis

r = redis.Redis(host='127.0.0.1', port=6379)
    if r.object('encoding', 'setkey') != None:    
    print('Key爲【setkey】的字節編碼爲【%s】' % r.object('encoding', 'setkey').decode('utf-8'))
for i in range(1, 600):
    r.sadd('setkey', i)
if r.object('encoding', 'setkey') != None:
    print('Key爲【setkey】的字節編碼爲【%s】' % r.object('encoding', 'setkey').decode('utf-8'))
Key爲【setkey】的字節編碼爲【intset】
Key爲【setkey】的字節編碼爲【hashtable】

有序集合類型

看名字咱們就知道,有序集合也是一種集合,而且這個集合仍是有序的。列表也是有序的,那它和有序集合又有什麼不一樣呢?有序集合的有序和列表的有序是不一樣的。列表中的有序指的的是插入元素的順序和查詢元素的順序相同,而有序集合中的有序指的是它會爲每一個元素設置一個分數(score),而查詢時能夠經過分數計算元素的排名,而後再返回結果。由於有序集合也是集合類型,因此有序集合中也是不插入重複元素的,但在有序集合中分數則是能夠重複,那若是在有序集合中有多個元素的分數是相同的,這些重複元素的排名是怎麼計算的呢?後邊咱們再作詳細說明。

下面先看一下列表、集合、有序集合三種數據類型之間的區別:

數據結構

是否容許重複元素

是否有序

有序實現方式

應用場景

列表

索引下標

時間軸、消息隊列

集合

標籤、社交

有序集合

分數

排行榜、社交

命令

1.集合內操做

  • 添加元素
zadd key [NX|XX] [CH] [INCR] score member [score member ...]

zadd 命令也是有返回值的,返回值就是當前 zadd 命令成功添加元素的個數。zadd 命令有不少選填參數:

  • nx: 元素必須不存在時,才能夠設置成功。
  • xx: 元素必須存在時,才能夠設置成功。
  • ch: 返回此命令執行完成後,有序集合元素和分數發生變化的個數
  • incr: 對 score 作增長。

備註:因爲有序集合相比集合提供了排序字段,正是由於如此也付出了相應的代價,sadd 的時間複雜度爲 O(1),而 zadd 的時間複雜度爲O(log(n))。

  • 計算成員個數
zcard key

  • 計算某個成員的分數
zscore key member

在使用 zscore 命令時,若是 key 不存在,或者元素不存在時,該命令返回的都是(nil)。

  • 計算成員的排名
zrank key member
zrevrank key member

zrank 命令是從分數低到高排名,而 zrevrank 命令則偏偏相反,從高到低排名。有一點要特別注意, zrank 和 zrevrank 命令與 zscore 是命令不一樣的,前者經過分數計算出最後的排名,然後者則是直接返回當前元素的分數。

  • 刪除元素
zrem key member [member ...]

返回的結果爲成功刪除元素的個數,由於 zrem 命令是支持批量刪除的。

  • 增長元素分數
zincrby key increment member

雖然 zincrby 命令是增長元素分數的,但咱們也能夠指定負數,這樣當前元素的分數,則會相減。

  • 返回指定排名範圍的元素
zrange key start stop [WITHSCORES]
zrevrange key start stop [WITHSCORES]

zrange 命令是經過分數從低到高返回數據,而 zrevrange 命令是經過分數從高到低返回數據。若是執行命令時添加了 WITHSCORES 可選參數,則返回數據時會返回當前元素的分數。

  • 返回指定分數範圍的元素
zrangebyscore key min max [WITHSCORES] [LIMIT offset count]
zrevrangebyscore key max min [WITHSCORES] [LIMIT offset count]

min 和 max 參數還支持開區間(小括號)和閉區間(中括號),同時咱們還能夠用 -inf 和 +inf 參數表明無限小和無限大。

  • 返回指定分數範圍元素個數
zcount key min max

  • 刪除指定排名內的升序元素
zremrangebyrank key start stop

  • 刪除指定分數範圍元素
zremrangebyscore key min max

2.集合間操做

  • 交集
zinterstore destination numkeys key [key ...] [WEIGHTS weight] [AGGREGATE SUM|MIN|MAX]

zinterstore 命令參數比較多:

  • destination:將交集的計算結果,保存到這個鍵中。
  • numkeys:須要作交集計算鍵的個數。
  • key [key ...]:須要作交集計算的鍵。
  • WEIGHTS weight:每一個鍵的權重,在作交集計算時,每一個鍵中的分數值都會乘以這個權重,默認每一個鍵的權重爲 1。
  • AGGREGATE SUM|MIN|MAX:計算成員交集後,分值能夠按照 sum(和)、min(最小值)、max(最大值)作彙總,默認值爲  sum。

下面咱們將權重設置爲 0.5,這樣當計算交集後,有序集合中的元素分數將都會減半,而且使用 max 參數彙總。

  • 並集
zunionstore destination numkeys key [key ...] [WEIGHTS weight] [AGGREGATE SUM|MIN|MAX]

zunionstore 命令的相關參數和 zinterstore 命令相同。

時間複雜度

命令

時間複雜度

zadd key [NX/XX] [CH] [INCR] score member [score member ...]

O(k*log(n)),k是添加元素的個數,n是當前有序集合元素個數

zcard key

O(1)

zscore key member

O(1)

zrank key member

O(log(n)),n是當前有序集合元素個數

zrevrank key member

O(log(n)),n是當前有序集合元素個數

zrem key member [member ...]

O(k*log(n)),k是刪除元素的個數,n是當前有序集合元素個數

zincrby key increment member

O(log(n)),n是當前有序集合元素個數

zrange key start stop [WITHSCORES]

O(log(n) + k),k是要獲取的元素個數,n是當前有序集合個數

zrevrange key start stop [WITHSCORES]

O(log(n) + k),k是要獲取的元素個數,n是當前有序集合個數

zrangebyscore key min max [WITHSCORES] [LIMIT offset count]

O(log(n) + k),k是要獲取的元素個數,n是當前有序集合個數

zrevrangebyscore key max min [WITHSCORES] [LIMIT offset count]

O(log(n) + k),k是要獲取的元素個數,n是當前有序集合個數

zcount key min max

O(log(n)),n是當前有序集合元素個數

zremrangebyrank key start stop

O(log(n) + k),k是要刪除的元素個數,n是當前有序集合個數

zremrangebyscore key min max

O(log(n) + k),k是要刪除的元素個數,n是當前有序集合個數

zinterstore destination numkeys key [key ...] [WEIGHTS weight] [AGGREGATE SUM/MIN/MAX]

O(n k) + O(m log(m)),n是元素元素最小的有序集合元素個數,k是有序集合個數,m是結果集中元素個數

zunionstore destination numkeys key [key ...] [WEIGHTS weight] [AGGREGATE SUM/MIN/MAX]

O(n) + O(m * log(m)),n是全部有序集合元素個數和,m是結果集中元素個數。

內部編碼

有序集合類型的內部編碼有兩種,它們分別是:

  • ziplist(壓縮列表):當有序集合的元素個數小於 128 個(默認設置),同時每一個元素的值都小於 64 字節(默認設置),Redis 會採用 ziplist 做爲有序集合的內部實現。
  • skiplist(跳躍表):當上述條件不知足時,Redis 會採用 skiplist 做爲內部編碼。

備註:上述中的默認值,也能夠經過如下參數設置:zset-max-ziplist-entries 和 zset-max-ziplist-value。

下面咱們用如下示例來驗證上述結論。

當元素個數比較少,而且每一個元素也比較小時,內部編碼爲 ziplist:

當元素個數超過 128 時,內部編碼爲 skiplist。下面咱們將採用 python 動態建立128個元素,下面爲源碼:

import redis

r = redis.Redis(host='127.0.0.1', port=6379)
if r.object('encoding', 'zsetkey') != None:
    print('Key爲【zsetkey】的字節編碼爲【%s】' % r.object('encoding', 'zsetkey').decode('utf-8'))
for i in range(1, 600):
    r.zadd('zsetkey',i,1)
if r.object('encoding', 'zsetkey') != None:
    print('Key爲【zsetkey】的字節編碼爲【%s】' % r.object('encoding', 'zsetkey').decode('utf-8'))
Key爲【zsetkey】的字節編碼爲【ziplist】
Key爲【zsetkey】的字節編碼爲【skiplist】

當有序集合中有任何一個元素大於 64 個字節時,內部編碼爲 skiplist。

import redis

r = redis.Redis(host='127.0.0.1', port=6379)
if r.object('encoding', 'zsetkey') != None:
    print('Key爲【zsetkey】的字節編碼爲【%s】' % r.object('encoding', 'zsetkey').decode('utf-8'))
value = ''
for i in range(1, 600):
    value += str(i)
r.zadd('zsetkey',value,1)
if r.object('encoding', 'zsetkey') != None:
    print('Key爲【zsetkey】的字節編碼爲【%s】' % r.object('encoding', 'zsetkey').decode('utf-8'))
Key爲【zsetkey】的字節編碼爲【skiplist】

到這裏,本文就結束了,寫了這麼多,其實主要大部分是關於命令的簡單介紹,其中也介紹了一些關鍵要點,若有不正確的地方,歡迎留言。

做者介紹

裴彬,Java 工程師,喜歡編程,本身開發的博客系統已上線:jilinwula.com

本文系做者投稿文章。歡迎投稿。

投稿內容要求

  • 互聯網技術相關,包括但不限於開發語言、網絡、數據庫、架構、運維、前端、DevOps(DevXXX)、AI、區塊鏈、存儲、移動、安全、技術團隊管理等內容。
  • 文章不須要首發,能夠是已經在開源中國博客或網上其它平臺發佈過的。可是鼓勵首發,首發內容被收錄可能性較大。
  • 若是你是記錄某一次解決了某一個問題(這在博客中佔絕大比例),那麼須要將問題的來龍去脈描述清楚,最直接的就是結合圖文等方式將問題復現,同時完整地說明解決思路與最終成功的方案。
  • 若是你是分析某一技術理論知識,請從定義、應用場景、實際案例、關鍵技術細節、觀點等方面,對其進行較爲全面地介紹。
  • 若是你是以實際案例分享本身或者公司對諸如某一架構模型、通用技術、編程語言、運維工具的實踐,那麼請將事件相關背景、具體技術細節、演進過程、思考、應用效果等方面描述清楚
  • 其它未盡 case 具體狀況具體分析,不虛的,文章投過來試試先,好比咱們並不拒絕就某個熱點事件對其進行的報導、深刻解析。

投稿方式

重要說明

  • 做者須要擁有所投文章的全部權,不能將別人的文章拿過來投遞。
  • 投遞的文章須要通過審覈,若是開源中國編輯以爲須要的話,將與做者一塊兒進一步完善文章,意在使文章更佳、傳播更廣。
  • 文章版權歸做者全部,開源中國得到文章的傳播權,可在開源中國各個平臺進行文章傳播,同時保留文章原始出處和做者信息,可在官方博客中標原創標籤。
相關文章
相關標籤/搜索