当前位置: 首页 > 后端技术 > PHP

用Go实现上篇文章写的Redis的第五次持久化

时间:2023-03-29 15:22:42 PHP

本文实现的Godis代码版本为:v0.1Redis持久化模式RDB持久化BGSAVE和SAVE命令生成RDB文件,存储数据库信息。服务器启动时,RDB文件也会作为原始数据加载到近服务内存中。这里有一个优先级的问题——当开启AOF持久化时,优先级是从AOF文件加载数据和恢复数据库状态。SAVE命令会阻塞服务,而BGSAVEfork一个独立的进程,不会阻塞。同时可以通过选项配置自动执行RDB持久化的周期。Redis服务器通过记录几个参数来维护数据库的修改(比如第一篇文章中提到的server.dirty字段记录了自上次SAVE以来数据库被修改了多少次)。执行周期性后台操作serverCon时,会检查数据库的更新状态是否满足RDB持久化条件,并据此保存数据库状态。注意:RDB文件通过存储键值对来存储数据库数据。AOFPersistence前面提到,RDB文件存储的是数据本身,而AOF文件存储的是执行命令转换后的协议。可以打开Redis的AOF持久化,运行几个命令后,查看appendonly.aof文件了解。因为是数据备份操作,所以不需要记录读取命令,只记录修改操作。如果AOF持久化将每条修改命令都统计到文件中,就会记录更多的无效命令。例如:setalpha123setalpha1setalpha321setalpha123这四个命令就是这个过程,数据库中记录的最终值123就是这个过程的最终结果。为了避免对同一个key的操作记录“无效命令”,Redis有一个AOF重写机制——读取当前数据状态作为命令记录追加到AOF文件中。Godis实现AOF持久化Godis只实现AOF持久化,并没有rewrite和merge命令,所有的修改操作都会记录在AOF文件中。这也意味着在数据保存阶段,会出现很多无效的I/O操作;在加载阶段,将执行许多无效命令。数据持久化到磁盘在Godis的编码中没有使用类似Redis的事件循环,我们在这里依赖server.dirty字段作为标识符。脏改是坚持的时机。首先在命令调用处加入AOF持久化判断,如果有dirty改变,则持久化:funccall(c*Client,s*Server){dirty:=s.Dirtyc.Cmd.Proc(c,s)dirty=s.dirty-dirtyifdirty>0{//脏的变化被持久化AppendToFile(s.AofFilename,c.QueryBuf)}}AppendToFile函数进行持久化操作也很简单,追加到文件后立即关闭:funcAppendToFile(fileNamestring,contentstring)error{//以只写模式打开文件f,err:=os.OpenFile(fileName,os.O_WRONLY|syscall.O_CREAT,0644)iferr!=nil{log.Println("日志文件打开失败"+err.Error())}else{n,_:=f.Seek(0,os.SEEK_END)_,err=f.WriteAt([]byte(content),n)}deferf.Close()returnerr}最后,在执行修改命令(比如set命令)的时候,添加一个update到server.dirty。funcSetCommand(c*Client,s*Server){···s.Dirty++···}测试一下效果,重新编译godis-server.go,执行setalpha123:successfullywritteninthefilecommandprotocol。启动服务加载数据和从文件加载持久化数据到内存的方式是模拟客户端执行命令,将AOF文件命令一一发送给服务端。funcLoadData(){c:=godis.CreateClient()pros:=core.ReadAof(godis.AofFilename)for_,v:=rangepros{c.QueryBuf=string(v)err:=c.ProcessInputBuffer()如果err!=nil{log.Println("ProcessInputBuffererr",err)}godis.ProcessCommand(c)}}core.ReadAof将AOF文件读入内存,分片存储。后面的ProcessCommand在set/get命令的实现中介绍,不再赘述。集成测试关闭服务器,重启服务器,直接在客户端执行getalpha,查看是否可以获取到之前设置的值:查看AOF文件,没有读取命令get相关的记录。执行本题伪客户端时不要执行持久化操作。在Client结构体中添加伪终端标志,用于持久化判断:funcLoadData(){c:=godis.CreateClient()c.FakeFlag=true···}总结暂无下集预告。这五篇短文的初衷是记录下学习GO时写的小demo。结果花在其他语言上的精力超出了预算,所以之后就没有继续开发Godis的新功能了。明天,我在公司的工作将迎来繁忙的项目转型期。趁着今天端午假期的最后一天,了解了V0.1版本。不过,在不久的将来,或许在下一个长假中,新的特性和优化,比如上面提到的key过期、网络优化、API开发、Stream等都会公开。欢迎讨论。