深入剖析Redis系列(四) - Redis資料結構與全域性命令概述

零壹技術棧發表於2018-09-29

前言

Redis 提供了 5 種資料結構。理解每種資料結構的特點,對於 Redis開發運維 非常重要,同時掌握 Redis單執行緒命令處理 機制,會使 資料結構命令 的選擇事半功倍。

深入剖析Redis系列(四) - Redis資料結構與全域性命令概述

接下來的幾篇文章,將從如下幾個方面介紹 Redis 的幾種資料結構,命令使用及其應用場景。

  • 預備知識:幾個簡單的 全域性命令資料結構內部編碼單執行緒命令 處理機制分析。
  • 資料結構特性5資料結構 的特點、命令使用應用場景
  • 資料管理鍵管理遍歷鍵資料庫管理

其他文章

正文

1. 預備知識

在介紹 5資料結構 之前,需要先了解 Redis 的一些 全域性命令資料結構內部編碼單執行緒命令處理機制

  1. Redis 的命令有 上百個,理解 Redis 的一些機制,會發現這些命令有很強的 通用性

  2. Redis 不是萬金油,有些 資料結構命令 必須在 特定場景 下使用,一旦 使用不當 可能對 Redis 本身 或者 應用本身 造成致命傷害。

2. 全域性命令

Redis5資料結構,它們是 鍵值對 中的 ,對於 來說有一些通用的命令。

2.1. 檢視所有鍵

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"
複製程式碼

2.2. 鍵總數

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 儲存了 大量鍵 時,線上環境 禁止 使用。

2.3. 檢查鍵是否存在

exists key

如果鍵存在則返回 1,不存在則返回 0

127.0.0.1:6379> exists java
(integer) 1
127.0.0.1:6379> exists not_exist_key
(integer) 0
複製程式碼

2.4. 刪除鍵

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

2.5. 鍵過期

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)
複製程式碼

2.6. 鍵的資料結構型別

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
複製程式碼

3. 資料結構和內部編碼

type 命令實際返回的就是當前 資料結構型別,它們分別是:string字串)、hash雜湊)、list列表)、set集合)、zset有序集合),但這些只是 Redis 對外的 資料結構。如圖所示:

深入剖析Redis系列(四) - Redis資料結構與全域性命令概述

對於每種 資料結構,實際上都有自己底層的 內部編碼 實現,而且是 多種實現。這樣 Redis 會在合適的 場景 選擇合適的 內部編碼,如圖所示:

深入剖析Redis系列(四) - Redis資料結構與全域性命令概述

可以看到,每種 資料結構 都有 兩種以上內部編碼實現。例如 list 資料結構 包含了 linkedlistziplist 兩種 內部編碼。同時有些 內部編碼,例如 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,結合了 ziplistlinkedlist 兩者的優勢,為 列表型別 提供了一種 更加高效內部編碼實現

  • 其二:不同 內部編碼 可以在 不同場景 下發揮各自的 優勢。例如 ziplist 比較 節省記憶體,但是在列表 元素比較多 的情況下,效能 會有所 下降,這時候 Redis 會根據 配置,將列表型別的 內部實現 轉換為 linkedlist

4. 單執行緒架構

Redis 使用了 單執行緒架構I/O 多路複用模型 來實現 高效能記憶體資料庫服務。那為什麼 單執行緒 還能這麼快,下面分析原因:

4.1. 純記憶體訪問

Redis 將所有資料放在 記憶體 中,記憶體的 響應時長 大約為 100 納秒,這是 Redis 達到 每秒萬級別 訪問的重要基礎。

4.2. 非阻塞I/O

Redis 使用 epoll 作為 I/O 多路複用技術 的實現,再加上 Redis 自身的 事件處理模型epoll 中的 連線讀寫關閉 都轉換為 事件,從而不用不在 網路 I/O 上浪費過多的時間,如圖所示:

深入剖析Redis系列(四) - Redis資料結構與全域性命令概述

4.3. 單執行緒避免執行緒切換和競態產生的消耗

採用 單執行緒 就能達到如此 高的效能,那麼不失為一種不錯的選擇,因為 單執行緒 能帶來幾個好處:

  • 單執行緒 可以簡化 資料結構和演算法 的實現,開發人員不需要了解複雜的 併發資料結構

  • 單執行緒 避免了 執行緒切換競態 產生的消耗,對於服務端開發來說,鎖和執行緒切換 通常是效能殺手。

單執行緒 的問題:對於 每個命令執行時間 是有要求的。如果某個命令 執行過長,會造成其他命令的 阻塞,對於 Redis 這種 高效能 的服務來說是致命的,所以 Redis 是面向 快速執行 場景的資料庫。

小結

本文堆 Redis 的幾種 資料結構 進行了概述,介紹了幾個簡單的 全域性命令資料結構內部編碼 以及 單執行緒命令 處理機制分析。

參考

《Redis 開發與運維》


歡迎關注技術公眾號: 零壹技術棧

零壹技術棧

本帳號將持續分享後端技術乾貨,包括虛擬機器基礎,多執行緒程式設計,高效能框架,非同步、快取和訊息中介軟體,分散式和微服務,架構學習和進階等學習資料和文章。

相關文章