《Redis設計與實現》筆記1 | 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-server 49917:M 23 Dec 2021 14:03:26.107 # Server initialized 49917:M 23 Dec 2021 14:03:26.108 * Loading RDB produced by version 6.2.4 49917:M 23 Dec 2021 14:03:26.108 * RDB age 81588 seconds 49917:M 23 Dec 2021 14:03:26.108 * RDB memory usage when created 0.98 Mb 49917:M 23 Dec 2021 14:03:26.108 * DB loaded from disk: 0.000 seconds 49917: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) 2 127.0.0.1:6379> sadd key1 c d (integer) 2 127.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服務器的一些操作需要在給定的時間點執行
文件事件
每當一個套接字準備好執行連接應答、寫入、讀取、關閉操作時,就會產生一個文件事件
多個事件可能會并發拋出,但總是被I/O多路復用程序放到隊列里,每次同步有序的只傳送一個套接字給文件事件分派器
下圖是客戶端和服務器的通信過程
時間事件
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"
Redis 數據庫
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。