從零單排學Redis【白銀】

前言

只有光頭才能變強html

今天繼續來學習Redis,上一篇從零單排學Redis【青銅】已經將Redis經常使用的數據結構過了一遍了。若是還沒看的同窗能夠先去看一遍再回來~git

這篇主要講的內容有:程序員

  • Redis服務器的數據庫
  • Redis對過時鍵的處理
  • Redis持久化策略(RDB和AOF)

本文力求簡單講清每一個知識點,但願你們看完能有所收穫github

1、Redis服務器中的數據庫

咱們應該都用過MySQL,MySQL咱們能夠在裏邊建立好幾個庫:redis

MySQL建立幾個數據庫

一樣地,Redis服務器中也有數據庫這麼一個概念。若是不指定具體的數量,默認會有16個數據庫。sql

經過SELECT命令能夠切換到0~15的數據庫

上面的命令咱們也能夠發現:當切換到15號數據庫,存進15號庫的數據,再切換到0號數據庫時,是獲取不到的!數據庫

  • 這說明,數據庫與數據庫之間的數據是隔離的。

1.1Redis數據庫的原理

Redis服務器用redisServer結構體來表示,其中redisDb是一個數組,用來保存全部的數據庫,dbnum表明數據庫的數量(這個能夠配置,默認是16)數組

struct redisServer{  

    //redisDb數組,表示服務器中全部的數據庫
    redisDb *db;  
    
    //服務器中數據庫的數量
    int dbnum;  
  
};

咱們知道Redis是C/S結構,Redis客戶端經過redisClient結構體來表示:緩存

typedef struct redisClient{  
   
    //客戶端當前所選數據庫
    redisDb *db;  
     
}redisClient;

Redis客戶端鏈接Redis服務端時的示例圖:安全

Redis客戶端鏈接Redis服務端時的示例圖

Redis中對每一個數據庫用redisDb結構體來表示:

typedef struct redisDb { 
    int id;         // 數據庫ID標識
    dict *dict;     // 鍵空間,存放着全部的鍵值對              
    dict *expires;  // 過時哈希表,保存着鍵的過時時間                          
    dict *watched_keys; // 被watch命令監控的key和相應client    
    long long avg_ttl;  // 數據庫內全部鍵的平均TTL(生存時間)     
} redisDb;

從代碼上咱們能夠發現最重要的應該是dict *dict,它用來存放着全部的鍵值對。對於dict數據結構(哈希表)咱們在上一篇也已經詳細說了。通常咱們將存儲全部鍵值對的dict稱爲鍵空間

鍵空間示意圖:

鍵空間示意圖

Redis的數據庫就是使用字典(哈希表)來做爲底層實現的,對數據庫的增刪改查都是構建在字典(哈希表)的操做之上的

例如:

redis > GET message

"hello world"

查找鍵的示意圖

1.2鍵的過時時間

Redis是基於內存,內存是比較昂貴的,容量確定比不上硬盤的。就咱們如今一臺普通的機子,可能就8G內存,但硬盤隨隨便便都1T了。

由於咱們的內存是有限的。因此咱們會幹掉不經常使用的數據,保留經常使用的數據。這就須要咱們設置一下鍵的過時(生存)時間了。

  • 設置鍵的生存時間能夠經過EXPIRE或者PEXPIRE命令。
  • 設置鍵的過時時間能夠經過EXPIREAT或者PEXPIREAT命令。

其實EXPIREPEXPIREEXPIREAT這三個命令都是經過PEXPIREAT命令來實現的。

咱們在redisDb結構體中還發現了dict *expires;屬性,存放全部鍵過時的時間。

舉個例子基本就能夠理解了:

redis > PEXPIREAT message 1391234400000
(integer) 1

設置了message鍵的過時時間爲1391234400000

新增一個過時時間的鍵

既然有設置過時(生存)時間的命令,那確定也有移除過時時間,查看剩餘生存時間的命令了:

  • PERSIST(移除過時時間)
  • TTL(Time To Live)返回剩餘生存時間,以秒爲單位
  • PTTL以毫秒爲單位返回鍵的剩餘生存時間

1.2.1過時策略

上面咱們已經可以瞭解到:過時鍵是保存在哈希表中了。那這些過時鍵到了過時的時間,就會立馬被刪除掉嗎??

要回答上面的問題,須要咱們瞭解一下刪除策略的知識,刪除策略可分爲三種

  • 定時刪除(對內存友好,對CPU不友好)
    • 到時間點上就把全部過時的鍵刪除了。
  • 惰性刪除(對CPU極度友好,對內存極度不友好)
    • 每次從鍵空間取鍵的時候,判斷一下該鍵是否過時了,若是過時了就刪除。
  • 按期刪除(折中)
    • 每隔一段時間去刪除過時鍵,限制刪除的執行時長和頻率。

Redis採用的是惰性刪除+按期刪除兩種策略,因此說,在Redis裏邊若是過時鍵到了過時的時間了,未必被立馬刪除的!

1.2.2內存淘汰機制

若是按期刪除漏掉了不少過時key,也沒及時去查(沒走惰性刪除),大量過時key堆積在內存裏,致使redis內存塊耗盡了,咋整?

咱們能夠設置內存最大使用量,當內存使用量超出時,會施行數據淘汰策略

Redis的內存淘汰機制有如下幾種:

內存淘汰機制

通常場景:

使用 Redis 緩存數據時,爲了提升緩存命中率,須要保證緩存數據都是熱點數據。能夠將內存最大使用量設置爲熱點數據佔用的內存量,而後啓用allkeys-lru淘汰策略,將最近最少使用的數據淘汰

2、Redis持久化

Redis是基於內存的,若是不想辦法將數據保存在硬盤上,一旦Redis重啓(退出/故障),內存的數據將會所有丟失。

  • 咱們確定不想Redis裏頭的數據因爲某些故障所有丟失(致使全部請求都走MySQL),即使發生了故障也但願能夠將Redis原有的數據恢復過來,這就是持久化的做用。

Redis提供了兩種不一樣的持久化方法來說數據存儲到硬盤裏邊:

  • RDB(基於快照),將某一時刻的全部數據保存到一個RDB文件中。
  • AOF(append-only-file),當Redis服務器執行寫命令的時候,將執行的寫命令保存到AOF文件中。

2.1RDB(快照持久化)

RDB持久化能夠手動執行,也能夠根據服務器配置按期執行。RDB持久化所生成的RDB文件是一個通過壓縮的二進制文件,Redis能夠經過這個文件還原數據庫的數據。

RDB文件還原數據

有兩個命令能夠生成RDB文件:

  • SAVE阻塞Redis服務器進程,服務器不能接收任何請求,直到RDB文件建立完畢爲止。
  • BGSAVE建立出一個子進程,由子進程來負責建立RDB文件,服務器進程能夠繼續接收請求。

Redis服務器在啓動的時候,若是發現有RDB文件,就會自動載入RDB文件(不須要人工干預)

  • 服務器在載入RDB文件期間,會處於阻塞狀態,直到載入工做完成。

除了手動調用SAVE或者BGSAVE命令生成RDB文件以外,咱們可使用配置的方式來按期執行:

在默認的配置下,若是如下的條件被觸發,就會執行BGSAVE命令

save 900 1              #在900秒(15分鐘)以後,至少有1個key發生變化,
    save 300 10            #在300秒(5分鐘)以後,至少有10個key發生變化
    save 60 10000        #在60秒(1分鐘)以後,至少有10000個key發生變化

原理大概就是這樣子的(結合上面的配置來看):

struct redisServer{
    // 修改計數器
    long long dirty;

    // 上一次執行保存的時間
    time_t lastsave;

    // 參數的配置
    struct saveparam *saveparams;
};

遍歷參數數組,判斷修改次數和時間是否符合,若是符合則調用besave()來生成RDB文件

Redis服務器的狀態

總結:經過手動調用SAVE或者BGSAVE命令或者配置條件觸發,將數據庫某一時刻的數據快照,生成RDB文件實現持久化。

2.2AOF(文件追加)

上面已經介紹了RDB持久化是經過將某一時刻數據庫的數據「快照」來實現的,下面咱們來看看AOF是怎麼實現的。

  • AOF是經過保存Redis服務器所執行的寫命令來記錄數據庫的數據的。

AOF原理圖

好比說咱們對空白的數據庫執行如下寫命令:

redis> SET meg "hello"
OK

redis> SADD fruits "apple" "banana" "cherry"
(integer) 3

redis> RPUSH numbers 128 256 512
(integer) 3

Redis會產生如下內容的AOF文件:

AOF文件

這些都是以Redis的命令請求協議格式保存的。Redis協議規範(RESP)參考資料:

AOF持久化功能的實現能夠分爲3個步驟:

  • 命令追加:命令寫入aof_buf緩衝區
  • 文件寫入:調用flushAppendOnlyFile函數,考慮是否要將aof_buf緩衝區寫入AOF文件中
  • 文件同步:考慮是否將內存緩衝區的數據真正寫入到硬盤

AOF持久化步驟

flushAppendOnlyFile函數的行爲由服務器配置的appendfsyn選項來決定的:

appendfsync always     # 每次有數據修改發生時都會寫入AOF文件。
    appendfsync everysec   # 每秒鐘同步一次,該策略爲AOF的默認策略。
    appendfsync no         # 從不一樣步。高效可是數據不會被持久化。

從字面上應該就更好理解了,這裏我就不細說了...

下面來看一下AOF是如何載入與數據還原的:

  • 建立一個僞客戶端(本地)來執行AOF的命令,直到AOF命令被所有執行完畢。

redis僞客戶端載入AOF文件

2.2.1AOF重寫

從前面的示例看出,咱們寫了三條命令,AOF文件就保存了三條命令。若是咱們的命令是這樣子的:

redis > RPUSH list "Java" "3y"
(integer)2

redis > RPUSH list "Java3y"
integer(3)

redis > RPUSH list "yyy"
integer(4)

一樣地,AOF也會保存3條命令。咱們會發現一個問題:上面的命令是能夠合併起來成爲1條命令的,並不須要3條。這樣就能夠讓AOF文件的體積變得更小

AOF重寫由Redis自行觸發(參數配置),也能夠用BGREWRITEAOF命令手動觸發重寫操做。

  • 要值得說明的是:AOF重寫不須要對現有的AOF文件進行任何的讀取、分析。AOF重寫是經過讀取服務器當前數據庫的數據來實現的

好比說如今有一個Redis數據庫的數據以下:

Redis數據庫的數據

新的AOF文件的命令以下,沒有一條是多餘的

AOF重寫後的命令

2.2.2AOF後臺重寫

Redis將AOF重寫程序放到子進程裏執行(BGREWRITEAOF命令),像BGSAVE命令同樣fork出一個子進程來完成重寫AOF的操做,從而不會影響到主進程。

AOF後臺重寫是不會阻塞主進程接收請求的,新的寫命令請求可能會致使當前數據庫和重寫後的AOF文件的數據不一致

爲了解決數據不一致的問題,Redis服務器設置了一個AOF重寫緩衝區,這個緩存區會在服務器建立出子進程以後使用

AOF後臺重寫過程

2.3RDB和AOF對過時鍵的策略

RDB持久化對過時鍵的策略:

  • 執行SAVE或者BGSAVE命令建立出的RDB文件,程序會對數據庫中的過時鍵檢查,已過時的鍵不會保存在RDB文件中
  • 載入RDB文件時,程序一樣會對RDB文件中的鍵進行檢查,過時的鍵會被忽略

RDB持久化對過時鍵的策略:

  • 若是數據庫的鍵已過時,但還沒被惰性/按期刪除,AOF文件不會由於這個過時鍵產生任何影響(也就說會保留),當過時的鍵被刪除了之後,會追加一條DEL命令來顯示記錄該鍵被刪除了
  • 重寫AOF文件時,程序會對RDB文件中的鍵進行檢查,過時的鍵會被忽略

複製模式:

  • 主服務器來控制從服務器統一刪除過時鍵(保證主從服務器數據的一致性)

2.4RDB和AOF用哪一個?

RDB和AOF並不互斥,它倆能夠同時使用

  • RDB的優勢:載入時恢復數據快、文件體積小。
  • RDB的缺點:會必定程度上丟失數據(由於系統一旦在定時持久化以前出現宕機現象,此前沒有來得及寫入磁盤的數據都將丟失。)
  • AOF的優勢:丟失數據少(默認配置只丟失一秒的數據)。
  • AOF的缺點:恢復數據相對較慢,文件體積大

若是Redis服務器同時開啓了RDB和AOF持久化,服務器會優先使用AOF文件來還原數據(由於AOF更新頻率比RDB更新頻率要高,還原的數據更完善)

可能涉及到RDB和AOF的配置:

redis持久化,兩種方式
一、rdb快照方式
二、aof日誌方式

----------rdb快照------------
save 900 1
save 300 10
save 60 10000

stop-writes-on-bgsave-error yes
rdbcompression yes
rdbchecksum yes
dbfilename dump.rdb
dir /var/rdb/

-----------Aof的配置-----------
appendonly no # 是否打開 aof日誌功能

appendfsync always #每個命令都當即同步到aof,安全速度慢
appendfsync everysec
appendfsync no 寫入工做交給操做系統,由操做系統判斷緩衝區大小,統一寫入到aof  同步頻率低,速度快


no-appendfsync-on-rewrite yes 正在導出rdb快照的時候不要寫aof
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb 


./bin/redis-benchmark -n 20000

官網文檔:

3、最後

如今臨近雙十一買阿里雲服務器就特別省錢!以前我買學生機也要9.8塊錢一個月,如今最低價只須要8.3一個月!

不管是Nginx/Elasticsearch/Redis這些技術都是在Linux下完美運行的,若是仍是程序員新手,買一個學習Linux基礎命令,學習搭建環境也是不錯的選擇。

若是有要買服務器的同窗可經過個人連接直接享受最低價https://m.aliyun.com/act/team1111/#/share?params=N.FF7yxCciiM.pfn5xpli


若是你們有更好的理解方式或者文章有錯誤的地方還請你們不吝在評論區留言,你們互相學習交流~~~

參考資料:

  • 《Redis設計與實現》
  • 《Redis實戰》

一個堅持原創的Java技術公衆號:Java3y,歡迎你們關注

3y全部的原創文章:

相關文章
相關標籤/搜索