Redis單機資料庫的實現

《Redis設計與實現》筆記1 | Redis單機資料庫的實現

《Redis設計與實現》的筆記,包括redis中物件的概念、記憶體回收、RDB、AOF、事件、客戶端、伺服器等

1。物件

1。1 型別

建立鍵值對時包含 鍵物件 和 找物件 ,鍵物件總是一個字串物件,值物件則有五種常用物件:字串物件、列表物件、雜湊物件、集合物件、有序集合物件。檢視物件型別

type [key]

1。2 記憶體回收

採用引用計數實現記憶體回收機制,計數次數會根據使用狀態變化。

建立新物件時,引用計數+1

物件被新程式使用,引用計數+1

物件不在被使用時,引用計數-1

引用計數為0時,記憶體釋放

檢視引用次數

object refcount [key]

1。3 物件共享

在值相同的情況下,該物件的記憶體可以被多個鍵共享,每共享一次,引用計數次數+1。

目前:redis會在初始化伺服器時建立1萬個字串物件,包含0-9999的所用整數值,優先使用這些共享值,而不是新建立物件

1。4 物件空轉時長

空轉時長表示某個鍵從現在起距離最後一次訪用的間隔時長,命令

object idletime [key]

2。單機資料庫

redis伺服器預設會建立16個數據庫(0-15),預設為0號資料庫,切換命令為

select [num]

2。1 RDB

RDB全稱Redis DataBase,Redis是記憶體資料庫,把資料儲存在記憶體,但是不能持久,所以redis提供了RDB持久化功能,可以把記憶體中的資料庫狀態儲存到磁碟中,避免資料意外丟失。

過程:redis記憶體資料庫狀態——>RDB檔案(經過壓縮的二進位制檔案),落盤——>還原為資料庫狀態

兩個命令生成RDB檔案:

save

bgsave

save命令會阻塞伺服器程序,拒絕客戶端傳送的所有請求,直到RDB檔案建立完畢

bgsave命令則是派生一個子程序負責建立RDB檔案,伺服器程序繼續執行客戶端的命令請求

在啟動redis伺服器後會自動載入RDB檔案(載入期間伺服器會處於阻塞狀態)

$redis-server49917:M 23 Dec 2021 14:03:26。107 # Server initialized49917:M 23 Dec 2021 14:03:26。108 * Loading RDB produced by version 6。2。449917:M 23 Dec 2021 14:03:26。108 * RDB age 81588 seconds49917:M 23 Dec 2021 14:03:26。108 * RDB memory usage when created 0。98 Mb49917:M 23 Dec 2021 14:03:26。108 * DB loaded from disk: 0。000 seconds49917:M 23 Dec 2021 14:03:26。108 * Ready to accept connections

注意:

如果伺服器開啟了AOF持久化功能,那麼伺服器會優先使用AOF檔案來還原資料庫狀態,因為AOF檔案的更新頻率通常比RDB檔案的更新頻率高

只有AOF處於關閉狀態,才會使用RDB檔案來還原資料庫狀態

自動間隔性儲存

只要滿足一條就會執行bgsave命令,透過子程序執行,不影響父程序

$save 900 1$save 300 10$save 60 10000

只要滿足其中一條就會執行:

伺服器900秒內對資料庫至少進行了1次修改

伺服器300秒內對資料庫至少進行了10次修改

伺服器60秒內對資料庫至少進行了10000次修改

2。2 AOF

AOF全稱Append Only File,RDB持久化是透過儲存資料庫中具體的鍵值對,而AOF持久化則是透過儲存redis伺服器所執行的寫命令

AOF持久化的實現:

命令追加

AOF開啟狀態下,伺服器執行完寫命令後,會將該命令追加到aof_buf緩衝區的末尾,

檔案寫入和同步

redis伺服器每次結束一個伺服器程序之前,都會呼叫flushAppendOnlyFile函式,考慮是否將aof_buf緩衝區中的內容寫入和儲存到

AOF檔案裡面。flushAppendOnlyFile函式行為由appendfsync選項的值決定,有三種行為:

always:將aof_buf緩衝區中所有內容寫入並同步到AOF檔案(效率最慢,安全性最高)

everysec:將aof_buf緩衝區中所有內容寫入到AOF檔案,如果上次同步AOF檔案的時間距離現在超過1秒,再次對AOF檔案進行同步,同步操作由一個執行緒專門負責(若沒有為appendfsync主動設定值,則該行為預設情況)(效率夠快,1秒同步一次,安全性較好)

no:將aof_buf緩衝區中所有內容寫入到AOF檔案,但不同步,由作業系統決定何時同步(效率最快,同步時間最長,安全性較低)

有兩個同步函式,fsync和fdatasync可以強制讓作業系統立即將緩衝區中的資料寫入到硬盤裡

AOF檔案載入和資料還原:

建立一個不帶網路連線的偽客戶端(因為redis命令只能在客戶端上下文中執行)——>從AOF檔案中取出一條寫命令——>用為客戶端執行該命令——>迴圈2、3步,直到所有命令執行完畢

AOF檔案重寫:

命令:

bgrewriteaof

隨著伺服器執行的時間增加,AOF檔案中的內容會越來越多,體積越來越大,用AOF檔案來進行資料還原所需的時間就要更多,為了解決這個問題,redis提供了重寫(rewrite)功能,即透過建立一個新的AOF檔案來代替現有的AOF檔案,新舊檔案儲存的資料庫狀態相同,但新AOF檔案沒有那麼多冗餘命令,所以會比舊AOF檔案小

為了儘量減少冗餘命令,AOF檔案重寫不需要操作舊AOF檔案裡的命令,而是讀取伺服器當前資料庫狀態來實現。

如下,舊AOF檔案需要儲存3條命令:

127。0。0。1:6379> sadd key1 a b(integer) 2127。0。0。1:6379> sadd key1 c d(integer) 2127。0。0。1:6379> sadd key1 e f(integer) 2

重寫的AOF檔案則只需要儲存一條命令,即用1條命令代替3條命令:

127。0。0。1:6379> sadd key1 a b c d e f(integer) 6

因為AOF重寫是一種輔助性維護手段,所以AOF重寫會放到子程序執行,不會阻塞主程序。但是這樣也會造成一個問題,由於伺服器主程序在處理命令請求時,子程序可以同時執行重寫,這就可能導致伺服器當前資料庫狀態和重寫後的AOF檔案儲存的資料庫狀態不一致,即資料不一致問題。

為了解決資料不一致問題,redis設定了一個AOF重寫緩衝區,當redis伺服器執行完一個寫命令後,會同時把寫命令傳送給AOF緩衝區和AOF重寫緩衝區,當子程序完成AOF重寫後,會通知父程序將AOF重寫緩衝區中內容寫入到新AOF檔案中,此時新AOF檔案中所儲存的資料庫狀態和伺服器當前的資料庫狀態一致,然後對新的AOF檔案改名,覆蓋舊的AOF檔案,即解決了資料不一致的問題

2。3 事件

redis的事件包括檔案事件和時間事件

redis的伺服器和客戶端通訊是透過套接字,會產生相應的檔案事件,檔案事件是伺服器對套接字操作的抽象,透過監聽這些事件來完成一系列網路通訊

時間事件就是redis伺服器的一些操作需要在給定的時間點執行

檔案事件

每當一個套接字準備好執行連線應答、寫入、讀取、關閉操作時,就會產生一個檔案事件

Redis單機資料庫的實現

多個事件可能會併發丟擲,但總是被I/O多路複用程式放到佇列裡,每次同步有序的只傳送一個套接字給檔案事件分派器

下圖是客戶端和伺服器的通訊過程

Redis單機資料庫的實現

時間事件

redis的時間事件分為兩類:

定時事件:讓一段程式在指定的時間之後再執行一次

週期性事件:讓一段程式每隔指定的時間就執行一次

伺服器將所有時間事件都放在一個無序連結串列中,每當時間事件執行器執行時,就遍歷整個連結串列,查詢所有已到達的時間事件,並呼叫

相應的事件處理器

serverCron函式:

持續執行的redis伺服器需要定期對自身的資源和狀態進行檢查和調整,從而確保伺服器可以長期和穩定執行,這些定期操作由serverCron函式完成。每隔一段時間serverCron就會執行一次,直到伺服器關閉,預設每秒執行10次,即間隔為100毫秒

事件的排程與執行

由於同時存在檔案事件和時間事件,所以需要排程,決定何時處理何種檔案。

首先,檔案事件是隨機出現的,時間事件是定時出現的,所以在定時事件的間隔處會執行檔案事件,並等待下一次檔案事件,直到時間事件的到來。

注意:每一次事件執行都是原子操作

2。4客戶端

redis伺服器和客戶端是一對多的關係,客戶端所包含的狀態資訊:

套接字描述符:fd為-1表示偽客戶端,fd為大於-1的整數表示普通客戶端

名字:預設情況連線到伺服器的客戶端是沒有名字的,可以透過命令

client setname

設定名字,然後用命令

client list

檢視所有客戶端

標誌:記錄了客戶端的角色和客戶端目前所處狀態

輸入緩衝區:用於儲存客戶端傳送的命令請求

輸出緩衝區:伺服器執行命令所得的命令回覆會被儲存在客戶端狀態的輸出緩衝區裡

身份驗證:客戶端狀態的authenticated為0表示客戶端未透過身份驗證,為1表示通過了身份驗證

時間:記錄客戶端建立時間、客戶端和伺服器最後一次互動時間、客戶端空轉時間,透過client list得出的age和idle可以看出建立時間(即連線了多少秒)和空轉時間

建立客戶端:透過連結的方式連線建立的客戶端

關閉客戶端:

client kill ip:port

偽客戶端:偽客戶端在整個伺服器執行生命週期中會一直存在,直到伺服器關閉

2。5伺服器

伺服器啟動到能處理客戶端的命令請求所需要執行的步驟:

初始化伺服器狀態——>載入伺服器配置——>初始化伺服器資料結構——>還原資料庫狀態——>執行事件迴圈

3。常用命令

清空資料庫鍵值對

flushdb

flushall

flushdb只清空當前資料庫內容,但不執行持久化操作,即RDB檔案不會改變,而redis的資料是從RDB快照檔案中讀取載入到記憶體的,所以在flushdb之後,如果想恢復資料庫,則可以直接kill掉redis-server程序,然後重新啟動服務,這樣redis重新讀取RDB檔案,資料恢復到flushdb操作之前的狀態。

flushall 清空資料庫並執行持久化操作,也就是RDB檔案會發生改變,變成76個位元組大小(初始狀態下為76位元組),所以執行flushAll之後資料庫真正意義上清空了。

隨機返回資料庫某個鍵

randomkey

返回資料庫鍵值對數量

dbsize

是否存在某個鍵

exists [key]

重新命名鍵

rename [oldkey] [newkey]

匹配某些鍵

keys

redis> MSET firstname Jack lastname Stuntman age 35“OK”redis> KEYS *name*1) “firstname”2) “lastname”redis> KEYS a??1) “age”redis> KEYS *1) “age”2) “firstname”3) “lastname”

如果感覺小編寫得不錯,請素質三連:點贊+轉發+關注。我會努力寫出更好的作品分享給大家。更多JAVA進階學習資料小編已打包好,可以關注私信找我領取哦!