Redis 中的數據庫

前面我們花了很多的時間介紹了 redis 中基本的數據結構,及其內部的實現情況,這些都是非常基礎的東西,可能不經意間你就會用到他們,希望你花點時間瞭解一下。

接下來,我們將走近 redis 數據庫,學習各種操作 redis 的命令,並介紹它的一些實現策略以及集羣配置等等內容。

一、redis 中的數據庫

server.h/redisServer 結構中有一個字段,db 字段:

redisDb *db;

db 被定義成一個 redisDb 數組,其中 redisDb 的定義如下:

typedef struct redisDb {
    dict *dict;                 
    dict *expires;              
    dict *blocking_keys;        
    dict *ready_keys;          
    dict *watched_keys;         
    int id;                    
    long long avg_ttl;      
} redisDb;

我們經常說 redis 具有十六個數據庫,可以切換不通的數據庫做數據隔離,這裏你就可以將一個 redisDb 實例理解爲一個數據庫,而 db 指針則可以訪問 redis 預定義的所有數據庫。

其中,dict 是一個字典結構,用於實際存儲數據,expires 也是一個字典結構,它存儲的是數據庫中所有設置過期時間的鍵值對,保存他們的過期時間,是一個 UNIX 時間戳。

blocking_keys 存儲了被阻塞的鍵集合,ready_keys 存儲的是可以解除阻塞的鍵集合,watched_keys 存儲的是正在被 watch 命令監控的鍵的集合,id 記錄的是當前數據庫的一個編號,avg_ttl 收集了所有鍵剩餘存活時間的一個平均值。這一部分的幾個字段的值這裏不做詳細的解釋,沒有基本的知識儲備,不容易理解的,後面我們還會遇到的。

server.h/redisServer 結構中還有一個字段,dbnum 字段:

int dbnum;  /* Total number of configured DBs */

與此同時,redis.conf 配置文件中,默認有這麼一項配置。

image

所以,我們啓動 redis-server 的時候,會根據配置文件中給定的配置默認創建 16 個數據庫。

1、select 命令

select 命令用於我們切換數據庫,例如:

image

默認連接上 redis-server 的客戶端使用 0 號數據庫,鑑於 redis 並沒有提供給客戶端查詢當前使用數據庫編號的命令,所以建議執行 redis 命令之前,尤其是修改、添加命令,先執行下切換數據庫的命令。

2、set 命令

set 命令其實無需過多介紹,它向數據庫中添加一個鍵值對,大部分情況下,鍵會是一個字符串對象,而值可取我們 redis 的五大對象之一。

因爲 redisDb 底層是字典結構,鍵不允許重複,故而 set 命令同樣適用於更新操作。

3、del 命令

del 命令用於刪除數據庫中一個鍵值對,標準語法如下:

del [KEY]

例如:

image

4、get 命令

get 命令用於獲取一個鍵值對的值,標準語法是:

get [KEY]

具體事例就不再演示了,get 命令對應於 set 命令,只能獲取由 set 命令添加的字符串對象鍵值對,而例如一些集合對象,或列表對象需要用類似於 sadd、zadd 等命令進行數據庫的添加,自然 get 命令也是無法得到這些鍵值對對象值的。

5、其他命令

第一個我們介紹 dbsize 命令,它返回當前數據庫有多少鍵值對,基本語法格式如下:

dbsize

第二個我們要介紹的是 flushdb 命令,它用於清空當前數據庫,這是一個非常危險的命令,謹慎使用,基本語法格式如下:

flushdb

image

第三個我們要介紹一個 kyes 命令,它會返回數據庫中所有符合匹配規則的鍵的集合,這個規則起初我以爲是正則表達式,幾番操作後發現匹配不上,查閱資料貌似不是正則,並且僅有以下三種規則:

  1. ?:用於匹配單個字符
  2. *:用於匹配零個或者多個字符
  3. []:可以用來指定模式的選擇區間

正則表達式中的問號,用於匹配前一個字符出現零次或一次,即要麼出現要麼不出現,而我們這裏的 keys 模式,問號具有不同的意義。

image

不過 keys 命令這種遍歷整個數據庫的命令是非常耗 CPU 的,可能會導致 redis 性能下降,不做推薦使用。

最後還有一個簡單的命令就是 exists,它用於判斷給定的 key 是否存在,返回 0 說明不存在,返回 1 說明存在。

二、過期鍵

有的時候我們希望給某些鍵一個過期時間,即希望它存活一段時間就失效,我們 redis 爲我們提供了這樣的機制。

1、expire

expire 用於設定某個鍵的過期時間,單位是秒,基本語法格式如下:

expire [key] [time]

image

可以看到,五秒後 redis 爲我們刪除了鍵 hello。與之對應的有一個命令 pexpire,他會將我們傳入的 time 參數解析爲毫秒,例如 「pexpire hello 5」會將鍵通過五毫秒之後刪除。

2、expireat

expireat 用於設定某個鍵在某個具體 Unix 時間戳過期,單位秒,基本語法如下:

expireat [key] [time]

image

過期鍵會在我們指定 Unix 時間戳別刪除。當然它也有一個對應毫秒單位的命令,pexpireat ,他會解析命令參數時間戳爲毫秒,也就是你需要傳入的時間戳不再是秒單位的,而是毫秒單位的時間戳。

3、ttl 和 pttl

這兩個命令用於查看某個過期鍵還剩餘多少時間,基本語法格式如下:

ttl/pttl [key]

image

ttl 命令輸出的單位是秒,pttl 輸出的單位是毫秒,僅此區別。

4、persist

persist 用於移除某個過期鍵的過期時間,也就是讓這個鍵成爲永久有效鍵。基本語法格式如下:

persist [key]

image

以上就是 redis 中過期鍵相關的命令,之前也說過,redisDb 數據結構中有一個 expires 字典,它存儲的就是庫中所有過期鍵以及他們生存截止時間。

那麼,你可能會好奇了,redis 只記錄了某個過期鍵的截止有效時間,那麼什麼時候刪除這些過期鍵呢?

三、過期鍵刪除策略

因爲 redis 通過 expires 字典記錄所有的過期鍵以及他們的過期時間(一個 Unix時間戳),那麼我們只需要比對當前系統時間戳 Unix 是否大於鍵的過期時間戳即可判斷鍵是否過期。

我們直接介紹 redis 使用的兩種刪除策略,定期刪除和惰性刪除。

  1. 定期刪除:redis 每間隔一段時間進行一次小規模,有限時長的刪除過期鍵操作。
  2. 惰性刪除:redis 每次讀取某個鍵的時候,會先判斷這個鍵是否過期,如果過期,執行刪除操作。

這兩個策略,每一個都有缺點,定期刪除需要每間隔一段時間觸發一次刪除,所以需要用戶對系統的業務量、請求峯谷點有熟悉的的瞭解,才能配置合適的頻率,否則過於高頻會平白增加 CPU 壓力,過於低頻會導致內存中過多無用內存佔用,降低系統性能。

而惰性刪除缺點非常直接,如果某些鍵過期了,且程序永遠不會訪問這些鍵,那麼 redis 就永遠不會釋放這些鍵佔用的內存,進而導致內存泄漏。

redis 中同時使用了這兩種刪除策略,一方面,每次讀取鍵的時候會調用方法 db.c/expireIfNeeded 判斷當前鍵是否過期,如果過期則刪除並返回 nil。另一方面,redis 中有一個定期的時間事件函數,server.c/serverCron,每次執行都會收集與更新一些與服務器狀態相關的信息,比如更新服務器時間、計算對象空轉時長,管理客戶端連接資源的釋放等等。

其中也會執行一個 expire.c/activeExpireCycle 函數,默認配置了一千微妙內返回,activeExpireCycle 會在時間內選定數據庫,隨機的處理這些過期鍵,並給予刪除。

所以,其實上 redis 通過這兩種策略的結合,定期刪除保證不存在某些過期鍵永遠得不到刪除以進而引發內存泄漏,惰性刪除使得 redis 不用集中大量時間處理這些過期鍵以引起 CPU 負載過大。

下一節,我們講 redis 如何做持久化存儲,畢竟數據放在內存,一旦服務器宕機、斷點,所有數據都會丟失,所以我們也需要將數據備份磁盤。下節見~


關注公衆不迷路,一個愛分享的程序員。

公衆號回覆「1024」加作者微信一起探討學習!

每篇文章用到的所有案例代碼素材都會上傳我個人 github

https://github.com/SingleYam/overview_java

歡迎來踩!

YangAM 公衆號

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章