歡迎光臨
每天分享高質量文章

Redis 資料結構和主要命令

來自:簡書,作者:kelgon
連結:http://www.jianshu.com/p/2f14bc570563


Redis 是一個開源的,基於記憶體的結構化資料儲存媒介,可以作為資料庫、快取服務或訊息服務使用。

Redis 支援多種資料結構,包括字串、雜湊表、連結串列、集合、有序集合、點陣圖、Hyperloglogs 等。

Redis 具備 LRU 淘汰、事務實現、以及不同級別的硬碟持久化等能力,並且支援副本集和透過 Redis Sentinel 實現的高可用方案,同時還支援透過 Redis Cluster 實現的資料自動分片能力。

Redis 的主要功能都基於單執行緒模型實現,也就是說 Redis 使用一個執行緒來服務所有的客戶端請求,同時 Redis 採用了非阻塞式 IO,並精細地最佳化各種命令的演演算法時間複雜度,這些資訊意味著:

  1. Redis 是執行緒安全的(因為只有一個執行緒),其所有操作都是原子的,不會因併發產生資料異常

  2. Redis 的速度非常快(因為使用非阻塞式 IO,且大部分命令的演演算法時間複雜度都是 O(1))

  3. 使用高耗時的 Redis 命令是很危險的,會佔用唯一的一個執行緒的大量處理時間,導致所有的請求都被拖慢。(例如時間複雜度為 O(N) 的 KEYS 命令,嚴格禁止在生產環境中使用)

本文只對 Redis 命令進行扼要的介紹,且只列出了較常用的命令。如果想要瞭解完整的 Redis 命令集,或瞭解某個命令的詳細使用方法,請參考官方檔案:https://redis.io/commands


 常用命令一:Key

Redis 採用 Key-Value 型的基本資料結構,任何二進位制序列都可以作為 Redis 的 Key 使用(例如普通的字串或一張 JPEG 圖片)

關於 Key 的一些註意事項:

不要使用過長的 Key。例如使用一個 1024 位元組的 key 就不是一個好主意,不僅會消耗更多的記憶體,還會導致查詢的效率降低

Key 短到缺失了可讀性也是不好的,例如”u1000flw” 比起”user:1000:followers” 來說,節省了寥寥的儲存空間,卻引發了可讀性和可維護性上的麻煩

最好使用統一的規範來設計 Key,比如”object-type:id:attr”,以這一規範設計出的 Key 可能是”user:1000” 或”comment:1234:reply-to”

Redis 允許的最大 Key 長度是 512MB(對 Value 的長度限制也是 512MB)

 常用命令二:String

String 是 Redis 的基礎資料型別,Redis 沒有 Int、Float、Boolean 等資料型別的概念,所有的基本型別在 Redis 中都以 String 體現。

與 String 相關的常用命令:

  1. SET:為一個 key 設定 value,可以配合 EX/PX 引數指定 key 的有效期,透過 NX/XX 引數針對 key 是否存在的情況進行區別操作,時間複雜度 O(1)

  2. GET:獲取某個 key 對應的 value,時間複雜度 O(1)

  3. GETSET:為一個 key 設定 value,並傳回該 key 的原 value,時間複雜度 O(1)

  4. MSET:為多個 key 設定 value,時間複雜度 O(N)

  5. MSETNX:同 MSET,如果指定的 key 中有任意一個已存在,則不進行任何操作,時間複雜度 O(N)

  6. MGET:獲取多個 key 對應的 value,時間複雜度 O(N)

上文提到過,Redis 的基本資料型別只有 String,但 Redis 可以把 String 作為整型或浮點型數字來使用,主要體現在 INCR、DECR 類的命令上:

  1. INCR:將 key 對應的 value 值自增 1,並傳回自增後的值。只對可以轉換為整型的 String 資料起作用。時間複雜度 O(1)

  2. INCRBY:將 key 對應的 value 值自增指定的整型數值,並傳回自增後的值。只對可以轉換為整型的 String 資料起作用。時間複雜度 O(1)

  3. DECR/DECRBY:同 INCR/INCRBY,自增改為自減。

INCR/DECR 系列命令要求操作的 value 型別為 String,並可以轉換為 64 位帶符號的整型數字,否則會傳回錯誤。

也就是說,進行 INCR/DECR 系列命令的 value,必須在 [-2^63 ~ 2^63 – 1] 範圍內。

前文提到過,Redis 採用單執行緒模型,天然是執行緒安全的,這使得 INCR/DECR 命令可以非常便利的實現高併發場景下的精確控制。

例 1:庫存控制

在高併發場景下實現庫存餘量的精準校驗,確保不出現超賣的情況。

設定庫存總量:

SET inv:remain “100”

庫存扣減 + 餘量校驗:

DECR inv:remain

當 DECR 命令傳回值大於等於 0 時,說明庫存餘量校驗透過,如果傳回小於 0 的值,則說明庫存已耗盡。

假設同時有 300 個併發請求進行庫存扣減,Redis 能夠確保這 300 個請求分別得到 99 到 – 200 的傳回值,每個請求得到的傳回值都是唯一的,絕對不會找出現兩個請求得到一樣的傳回值的情況。

例 2:自增序列生成

實現類似於 RDBMS 的 Sequence 功能,生成一系列唯一的序列號

設定序列起始值:

SET sequence “10000”

獲取一個序列值:

INCR sequence

直接將傳回值作為序列使用即可。

獲取一批(如 100 個)序列值:

INCRBY sequence 100

假設傳回值為 N,那麼 [N – 99 ~ N] 的數值都是可用的序列值。

當多個客戶端同時向 Redis 申請自增序列時,Redis 能夠確保每個客戶端得到的序列值或序列範圍都是全域性唯一的,絕對不會出現不同客戶端得到了重覆的序列值的情況。

 常用命令三:List

Redis 的 List 是連結串列型的資料結構,可以使用 LPUSH/RPUSH/LPOP/RPOP 等命令在 List 的兩端執行插入元素和彈出元素的操作。雖然 List 也支援在特定 index 上插入和讀取元素的功能,但其時間複雜度較高(O(N)),應小心使用。

與 List 相關的常用命令:

  1. LPUSH:向指定 List 的左側(即頭部)插入 1 個或多個元素,傳回插入後的 List 長度。時間複雜度 O(N),N 為插入元素的數量

  2. RPUSH:同 LPUSH,向指定 List 的右側(即尾部)插入 1 或多個元素

  3. LPOP:從指定 List 的左側(即頭部)移除一個元素並傳回,時間複雜度 O(1)

  4. RPOP:同 LPOP,從指定 List 的右側(即尾部)移除 1 個元素並傳回

  5. LPUSHX/RPUSHX:與 LPUSH/RPUSH 類似,區別在於,LPUSHX/RPUSHX 操作的 key 如果不存在,則不會進行任何操作

  6. LLEN:傳回指定 List 的長度,時間複雜度 O(1)

  7. LRANGE:傳回指定 List 中指定範圍的元素(雙端包含,即 LRANGE key 0 10 會傳回 11 個元素),時間複雜度 O(N)。應盡可能控制一次獲取的元素數量,一次獲取過大範圍的 List 元素會導致延遲,同時對長度不可預知的 List,避免使用 LRANGE key 0 -1 這樣的完整遍歷操作。

應謹慎使用的 List 相關命令:

  1. LINDEX:傳回指定 List 指定 index 上的元素,如果 index 越界,傳回 nil。index 數值是迴環的,即 – 1 代表 List 最後一個位置,-2 代表 List 倒數第二個位置。時間複雜度 O(N)

  2. LSET:將指定 List 指定 index 上的元素設定為 value,如果 index 越界則傳回錯誤,時間複雜度 O(N),如果操作的是頭 / 尾部的元素,則時間複雜度為 O(1)

  3. LINSERT:向指定 List 中指定元素之前 / 之後插入一個新元素,並傳回操作後的 List 長度。如果指定的元素不存在,傳回 – 1。如果指定 key 不存在,不會進行任何操作,時間複雜度 O(N)

由於 Redis 的 List 是連結串列結構的,上述的三個命令的演演算法效率較低,需要對 List 進行遍歷,命令的耗時無法預估,在 List 長度大的情況下耗時會明顯增加,應謹慎使用。

換句話說,Redis 的 List 實際是設計來用於實現佇列,而不是用於實現類似 ArrayList 這樣的串列的。如果你不是想要實現一個雙端出入的佇列,那麼請儘量不要使用 Redis 的 List 資料結構。

為了更好支援佇列的特性,Redis 還提供了一系列阻塞式的操作命令,如 BLPOP/BRPOP 等,能夠實現類似於 BlockingQueue 的能力,即在 List 為空時,阻塞該連線,直到 List 中有物件可以出隊時再傳回。針對阻塞類的命令,此處不做詳細探討,請參考官方檔案(https://redis.io/topics/data-types-intro) 中”Blocking operations on lists” 一節。

 常用命令四:Hash

Hash 即雜湊表,Redis 的 Hash 和傳統的雜湊表一樣,是一種 field-value 型的資料結構,可以理解成將 HashMap 搬入 Redis。

Hash 非常適合用於表現物件型別的資料,用 Hash 中的 field 對應物件的 field 即可。

Hash 的優點包括:

  1. 可以實現二元查詢,如” 查詢 ID 為 1000 的使用者的年齡”

  2. 比起將整個物件序列化後作為 String 儲存的方法,Hash 能夠有效地減少網路傳輸的消耗

  3. 當使用 Hash 維護一個集合時,提供了比 List 效率高得多的隨機訪問命令

與 Hash 相關的常用命令:

  1. HSET:將 key 對應的 Hash 中的 field 設定為 value。如果該 Hash 不存在,會自動建立一個。時間複雜度 O(1)

  2. HGET:傳回指定 Hash 中 field 欄位的值,時間複雜度 O(1)

  3. HMSET/HMGET:同 HSET 和 HGET,可以批次操作同一個 key 下的多個 field,時間複雜度:O(N),N 為一次操作的 field 數量

  4. HSETNX:同 HSET,但如 field 已經存在,HSETNX 不會進行任何操作,時間複雜度 O(1)

  5. HEXISTS:判斷指定 Hash 中 field 是否存在,存在傳回 1,不存在傳回 0,時間複雜度 O(1)

  6. HDEL:刪除指定 Hash 中的 field(1 個或多個),時間複雜度:O(N),N 為操作的 field 數量

  7. HINCRBY:同 INCRBY 命令,對指定 Hash 中的一個 field 進行 INCRBY,時間複雜度 O(1)

應謹慎使用的 Hash 相關命令:

  1. HGETALL:傳回指定 Hash 中所有的 field-value 對。傳回結果為陣列,陣列中 field 和 value 交替出現。時間複雜度 O(N)

  2. HKEYS/HVALS:傳回指定 Hash 中所有的 field/value,時間複雜度 O(N)

上述三個命令都會對 Hash 進行完整遍歷,Hash 中的 field 數量與命令的耗時線性相關,對於尺寸不可預知的 Hash,應嚴格避免使用上面三個命令,而改為使用 HSCAN 命令進行遊標式的遍歷,具體請見 https://redis.io/commands/scan

 常用命令五:Set

Redis Set 是無序的,不可重覆的 String 集合。

與 Set 相關的常用命令:

  1. SADD:向指定 Set 中新增 1 個或多個 member,如果指定 Set 不存在,會自動建立一個。時間複雜度 O(N),N 為新增的 member 個數

  2. SREM:從指定 Set 中移除 1 個或多個 member,時間複雜度 O(N),N 為移除的 member 個數

  3. SRANDMEMBER:從指定 Set 中隨機傳回 1 個或多個 member,時間複雜度 O(N),N 為傳回的 member 個數

  4. SPOP:從指定 Set 中隨機移除並傳回 count 個 member,時間複雜度 O(N),N 為移除的 member 個數

  5. SCARD:傳回指定 Set 中的 member 個數,時間複雜度 O(1)

  6. SISMEMBER:判斷指定的 value 是否存在於指定 Set 中,時間複雜度 O(1)

  7. SMOVE:將指定 member 從一個 Set 移至另一個 Set

慎用的 Set 相關命令:

  1. SMEMBERS:傳回指定 Hash 中所有的 member,時間複雜度 O(N)

  2. SUNION/SUNIONSTORE:計算多個 Set 的並集並傳回 / 儲存至另一個 Set 中,時間複雜度 O(N),N 為參與計算的所有集合的總 member 數

  3. SINTER/SINTERSTORE:計算多個 Set 的交集並傳回 / 儲存至另一個 Set 中,時間複雜度 O(N),N 為參與計算的所有集合的總 member 數

  4. SDIFF/SDIFFSTORE:計算 1 個 Set 與 1 或多個 Set 的差集並傳回 / 儲存至另一個 Set 中,時間複雜度 O(N),N 為參與計算的所有集合的總 member 數。

上述幾個命令涉及的計算量大,應謹慎使用,特別是在參與計算的 Set 尺寸不可知的情況下,應嚴格避免使用。可以考慮透過 SSCAN 命令遍歷獲取相關 Set 的全部 member(具體請見 https://redis.io/commands/scan ),如果需要做並集 / 交集 / 差集計算,可以在客戶端進行,或在不服務實時查詢請求的 Slave 上進行。

 常用命令六:Sorted Set

Redis Sorted Set 是有序的、不可重覆的 String 集合。Sorted Set 中的每個元素都需要指派一個分數 (score),Sorted Set 會根據 score 對元素進行升序排序。如果多個 member 擁有相同的 score,則以字典序進行升序排序。

Sorted Set 非常適合用於實現排名。

Sorted Set 的主要命令:

  1. ZADD:向指定 Sorted Set 中新增 1 個或多個 member,時間複雜度 O(Mlog(N)),M 為新增的 member 數量,N 為 Sorted Set 中的 member 數量

  2. ZREM:從指定 Sorted Set 中刪除 1 個或多個 member,時間複雜度 O(Mlog(N)),M 為刪除的 member 數量,N 為 Sorted Set 中的 member 數量

  3. ZCOUNT:傳回指定 Sorted Set 中指定 score 範圍內的 member 數量,時間複雜度:O(log(N))

  4. ZCARD:傳回指定 Sorted Set 中的 member 數量,時間複雜度 O(1)

  5. ZSCORE:傳回指定 Sorted Set 中指定 member 的 score,時間複雜度 O(1)

  6. ZRANK/ZREVRANK:傳回指定 member 在 Sorted Set 中的排名,ZRANK 傳回按升序排序的排名,ZREVRANK 則傳回按降序排序的排名。時間複雜度 O(log(N))

  7. ZINCRBY:同 INCRBY,對指定 Sorted Set 中的指定 member 的 score 進行自增,時間複雜度 O(log(N))

慎用的 Sorted Set 相關命令:

  1. ZRANGE/ZREVRANGE:傳回指定 Sorted Set 中指定排名範圍內的所有 member,ZRANGE 為按 score 升序排序,ZREVRANGE 為按 score 降序排序,時間複雜度 O(log(N)+M),M 為本次傳回的 member 數

  2. ZRANGEBYSCORE/ZREVRANGEBYSCORE:傳回指定 Sorted Set 中指定 score 範圍內的所有 member,傳回結果以升序 / 降序排序,min 和 max 可以指定為 – inf 和 + inf,代表傳回所有的 member。時間複雜度 O(log(N)+M)

  3. ZREMRANGEBYRANK/ZREMRANGEBYSCORE:移除 Sorted Set 中指定排名範圍 / 指定 score 範圍內的所有 member。時間複雜度 O(log(N)+M)

上述幾個命令,應儘量避免傳遞 [0 -1] 或 [-inf +inf] 這樣的引數,來對 Sorted Set 做一次性的完整遍歷,特別是在 Sorted Set 的尺寸不可預知的情況下。可以透過 ZSCAN 命令來進行遊標式的遍歷(具體請見 https://redis.io/commands/scan ),或透過 LIMIT 引數來限制傳回 member 的數量(適用於 ZRANGEBYSCORE 和 ZREVRANGEBYSCORE 命令),以實現遊標式的遍歷。

 常用命令七:Bitmap 和 HyperLogLog

Redis 的這兩種資料結構相較之前的並不常用,在本文中只做簡要介紹,如想要詳細瞭解這兩種資料結構與其相關的命令,請參考官方檔案 https://redis.io/topics/data-types-intro 中的相關章節

Bitmap 在 Redis 中不是一種實際的資料型別,而是一種將 String 作為 Bitmap 使用的方法。可以理解為將 String 轉換為 bit 陣列。使用 Bitmap 來儲存 true/false 型別的簡單資料極為節省空間。

HyperLogLogs 是一種主要用於數量統計的資料結構,它和 Set 類似,維護一個不可重覆的 String 集合,但是 HyperLogLogs 並不維護具體的 member 內容,只維護 member 的個數。也就是說,HyperLogLogs 只能用於計算一個集合中不重覆的元素數量,所以它比 Set 要節省很多記憶體空間。

 其他常用命令

  1. EXISTS:判斷指定的 key 是否存在,傳回 1 代表存在,0 代表不存在,時間複雜度 O(1)

  2. DEL:刪除指定的 key 及其對應的 value,時間複雜度 O(N),N 為刪除的 key 數量

  3. EXPIRE/PEXPIRE:為一個 key 設定有效期,單位為秒或毫秒,時間複雜度 O(1)

  4. TTL/PTTL:傳回一個 key 剩餘的有效時間,單位為秒或毫秒,時間複雜度 O(1)

  5. RENAME/RENAMENX:將 key 重新命名為 newkey。使用 RENAME 時,如果 newkey 已經存在,其值會被改寫;使用 RENAMENX 時,如果 newkey 已經存在,則不會進行任何操作,時間複雜度 O(1)

  6. TYPE:傳回指定 key 的型別,string, list, set, zset, hash。時間複雜度 O(1)

  7. CONFIG GET:獲得 Redis 某配置項的當前值,可以使用 * 萬用字元,時間複雜度 O(1)

  8. CONFIG SET:為 Redis 某個配置項設定新值,時間複雜度 O(1)

  9. CONFIG REWRITE:讓 Redis 重新載入 redis.conf 中的配置


●編號420,輸入編號直達本文

●輸入m獲取文章目錄

推薦↓↓↓

 

Web開發

更多推薦18個技術類微信公眾號

涵蓋:程式人生、演演算法與資料結構、駭客技術與網路安全、大資料技術、前端開發、Java、Python、Web開發、安卓開發、iOS開發、C/C++、.NET、Linux、資料庫、運維等。

贊(0)

分享創造快樂