GO实现Redis:GO实现Redis的AOF持久化(4)
- 将用户发来的指令以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)的更多相关文章
- redis学习笔记——RDB和AOF持久化一
为防止数据丢失,需要将 Redis 中的数据从内存中 dump 到磁盘,这就是持久化.Redis 提供两种持久化方式:RDB 和 AOF.Redis 允许两者结合,也允许两者同时关闭. RDB 可以定 ...
- 《Redis设计与实现》- AOF持久化
1. AOF持久化 Redis AOF 持久化是通过保存Redis服务器所执行的写命令来记录数据库状态的. 2. RDB持久化与AOF持久化的区别 RDB持久化 RDB持久化通过保存数据中的键值对来记 ...
- redis源码分析(四)--aof持久化
Redis aof持久化 Redis支持两种持久化方式:rdb与aof,上一篇文章中已经大致介绍了rdb的持久化实现,这篇文章主要介绍aof实现. 与rdb方式相比,aof会使用更多的存储空间,因为它 ...
- 使用AOF持久化文件实现还原Redis数据库并得到RDB持久化文件
目录 1 编写本文的初衷 2 具体实施 2.1 Redis持久化概念简介 2.2 获取指定Redis的AOF持久化文件 2.3 把Redis的持久化AOF文件转换为RDB文件 1 编写本文的初衷 因为 ...
- Redis基础篇(三)持久化:AOF日志
Redis是内存数据库,但是一旦服务器宕机,内存中的数据将会全部丢失. 最简单的恢复方式是从后端数据库恢复,但这种方式有两个问题: 频繁访问数据库,会给数据库带来巨大的压力: 从数据库中读取相比从Re ...
- redis数据库安装 redis持久化及主从复制
----------------------------------------安装redis-5.0.4---------------------------------------- wget h ...
- redis的 rdb 和 aof 持久化的区别 [转]
aof,rdb是两种 redis持久化的机制.用于crash后,redis的恢复. rdb的特性如下: Code: fork一个进程,遍历hash table,利用copy on write,把整个d ...
- 深入剖析 redis AOF 持久化策略
本篇主要讲的是 AOF 持久化,了解 AOF 的数据组织方式和运作机制.redis 主要在 aof.c 中实现 AOF 的操作. 数据结构 rio redis AOF 持久化同样借助了 struct ...
- Redis数据持久化之AOF持久化
一.RDB持久化的缺点创建RDB文件需要将服务器所有的数据库的数据都保存起来,这是一个非常耗费资源和时间的操作,所以服务器需要隔一段时间才能创建一个新的RDB文件,就也是说创建RDB文件的操作不能执行 ...
- Redis的两种持久化方式-快照持久化和AOF持久化
Redis为了内部数据的安全考虑,会把本身的数据以文件形式保存到硬盘中一份,在服务器重启之后会自动把硬盘的数据恢复到内存(redis)的里边,数据保存到硬盘的过程就称为"持久化"效 ...
随机推荐
- python + appium 常用公共方法封装
appium 程序下载安装见之前的帖子:https://www.cnblogs.com/gancuimian/p/16536322.html appium 环境搭建见之前的帖子:https://www ...
- Hive. 函数 instr 的用法
INSTR(C1,C2,I,J) 在一个字符串中搜索指定的字符,返回发现指定的字符的位置; C1 被搜索的字符串 C2 希望搜索的字符串 I 搜索的开始位置,默认为1 J 出现的位置,默认为1 sel ...
- 向excel中写数据的python代码
①首先需要安装xlwt插件 安装语句:pip install xlwt ②python代码 1 import xlwt 2 ''' 3 调用xlwt模块中的Workbook方法来创建一个excel表格 ...
- jupyter nbconvert --to FORMAT xxx
$ jupyter nbconvert --to FORMAT notebook.ipynb This will convert the Jupyter notebook file notebook. ...
- Linux系统管理实战-配置静态IP
配置静态IP 前置条件 防火墙: EL7 EL6 查看状态: # systemctl status firewalld # /etc/init.d/iptables status 立即关闭: # sy ...
- intellij idea中怎么没有git版本控制设置项
在使用intellij idea的时候想要使用git进行版本控制,但是在设置项和界面没有发现相关内容,怎么回事呢? 我们先打开电脑,从桌面的快捷方式打开intellij idea,进入到intelli ...
- oracle 用户连接数查询
oracle 用户连接数查询 --当前的连接数 select count(*) from v$session; --数据库允许的最大连接数 select value from v$parameter ...
- C++的万能引用解析
C++11除了带来了右值引用以外,还引入了一种称为"万能引用"的语法:通过"万能引用",对某型别的引用T&&,既可以表达右值引用,也可以表达左值 ...
- C语言基础编程题
//1.ASCII码值 ->相应字符 #include <stdio.h> int main() { int asc; scanf("%d",&asc); ...
- PHP判断0和空的方法
可以兼容,传参数,或者不参数与0的判断 if ( isset($data['other_id']) && (!empty($data['other_id']) || is_nume ...