• 将用户发来的指令以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. request提取json中的信息

    1 import requests 2 3 response = requests.get(host) 4 5 #无需import json库 6 7 i = respon.json() 8 9 #此 ...

  2. Edge浏览器相关

    Ctrl+T 新建页面 Ctrl+W 关闭页面 不可复制的页面,前加read: 若是不可访问 变read://http:// 阅读可翻译,也可有图片字典 还可用插件: 截图软件:Snipaste  微 ...

  3. Django基础篇 07-Django后端和html交互

    一.上下文管理器 context_processors 执行顺序是 先执行views.py中代码,在views返回之前在走到TEMPLATES.OPTIONS.context_processors的方 ...

  4. 【JavaScript】JS写法随笔(三) JS联动设置元素默认值

    问题: 使用DOM获取元素后setAttribute("value", "1")在页面有修改此标签value的情况下,再次触发function发生不生效.无法修 ...

  5. 【APT】Patchwork APT组织针对巴基斯坦国防官员攻击活动分析

    前言 Patchwork(白象.摩诃草.APT-C-09.Dropping Elephant)是一个疑似具有印度国家背景的APT组织,该组织长期针对中国.巴基斯坦等南亚地区国家进行网络攻击窃密活动.本 ...

  6. 记录一次 网关负载 流量不均匀 cpu使用率不均衡问题

    网关负载  流量不均匀  cpu使用率不均衡问题??? 1.压力机访问源  有多少ip 有10个? 还是20个? 就是样本源不多的话,负载上hash的话  就你可能不是真实的访问需求 ,你客户端就那么 ...

  7. Django框架搭建web项目(一)

    建议查看官方文档:https://docs.djangoproject.com/zh-hans/4.0/intro/tutorial01/ 1.本地安装python环境(略) 2.本地安装Django ...

  8. reflection反射

    reflection反射 动态和静态语言 动态语言 动态语言就是一类在运行时可以改变其结构的语言,通俗点说就是在运行时代码可以根据某些条件改变自身结构 主要动态语言:object-C,C#,JavaS ...

  9. c# + appium 连接设备自动化

    //private static AndroidDriver<AppiumWebElement> _driver; //private static AppiumLocalService ...

  10. 电脑日常维护技巧(windows系统)

    一.磁盘检测 cmd-->chkdsk 二.磁盘修复 cmd-->sfc/scannow 三.删除缓存文件 运行-->%temp%