1. 程式人生 > >Redis 避不開的五種資料結構

Redis 避不開的五種資料結構

Redis 中有 5 種資料結構,分別是字串(String)、雜湊(Hash)、列表(List)、集合(Set)和有序集合(Sorted Set),因為使用 Redis 場景的開發中肯定是無法避開這些基礎結構的,所以熟練掌握它們也就成了一項必不可少的能力。本文章精要地介紹了 Redis 的這幾種資料結構,主要覆蓋了它們各自的定義、基本用法與相關要點。

字串型別

字串是 Redis 中的最基礎的資料結構,我們儲存到 Redis 中的 key,也就是鍵,就是字串結構的。除此之外,Redis 中其它資料結構也是在字串的基礎上設計的,可見字串結構對於 Redis 是多麼重要。

Redis 中的字串結構可以儲存多種資料型別,如:簡單的字串、JSON、XML、二進位制等,但有一點要特別注意:在 Redis 中字串型別的值最大隻能儲存 512 MB。

命令

下面通過命令瞭解一下對字串型別的操作:

1.設定值

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.獲取值

1 get key

3.批量設定值

1 mset key value

4.批量獲取值

1 mget key

如果有些鍵不存在,那麼它的值將為 nil,也就是空,並且返回結果的順序與傳入時相同。

5.計數

1 incr key

incr 命令用於對值做自增操作,返回的結果分為 3 種情況:

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

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

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

6.追加值

1 append key value

append 命令可以向字串尾部追加值。

7.字串長度

1 strlen key

由於每個中文佔用 3 個位元組,所以 jilinwula 這個鍵,返回是字串長度為 12,而不是 4。

8.設定並返回原值

1 getset key value

9.設定指定位置的字元

1 setrange key offeset value

10.獲取部分字串

1 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 語言中的字典型別等等。雖然語言不同,但它們基本使用都是一樣的,也就是都是鍵值對結構的。例如:

1 value={{field1,value1}

通過下圖可以直觀感受一下字串型別和雜湊型別的區別:

Redis 中雜湊型別都是鍵值對結構的,所以要特別注意這裡的 value 並不是指 Redis 中 key 的 value,而是雜湊型別中的 field 所對應的 value。

命令

下面我們還是和介紹字串型別一樣,瞭解一下 Redis 中雜湊型別的相關命令。

1.設定值

1 hset key field value

我們看上圖執行的命令知道,hset 命令也是有返回值的。如果 hset 命令設定成功,則返回 1,否則返回 0。除此之外 Redis 也為雜湊型別提供了 hsetnx 命令。在前文對字串的介紹中,我們知道 nx 命令只有當 key 不存在的時候,才能設定成功,同樣的,hsetnx 命令在 field 不存在的時候,才能設定成功。

2.獲取值

1 hget key field

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

3.刪除 field

1 hdel key field[field...]

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

4.計算 field 個數

1 hlen key

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

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

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

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

6.判斷 field 是否存在

1 hexists key field

當執行 hexists 命令時,如果當前 key 包括 field,則返回 1,否則返回 0。

7.獲取所有 field

1 hkeys key

8.獲取所有 value

1 hvals key

9.獲取所有的 field-value

1 hgetall key

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

10.計數

12 hincrby key field incrementhincrbyfloat key field increment

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

11.計算 value 的字串長度

1 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 來驗證此功能,下面為具體的程式碼:

12345678 import redisr=redis.Redis(host='127.0.0.1',port=6379)print('Key為【userinfo】的位元組編碼為【%s】'%r.object('encoding','userinfo').decode('utf-8'))foriinrange(1,513):    r.hset('userinfo',i,'吉林烏拉')print('Key為【userinfo】的位元組編碼為【%s】'%r.object('encoding','userinfo').decode('utf-8'))Key為【userinfo】的位元組編碼為【ziplistKey為【userinfo】的位元組編碼為【hashtable

列表型別

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

下面我們看一下 Redis 中列表型別的獲取與刪除操作:

Redis 列表型別的特點如下:

  • 列表中所有的元素都是有序的,所以它們是可以通過索引獲取的,也就是上圖中的 lindex 命令。並且在 Redis 中列表型別的索引是從 0 開始的。
  • 列表中的元素是可以重複的,也就是說在 Redis 列表型別中,可以儲存同名元素,如下圖所示:

命令

下面我們還是和學習其它資料型別一樣,我們還是先學習一下 Redis 列表型別的命令。

1.新增操作

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

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

我們也可以用下面的命令從左到右獲取當前列表中的所有的元素,也就是如上圖所示中那樣。

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

lpush 命令的返回值及用法和 rpush 命令一樣。通過上面的事例證明了我們前面說的,rpush 命令和 lpush 命令的返回值並不是當前插入元素的個數,而是當前 key 中全部元素的個數,因為當前 key 中已經有了 3 個元素,所以我們在執行插入命令時,返回的就是 6 而不是 3,。

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

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

2.查詢

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

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

通過索引獲取列表主要有兩個特點:

  • 索引下標從左到右分別是 0 到 N-1,從右到左是 -1 到 -N。
  • lrange 命令中的 stop 引數在執行時會包括當前元素,並不是所有的語言都是這樣的。我們要獲取列表中前兩個