Redis
提供了 5
種數據結構。理解每種數據結構的特色,對於 Redis
的 開發運維 很是重要,同時掌握 Redis
的 單線程命令處理 機制,會使 數據結構 和 命令 的選擇事半功倍。java
接下來的幾篇文章,將從以下幾個方面介紹 Redis
的幾種數據結構,命令使用及其應用場景。python
5
種 數據結構 的特色、命令使用、應用場景。在介紹 5
種 數據結構 以前,須要先了解 Redis
的一些 全局命令、數據結構 和 內部編碼、單線程命令處理機制。
Redis
的命令有 上百個,理解 Redis
的一些機制,會發現這些命令有很強的 通用性。
Redis
不是萬金油,有些 數據結構 和 命令 必須在 特定場景 下使用,一旦 使用不當 可能對 Redis
自己 或者 應用自己 形成致命傷害。
Redis
有 5
種 數據結構,它們是 鍵值對 中的 值,對於 鍵 來講有一些通用的命令。
keys *
下面插入了 3
對字符串類型的鍵值對:
127.0.0.1:6379> set hello world
OK
127.0.0.1:6379> set java jedis
OK
127.0.0.1:6379> set python redis-py
OK
複製代碼
命令會將全部的鍵輸出:
127.0.0.1:6379> keys *
1) "python"
2) "java"
3) "hello"
複製代碼
dbsize
下面插入一個 列表類型 的 鍵值對(值是 多個元素 組成):
127.0.0.1:6379> rpush mylist a b c d e f g
(integer) 7
複製代碼
dbsize
命令會返回當前數據庫中 鍵的總數。
127.0.0.1:6379> dbsize
(integer) 4
複製代碼
dbsize
命令在 計算鍵總數 時 不會遍歷 全部鍵,而是直接獲取 Redis
內置的鍵總數變量,因此 dbsize
命令的 時間複雜度 是 O(1)
。而 keys
命令會 遍歷 全部鍵,因此它的 時間複雜度 是 O(n)
,當 Redis
保存了 大量鍵 時,線上環境 禁止 使用。
exists key
若是鍵存在則返回 1
,不存在則返回 0
:
127.0.0.1:6379> exists java
(integer) 1
127.0.0.1:6379> exists not_exist_key
(integer) 0
複製代碼
del key
del
是一個 通用命令,不管值是什麼 數據結構 類型,del
命令均可以將其 刪除。
127.0.0.1:6379> del java
(integer) 1
127.0.0.1:6379> exists java
(integer) 0
127.0.0.1:6379> del not_exist_key
(integer) 0
127.0.0.1:6379> exists not_exist_key
(integer) 0
複製代碼
返回結果爲 成功刪除 的 鍵的個數,假設刪除一個 不存在 的鍵,就會返回 0
。
expire key seconds
Redis
支持對 鍵 添加 過時時間,當超過過時時間後,會 自動刪除鍵,例如爲鍵 hello
設置 10
秒過時時間:
127.0.0.1:6379> set hello world
OK
127.0.0.1:6379> expire hello 10
(integer) 0
複製代碼
ttl
命令會返回鍵的 剩餘過時時間,它有 3
種返回值:
0
的整數:表示鍵 剩餘 的 過時時間。-1
:鍵 沒設置 過時時間。-2
:鍵 不存在。能夠經過 ttl
命令觀察 鍵 hello
的 剩餘過時時間:
# 還剩7秒
127.0.0.1:6379> ttl hello(integer)
(integer) 7
...
# 還剩1秒
127.0.0.1:6379> ttl hello(integer)
(integer) 1
# 返回結果爲-2,說明鍵hello已經被刪除
127.0.0.1:6379> ttl hello(integer)
(integer) -2
127.0.0.1:6379> get hello
(nil)
複製代碼
type key
例如鍵 hello
是的值 字符串類型,返回結果爲 string
。鍵 mylist
的值是 列表類型,返回結果爲 list
。若是鍵不存在,則返回 none
。
127.0.0.1:6379> set a b
OK
127.0.0.1:6379> type a
string
127.0.0.1:6379> rpush mylist a b c d e f g
(integer) 7
127.0.0.1:6379> type mylist
list
複製代碼
type
命令實際返回的就是當前 鍵 的 數據結構類型,它們分別是:string
(字符串)、hash
(哈希)、list
(列表)、set
(集合)、zset
(有序集合),但這些只是 Redis
對外的 數據結構。如圖所示:
對於每種 數據結構,實際上都有本身底層的 內部編碼 實現,並且是 多種實現。這樣 Redis
會在合適的 場景 選擇合適的 內部編碼,如圖所示:
能夠看到,每種 數據結構 都有 兩種以上 的 內部編碼實現。例如 list
數據結構 包含了 linkedlist
和 ziplist
兩種 內部編碼。同時有些 內部編碼,例如 ziplist
,能夠做爲 多種外部數據結構 的內部實現,能夠經過 object encoding
命令查詢 內部編碼:
127.0.0.1:6379> object encoding hello
"embstr"
127.0.0.1:6379> object encoding mylist
"quicklist"
複製代碼
能夠看到鍵 hello
對應值的 內部編碼 是 embstr
,鍵 mylist
對應值的 內部編碼 是 ziplist
。
Redis
這樣設計有兩個好處:
其一:能夠改進 內部編碼,而對外的 數據結構 和 命令 沒有影響。例如 Redis3.2
提供的 quicklist
,結合了 ziplist
和 linkedlist
二者的優點,爲 列表類型 提供了一種 更加高效 的 內部編碼實現。
其二:不一樣 內部編碼 能夠在 不一樣場景 下發揮各自的 優點。例如 ziplist
比較 節省內存,可是在列表 元素比較多 的狀況下,性能 會有所 降低,這時候 Redis
會根據 配置,將列表類型的 內部實現 轉換爲 linkedlist
。
Redis
使用了 單線程架構 和 I/O
多路複用模型 來實現 高性能 的 內存數據庫服務。那爲何 單線程 還能這麼快,下面分析緣由:
Redis
將全部數據放在 內存 中,內存的 響應時長 大約爲 100
納秒,這是 Redis
達到 每秒萬級別 訪問的重要基礎。
Redis
使用 epoll
做爲 I/O
多路複用技術 的實現,再加上 Redis
自身的 事件處理模型 將 epoll
中的 鏈接、讀寫、關閉 都轉換爲 事件,從而不用不在 網絡 I/O
上浪費過多的時間,如圖所示:
採用 單線程 就能達到如此 高的性能,那麼不失爲一種不錯的選擇,由於 單線程 能帶來幾個好處:
單線程 能夠簡化 數據結構和算法 的實現,開發人員不須要了解複雜的 併發數據結構。
單線程 避免了 線程切換 和 競態 產生的消耗,對於服務端開發來講,鎖和線程切換 一般是性能殺手。
單線程 的問題:對於 每一個命令 的 執行時間 是有要求的。若是某個命令 執行過長,會形成其餘命令的 阻塞,對於
Redis
這種 高性能 的服務來講是致命的,因此Redis
是面向 快速執行 場景的數據庫。
本文堆 Redis
的幾種 數據結構 進行了概述,介紹了幾個簡單的 全局命令,數據結構 和 內部編碼 以及 單線程命令 處理機制分析。
《Redis 開發與運維》
歡迎關注技術公衆號: 零壹技術棧
本賬號將持續分享後端技術乾貨,包括虛擬機基礎,多線程編程,高性能框架,異步、緩存和消息中間件,分佈式和微服務,架構學習和進階等學習資料和文章。