• 将用户发来的指令以RESP协议的形式存储在本地的AOF文件,重启Redis后执行此文件恢复数据
  • https://github.com/csgopher/go-redis
  • 本文涉及以下文件:

    redis.conf:配置文件

    aof:实现aof

redis.conf

appendonly yes
appendfilename appendonly.aof

aof/aof.go

type CmdLine = [][]byte

const (
aofQueueSize = 1 << 16
) type payload struct {
cmdLine CmdLine
dbIndex int
} type AofHandler struct {
db databaseface.Database
aofChan chan *payload
aofFile *os.File
aofFilename string
currentDB int
} func NewAOFHandler(db databaseface.Database) (*AofHandler, error) {
handler := &AofHandler{}
handler.aofFilename = config.Properties.AppendFilename
handler.db = db
handler.LoadAof()
aofFile, err := os.OpenFile(handler.aofFilename, os.O_APPEND|os.O_CREATE|os.O_RDWR, 0600)
if err != nil {
return nil, err
}
handler.aofFile = aofFile
handler.aofChan = make(chan *payload, aofQueueSize)
go func() {
handler.handleAof()
}()
return handler, nil
} func (handler *AofHandler) AddAof(dbIndex int, cmdLine CmdLine) {
if config.Properties.AppendOnly && handler.aofChan != nil {
handler.aofChan <- &payload{
cmdLine: cmdLine,
dbIndex: dbIndex,
}
}
} func (handler *AofHandler) handleAof() {
handler.currentDB = 0
for p := range handler.aofChan {
if p.dbIndex != handler.currentDB {
// select db
data := reply.MakeMultiBulkReply(utils.ToCmdLine("SELECT", strconv.Itoa(p.dbIndex))).ToBytes()
_, err := handler.aofFile.Write(data)
if err != nil {
logger.Warn(err)
continue
}
handler.currentDB = p.dbIndex
}
data := reply.MakeMultiBulkReply(p.cmdLine).ToBytes()
_, err := handler.aofFile.Write(data)
if err != nil {
logger.Warn(err)
}
}
} func (handler *AofHandler) LoadAof() {
file, err := os.Open(handler.aofFilename)
if err != nil {
logger.Warn(err)
return
}
defer file.Close()
ch := parser.ParseStream(file)
fakeConn := &connection.Connection{}
for p := range ch {
if p.Err != nil {
if p.Err == io.EOF {
break
}
logger.Error("parse error: " + p.Err.Error())
continue
}
if p.Data == nil {
logger.Error("empty payload")
continue
}
r, ok := p.Data.(*reply.MultiBulkReply)
if !ok {
logger.Error("require multi bulk reply")
continue
}
ret := handler.db.Exec(fakeConn, r.Args)
if reply.IsErrorReply(ret) {
logger.Error("exec err", err)
}
}
}

AofHandler:1.从管道中接收数据 2.写入AOF文件
AddAof:用户的指令包装成payload放入管道
handleAof:将管道中的payload写入磁盘
LoadAof:重启Redis后加载aof文件

database/database.go

type Database struct {
dbSet []*DB
aofHandler *aof.AofHandler
} func NewDatabase() *Database {
mdb := &Database{}
if config.Properties.Databases == 0 {
config.Properties.Databases = 16
}
mdb.dbSet = make([]*DB, config.Properties.Databases)
for i := range mdb.dbSet {
singleDB := makeDB()
singleDB.index = i
mdb.dbSet[i] = singleDB
}
if config.Properties.AppendOnly {
aofHandler, err := aof.NewAOFHandler(mdb)
if err != nil {
panic(err)
}
mdb.aofHandler = aofHandler
for _, db := range mdb.dbSet {
singleDB := db
singleDB.addAof = func(line CmdLine) {
mdb.aofHandler.AddAof(singleDB.index, line)
}
}
}
return mdb
}

将AOF加入到database里
使用singleDB的原因:因为在循环中获取返回变量的地址都完全相同,因此当我们想要访问数组中元素所在的地址时,不应该直接获取 range 返回的变量地址 db,而应该使用 singleDB := db

database/db.go

type DB struct {
index int
data dict.Dict
addAof func(CmdLine)
} func makeDB() *DB {
db := &DB{
data: dict.MakeSyncDict(),
addAof: func(line CmdLine) {},
}
return db
}

由于分数据库db引用不到aof,所以添加一个addAof匿名函数,在NewDatabase中用这个匿名函数调用AddAof

database/keys.go

func execDel(db *DB, args [][]byte) resp.Reply {
......
if deleted > 0 {
db.addAof(utils.ToCmdLine2("del", args...))
}
return reply.MakeIntReply(int64(deleted))
} func execFlushDB(db *DB, args [][]byte) resp.Reply {
db.Flush()
db.addAof(utils.ToCmdLine2("flushdb", args...))
return &reply.OkReply{}
} func execRename(db *DB, args [][]byte) resp.Reply {
......
db.addAof(utils.ToCmdLine2("rename", args...))
return &reply.OkReply{}
} func execRenameNx(db *DB, args [][]byte) resp.Reply {
......
db.addAof(utils.ToCmdLine2("renamenx", args...))
return reply.MakeIntReply(1)
}

database/string.go

func execSet(db *DB, args [][]byte) resp.Reply {
......
db.addAof(utils.ToCmdLine2("set", args...))
return &reply.OkReply{}
} func execSetNX(db *DB, args [][]byte) resp.Reply {
......
db.addAof(utils.ToCmdLine2("setnx", args...))
return reply.MakeIntReply(int64(result))
} func execGetSet(db *DB, args [][]byte) resp.Reply {
key := string(args[0])
value := args[1] entity, exists := db.GetEntity(key)
db.PutEntity(key, &database.DataEntity{Data: value})
db.addAof(utils.ToCmdLine2("getset", args...))
......
}

添加addAof方法

测试命令

*3\r\n$3\r\nSET\r\n$3\r\nkey\r\n$5\r\nvalue\r\n
*2\r\n$3\r\nGET\r\n$3\r\nkey\r\n
*2\r\n$6\r\nSELECT\r\n$1\r\n1\r\n

GO实现Redis:GO实现Redis的AOF持久化(4)的更多相关文章

  1. redis学习笔记——RDB和AOF持久化一

    为防止数据丢失,需要将 Redis 中的数据从内存中 dump 到磁盘,这就是持久化.Redis 提供两种持久化方式:RDB 和 AOF.Redis 允许两者结合,也允许两者同时关闭. RDB 可以定 ...

  2. 《Redis设计与实现》- AOF持久化

    1. AOF持久化 Redis AOF 持久化是通过保存Redis服务器所执行的写命令来记录数据库状态的. 2. RDB持久化与AOF持久化的区别 RDB持久化 RDB持久化通过保存数据中的键值对来记 ...

  3. redis源码分析(四)--aof持久化

    Redis aof持久化 Redis支持两种持久化方式:rdb与aof,上一篇文章中已经大致介绍了rdb的持久化实现,这篇文章主要介绍aof实现. 与rdb方式相比,aof会使用更多的存储空间,因为它 ...

  4. 使用AOF持久化文件实现还原Redis数据库并得到RDB持久化文件

    目录 1 编写本文的初衷 2 具体实施 2.1 Redis持久化概念简介 2.2 获取指定Redis的AOF持久化文件 2.3 把Redis的持久化AOF文件转换为RDB文件 1 编写本文的初衷 因为 ...

  5. Redis基础篇(三)持久化:AOF日志

    Redis是内存数据库,但是一旦服务器宕机,内存中的数据将会全部丢失. 最简单的恢复方式是从后端数据库恢复,但这种方式有两个问题: 频繁访问数据库,会给数据库带来巨大的压力: 从数据库中读取相比从Re ...

  6. redis数据库安装 redis持久化及主从复制

    ----------------------------------------安装redis-5.0.4---------------------------------------- wget h ...

  7. redis的 rdb 和 aof 持久化的区别 [转]

    aof,rdb是两种 redis持久化的机制.用于crash后,redis的恢复. rdb的特性如下: Code: fork一个进程,遍历hash table,利用copy on write,把整个d ...

  8. 深入剖析 redis AOF 持久化策略

    本篇主要讲的是 AOF 持久化,了解 AOF 的数据组织方式和运作机制.redis 主要在 aof.c 中实现 AOF 的操作. 数据结构 rio redis AOF 持久化同样借助了 struct ...

  9. Redis数据持久化之AOF持久化

    一.RDB持久化的缺点创建RDB文件需要将服务器所有的数据库的数据都保存起来,这是一个非常耗费资源和时间的操作,所以服务器需要隔一段时间才能创建一个新的RDB文件,就也是说创建RDB文件的操作不能执行 ...

  10. Redis的两种持久化方式-快照持久化和AOF持久化

    Redis为了内部数据的安全考虑,会把本身的数据以文件形式保存到硬盘中一份,在服务器重启之后会自动把硬盘的数据恢复到内存(redis)的里边,数据保存到硬盘的过程就称为"持久化"效 ...

随机推荐

  1. 在Unity3D中开发的Ghost Shader

    SwordMaster Ghost Shader 特点 此Shader是顶点片元Shader,由本人手动编写完成 此Shader已经在移动设备真机上进行过测试,可以直接应用到您的项目中 所支持的Uni ...

  2. sublime Vue、ejs、less、scss高亮展示

    sublime中默认不支持ejs.vue.less.scss等文件高亮展示.解决办法可以增加对应插件: 一.安装: 1.打开sublime 2.点击  preferences ->Browse  ...

  3. linux资源查看与监控命令

  4. Linux CentOS 8系统离线升级内核版本

    Linux CentOS 8系统离线升级内核版本 搬运如下文章,十分感谢 https://blog.csdn.net/WQwinter/article/details/127231086 二.升级步骤 ...

  5. Jetpack Compose 加载 Drawable

    Drawable Painter A library which provides a way to use Android drawables as Jetpack Compose Painters ...

  6. 个人IDEA常用快捷键

    1. Ctrl 快捷键 说明 Ctrl + P 在方法参数括号内显示参数类型提示 Ctrl + Q 显示类或方法的文档注释相关信息 Ctrl + D 复制当前行 Ctrl + Y 删除当前行 Ctrl ...

  7. layui.dtree的学习,自定义扩展toolbar按钮(toolbarExt)

    学习layui.dtree请前往 http://www.wisdomelon.com/DTreeHelper/ 记录一下dtree的自定义扩展toolbar按钮(toolbarExt) html代码: ...

  8. 关于PLC的脉冲输出(S7-300)

    1.   关于脉冲输出 脉冲输出的方法有很多: 如果要产生占空比为50%的脉冲信号: ①     用S7-300PLC的时钟存储器 右键点击PLC,选中时钟存储器,默认存储字节为0. 各时钟存储器的周 ...

  9. 前端基础复习之HTML

    1.web基础知识 1 1.Web基础知识 2 1.Internet 3 1.简介 4 Internet 实际上就是由计算机所组成的网络结构 5 6 服务: 7 1.Telnet 8 远程登录 9 2 ...

  10. JSP环境搭建及入门 和 虚拟路径和虚拟主机

    Jsp:是一个动态网页,而不是静态网页 html,css,js,Jquery:是静态网页 动态网页是随着,时间,地点,用户操作,而改变 静态不需要jsp 动态是需要的 BS 可以通过浏览器直接访问浏览 ...