深入剖析Redis系列 - Redis數據結構與全局命令概述

前言

Redis?提供了?5?種數據結構。理解每種數據結構的特點,對于?Redis?的?開發運維?非常重要,同時掌握?Redis?的?單線程命令處理?機制,會使?數據結構?和?命令?的選擇事半功倍。


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

預備知識:幾個簡單的?全局命令數據結構?和?內部編碼單線程命令?處理機制分析。

數據結構特性:5?種?數據結構?的特點、命令使用應用場景

數據管理鍵管理遍歷鍵數據庫管理

正文

1. 預備知識

在介紹?5?種?數據結構?之前,需要先了解?Redis?的一些?全局命令數據結構?和?內部編碼單線程命令處理機制

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

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

2. 全局命令

Redis?有?5?種?數據結構,它們是?鍵值對?中的?,對于??來說有一些通用的命令。

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?會在合適的?場景?選擇合適的?內部編碼,如圖所示:


可以看到,每種?數據結構?都有?兩種以上?的?內部編碼實現。例如?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。

4. 單線程架構

Redis?使用了?單線程架構?和?I/O?多路復用模型?來實現?高性能?的?內存數據庫服務。那為什么?單線程?還能這么快,下面分析原因:

4.1. 純內存訪問

Redis?將所有數據放在?內存?中,內存的?響應時長?大約為?100?納秒,這是?Redis?達到?每秒萬級別?訪問的重要基礎。

4.2. 非阻塞I/O

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


4.3. 單線程避免線程切換和競態產生的消耗

采用?單線程?就能達到如此?高的性能,那么不失為一種不錯的選擇,因為?單線程?能帶來幾個好處:

單線程?可以簡化?數據結構和算法?的實現,開發人員不需要了解復雜的?并發數據結構

單線程?避免了?線程切換?和?競態?產生的消耗,對于服務端開發來說,鎖和線程切換?通常是性能殺手。

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

小結

本文堆?Redis?的幾種?數據結構?進行了概述,介紹了幾個簡單的?全局命令數據結構?和?內部編碼?以及?單線程命令?處理機制分析。

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容