摘要:數據持久化到磁盤在的編碼中沒有使用類似的事件循環,我們在此依賴字段作為標識。變化即為持久化的時機。服務啟動加載數據持久化數據從文件加載進內存的方式是模擬客戶端執行命令,逐條將文件命令發送給服務端。
寫在前面
本文實現的Godis代碼版本為:v0.1
Redis持久化方式 RDB持久化BGSAVE和SAVE命令生成RDB文件,存儲數據庫信息。當服務器啟動,RDB文件也會作為原始數據,加載近服務內存。這里存在一個優先級問題——當AOF持久化是打開狀態,優先從AOF文件加載數據、還原數據庫狀態。
SAVE命令會阻塞服務,而BGSAVE派生獨立進程,不會阻塞。同時可以通過選項配置自動執行RDB持久化的周期。
Redis服務端通過記錄幾個參數(如第一篇提到的server.dirty字段記錄了上一次SAVE后經歷了多少次數據庫修改)維護數據庫的修改情況。當周期性的后臺操作serverCon執行時,會檢查數據庫的更新狀態是否滿足RDB持久化條件,依此保存數據庫狀態。
注意:RDB的文件對數據庫數據的存儲,采用的方式是存儲鍵值對。
前文提到RDB文件保存的是數據本身,而AOF文件存儲的是執行的命令轉成的協議。可以通過開啟Redis的AOF持久化,操作若干命令后,查看appendonly.aof文件了解。
因為是對數據的備份操作,讀命令無需記錄,只需記錄修改型操作。
如果AOF持久化對每次修改命令都計入文件,會多記錄一些無效命令。如:
set alpha 123 set alpha 1 set alpha 321 set alpha 123
四條命令是過程,數據庫記錄的最終值123才是過程的最終結果。
為了避免對同一個key的操作的“無效命令”的記錄,Redis有AOF重寫機制——讀取當前數據狀態作為AOF文件要追加的命令記錄。
Godis只實現AOF持久化,并且不對命令進行重寫歸并操作,所有修改操作都會記錄進AOF文件。這也意味著,在數據保存階段,會有很多無效I/O操作;加載階段,會有很多無效的命令被執行。
數據持久化到磁盤在Godis的編碼中沒有使用Redis類似的事件循環,我們在此依賴server.dirty字段作為標識。dirty變化即為持久化的時機。
首先,在命令調用處添加AOF持久化判斷,如果dirty變化,則進行持久化:
func call(c *Client, s *Server) { dirty := s.Dirty c.Cmd.Proc(c, s) dirty = s.Dirty - dirty if dirty > 0 {//dirty變化 進行持久化 AppendToFile(s.AofFilename, c.QueryBuf) } }
執行持久化操作的函數AppendToFile也很簡單,對文件追加寫,并且即刻關閉:
func AppendToFile(fileName string, content string) error { // 以只寫的模式,打開文件 f, err := os.OpenFile(fileName, os.O_WRONLY|syscall.O_CREAT, 0644) if err != nil { log.Println("log file open failed" + err.Error()) } else { n, _ := f.Seek(0, os.SEEK_END) _, err = f.WriteAt([]byte(content), n) } defer f.Close() return err }
最后,在修改型命令(如set命令)的實現處,添加對server.dirty的更新。
func SetCommand(c *Client, s *Server) { ··· s.Dirty++ ··· }
我們來測試下效果,重新編譯godis-server.go,并執行set alpha 123:
已經成功在文件中寫入了命令協議。
服務啟動加載數據持久化數據從文件加載進內存的方式是模擬客戶端執行命令,逐條將AOF文件命令發送給服務端。
func LoadData() { c := godis.CreateClient() pros := core.ReadAof(godis.AofFilename) for _, v := range pros { c.QueryBuf = string(v) err := c.ProcessInputBuffer() if err != nil { log.Println("ProcessInputBuffer err", err) } godis.ProcessCommand(c) } }
core.ReadAof將AOF文件讀入內存,分條存儲。而后的ProcessCommand在set/get命令實現處有介紹,不再說明。
集成測試關閉服務端,重新啟動服務端,直接在客戶端執行get alpha,查看是否能獲取之前set的值:
查驗AOF文件,沒有讀命令get相關的記錄。
本篇問題偽客戶端執行時不要執行持久化操作,對Client結構增加偽終端標志位,用于持久化判斷:
func LoadData() { c := godis.CreateClient() c.FakeFlag = true ··· }小結
沒有下集預告了。
這五篇短文的初衷是記錄在學習GO時寫的小demo,結果花在其他語言之前的精力超出了預算,所以在那以后也沒有再繼續開發Godis的新feature。
明天在公司的工作迎來忙碌的項目改造期,趁著今天端午小長假最后一天,了解了V0.1版本。
不過,在不久的將來,也許下個小長假,會將前文提到過的key過期、網絡優化、API開發、Stream等新的feature和優化公之于眾。
歡迎討論。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/28879.html
摘要:命令實現命令是最常用的命令之一,也是最能反映緩存發展歷史的操作。命令在客戶端接收之后,經由協議轉換傳遞給服務端執行。服務端執行命令前先查詢是否支持該命令,以決定是否執行。,是的簡稱,代表的是只存增量的持久化方式。 緣起 最近公司的第一個PHP轉GO項目已經在生產環境穩定運行數周,又逢需求小年兒,最近可以得空分享下去年學GO過程中的練手項目Godis——用Golang實現的Redis. ...
閱讀 2000·2021-09-13 10:23
閱讀 2332·2021-09-02 09:47
閱讀 3792·2021-08-16 11:01
閱讀 1214·2021-07-25 21:37
閱讀 1597·2019-08-30 15:56
閱讀 521·2019-08-30 13:52
閱讀 3127·2019-08-26 10:17
閱讀 2442·2019-08-23 18:17