用Go實現Redis之五持久化

來源:互聯網
上載者:User

寫在前面

本文實現的Godis代碼版本為:v0.1

Redis持久化方式

RDB持久化

BGSAVE和SAVE命令產生RDB檔案,儲存資料庫資訊。當伺服器啟動,RDB檔案也會作為未經處理資料,載入近服務記憶體。這裡存在一個優先順序問題——當AOF持久化是開啟狀態,優先從AOF檔案載入資料、還原資料庫狀態。

SAVE命令會阻塞服務,而BGSAVE派生獨立進程,不會阻塞。同時可以通過選項配置自動執行RDB持久化的周期。

Redis服務端通過記錄幾個參數(如第一篇提到的server.dirty欄位記錄了上一次SAVE後經曆了多少次資料庫修改)維護資料庫的修改情況。當周期性的後台操作serverCon執行時,會檢查資料庫的更新狀態是否滿足RDB持久化條件,依此儲存資料庫狀態。
注意:RDB的檔案對資料庫資料的儲存,採用的方式是儲存索引值對。

AOF持久化

前文提到RDB檔案儲存的是資料本身,而AOF檔案儲存體的是執行的命令轉成的協議。可以通過開啟Redis的AOF持久化,操作若干命令後,查看appendonly.aof檔案瞭解。
因為是對資料的備份操作,讀命令無需記錄,只需記錄修改型操作。

如果AOF持久化對每次修改命令都計入檔案,會多記錄一些無效命令。如:

set alpha 123set alpha 1set alpha 321set alpha 123

四條命令是過程,資料庫記錄的最終值123才是過程的最終結果。
為了避免對同一個key的操作的“無效命令”的記錄,Redis有AOF重寫機制——讀取當前資料狀態作為AOF檔案要追加的命令記錄。

Godis實現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和最佳化公之於眾。
歡迎討論。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.