Redis 中有 5 種數(shù)據(jù)結構,分別是字符串(String)、哈希(Hash)、列表(List)、集合(Set)和有序集合(Sorted Set),因為使用 Redis 場景的開發(fā)中肯定是無法避開這些基礎結構的,所以熟練掌握它們也就成了一項必不可少的能力。
字符串類型
字符串是 Red ?is 中的最基礎的數(shù)據(jù)結構,我們保存到 Redis 中的 key,也就是鍵,就是字符串結構的。除此之外,Redis 中其它數(shù)據(jù)結構也是在字符串的基礎上設計的,可見字符串結構對于 Redis 是多么重要。
Redis 中的字符串結構可以保存多種數(shù)據(jù)類型,如:簡單的字符串、JSON、XML、二進制等,但有一點要特別注意:在 Redis 中字符串類型的值最大只能保存 512 MB。
?
命令
下面通過命令了解一下對字符串類型的操作:
?
1.設置值
1 | set key value [EX seconds] [PX milliseconds] [NX|XX] |
set 命令有幾個非必須的選項,下面我們看一下它們的具體說明:
EX seconds:為鍵設置秒級過期時間
PX milliseconds:為鍵設置毫秒級過期時間
NX:鍵必須不存在,才可以設置成功,用于添加
XX:鍵必須存在,才可以設置成功,用于更新
set 命令帶上可選參數(shù) NX 和 XX 在實際開發(fā)中的作用與 setnx 和 setxx 命令相同。我們知道 setnx 命令只有當?key 不存在的時候才能設置成功,換句話說,也就是同一個 key 在執(zhí)行 setnx 命令時,只能成功一次,并且由于 Redis 的單線程命令處理機制,即使多個客戶端同時執(zhí)行 setnx 命令,也只有一個客戶端執(zhí)行成功。所以,基于 setnx 這種特性,setnx 命令可以作為分布式鎖的一種解決方案。
而 setxx 命令則可以在安全性比較高的場景中使用,因為 set 命令執(zhí)行時,會執(zhí)行覆蓋的操作,而 setxx 在更新 key 時可以確保該 key 已經存在了,所以為了保證 key 中數(shù)據(jù)類型的正確性,可以使用 setxx 命令。
?
2.獲取值
1 | get key |
?
3.批量設置值
1 | mset key value |
?
4.批量獲取值
1 | mget key |
如果有些鍵不存在,那么它的值將為 nil,也就是空,并且返回結果的順序與傳入時相同。
?
5.計數(shù)
1 | incr key |
incr 命令用于對值做自增操作,返回的結果分為 3 種情況:
如果值不是整數(shù),那么返回的一定是錯誤
如果值是整數(shù),那么返回自增后的結果
如果鍵不存在,那么就會創(chuàng)建此鍵,然后按照值為 0 自增, 就是返回 1
除了有 incr 自增命令外,Redis 中還提供了其它對數(shù)字處理的命令。例如:
1 2 3 4 | decr key 自減 incrby kek increment 自增指定數(shù)字 decrby key decrement 自減指定數(shù)字 incrbyfloat key increment 自增浮點數(shù) |
?
6.追加值
1 | append key value |
append 命令可以向字符串尾部追加值。
?
7.字符串長度
1 | strlen key |
由于每個中文占用 3 個字節(jié),所以 jilinwula 這個鍵,返回是字符串長度為 12,而不是 4。
?
8.設置并返回原值
1 | getset key value |
?
9.設置指定位置的字符
1 | setrange key offeset value |
?
10.獲取部分字符串
1 | getrange key start end |
?
時間復雜度
在 Redis 中執(zhí)行任何命令時,都有相應的時間復雜度,復雜度越高也就越費時間,所以在執(zhí)行 Redis 中的命令時,如果要執(zhí)行的命令復雜度越高,就越要慎重。下面是字符串命令時間復雜度類型表:
命令 | 時間復雜度 |
set key value | O(1) |
get key | O(1) |
del key | O(k) k是鍵的個數(shù) |
mset key value | O(k) k是鍵的個數(shù) |
mget key | O(k) k是鍵的個數(shù) |
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 個字節(jié)的長整型
embstr:小于等于 39 個字節(jié)的字符串
raw:大于 39 個字節(jié)的字符串
?
哈希類型
大部分語言基本都提供了哈希類型,如 Java 語言中的 Map 類型及 Python 語言中的字典類型等等。雖然語言不同,但它們基本使用都是一樣的,也就是都是鍵值對結構的。例如:
1 | value={{field1, value1} |
通過下圖可以直觀感受一下字符串類型和哈希類型的區(qū)別:
Redis 中哈希類型都是鍵值對結構的,所以要特別注意這里的 value 并不是指 Redis 中 key 的 value,而是哈希類型中的 field 所對應的 value。
?
命令
下面我們還是和介紹字符串類型一樣,了解一下 Redis 中哈希類型的相關命令。
?
1.設置值
1 | hset key field value |
我們看上圖執(zhí)行的命令知道,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 的個數(shù)。當 field 不存在時,并不會報錯,而是直接返回 0。
?
4.計算 field 個數(shù)
1 | hlen key |
hlen 命令返回的就是當前 key 中 field 的個數(shù),如果 key 不存在,則返回 0。
?
5.批量設置或獲取 field-value
1 2 | 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 |
當執(zhí)行 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.計數(shù)
1 2 | hincrby key field increment hincrbyfloat 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個數(shù) |
hlen key | O(1) |
hgetall key | O(n) ,n是field總數(shù) |
hmget key field [field …] | O(k) ,k是field個數(shù) |
hmset key field value [field value …] | O(k) ,k是field個數(shù) |
hexists key field | O(1) |
hkeys key | O(n) ,n是field總數(shù) |
hvals key | O(n) ,n是field總數(shù) |
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(壓縮列表):當哈希類型中元素個數(shù)小于 hash-max-ziplist-entries 配置(默認 512 個),同時所有值都小于 hash-max-ziplist-value 配置(默認 64 字節(jié))時,Redis 會使用 ziplist 作為哈希的內部實現(xiàn)。
hashtable(哈希表):當上述條件不滿足時,Redis 則會采用 hashtable 作為哈希的內部實現(xiàn)。
下面我們通過以下命令來演示一下 ziplist 和 hashtable 這兩種內部編碼。
當 field 個數(shù)比較少并且 value 也不是很大時候 Redis 哈希類型的內部編碼為 ziplist:
當 value 中的字節(jié)數(shù)大于 64 字節(jié)時(可以通過 hash-max-ziplist-value 設置),內部編碼會由 ziplist 變成 hashtable。
當 field 個數(shù)超過 512(可以通過 hash-max-ziplist-entries 參數(shù)設置),內部編碼也會由 ziplist 變成 hashtable。
由于直接手動創(chuàng)建 512 個 field 不方便,為了更好的驗證該功能,我將用程序的方式,動態(tài)創(chuàng)建 512 個 field 來驗證此功能,下面為具體的代碼:
1 2 3 4 5 6 7 8 | import redis r = redis.Redis(host='127.0.0.1', port=6379) print('Key為【userinfo】的字節(jié)編碼為【%s】' % r.object('encoding', 'userinfo').decode('utf-8')) for i in range(1,513):???? ????r.hset('userinfo', i, '吉林烏拉') print('Key為【userinfo】的字節(jié)編碼為【%s】' % r.object('encoding', 'userinfo').decode('utf-8')) Key為【userinfo】的字節(jié)編碼為【ziplist】 Key為【userinfo】的字節(jié)編碼為【hashtable】 |
?
列表類型
Redis 中列表類型可以簡單地理解為存儲多個有序字符串的一種新類型,這種類型除了字符串類型中已有的功能外,還提供了其它功能,如可以對列表的兩端插入和彈出元素(在列表中的字符串都可以稱之為元素),除此之外還可以獲取指定的元素列表,并且還可以通過索引下標獲取指定元素等等。下面我們通過下圖來看一下 Redis 中列表類型的插入和彈出操作:
下面我們看一下 Redis 中列表類型的獲取與刪除操作:
Redis 列表類型的特點如下:
列表中所有的元素都是有序的,所以它們是可以通過索引獲取的,也就是上圖中的 lindex 命令。并且在 Redis 中列表類型的索引是從 0 開始的。
列表中的元素是可以重復的,也就是說在 Redis 列表類型中,可以保存同名元素,如下圖所示:
?