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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.kler.cn/a/4956.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Ubuntu22.04部署Kubernetes集群(亲测可用)

本文将使用kubeadm在Ubuntu22.04上部署k8s集群&#xff0c;kubeadm 是官方社区推出的一个用于快速部署kubernetes 集群的工具&#xff0c;用于快速部署Kubernetes 集群。 虚拟机准备 下载ubuntu22.04镜像&#xff0c;使用vmware部署三台ubuntu22.04虚拟机并配置静态ip和主机名…

TryHackMe-Madeye‘s Castle(boot2root)

Madeye’s Castle 一个boot2root盒子&#xff0c;由Runcode.ninja团队在CuCTF中使用的盒子修改而来 祝你冲进麦德耶的城堡玩得开心&#xff01;在这个房间里&#xff0c;你需要完全枚举系统&#xff0c;站稳脚跟&#xff0c;然后转向几个不同的用户。 端口扫描 循例nmap SMB…

基于springboot和vue实现地方美食分享网站演示【附项目源码】分享

基于springboot和vue实现地方美食分享网站演示开发语言&#xff1a;Java 框架&#xff1a;springboot JDK版本&#xff1a;JDK1.8 服务器&#xff1a;tomcat7 数据库&#xff1a;mysql 5.7 数据库工具&#xff1a;Navicat11 开发软件&#xff1a;eclipse/myeclipse/idea Maven包…

Python 装饰器

一、装饰器 就是给已有函数增加额外功能的函数&#xff0c;它本质上就是一个闭包函数​。类似于Spring的切面&#xff01; 二、应用场景 收集函数错误日志信息验证函数使用权限计算函数运行时间定制函数的输入和输出&#xff08;序列化和反序列化&#xff09; 三、装饰器的特…

基于DDS的SOA测试方案实现

随着以太网技术在车载网络中的应用&#xff0c;各种基于以太网的中间件也相继被应用在车内&#xff0c;如果对车载网络有过相关了解的小伙伴&#xff0c;对于作为中间件之一的DDS&#xff08;数据分发服务Data Distribution Service&#xff09;可能并不陌生&#xff1b;若没有…

mmdetection3d可视化多模态模型推理结果

本篇博文讲一下mmdetection3d可视化 参考文献: 带你玩转 3D 检测和分割 &#xff08;三&#xff09;&#xff1a;有趣的可视化 - 知乎 (zhihu.com) Welcome to MMDetection3D’s documentation! — MMDetection3D 1.0.0rc4 文档 1、介绍 让我们看一下ChatGPT的回答[手动狗头]:…

瑞萨G2UL工业核心板内存测试,您想了解的内容全都有

1. 测试对象 HD-G2UL-EVM基于HD-G2UL-CORE工业级核心板设计&#xff0c;一路千兆网口、一路CAN-bus、3路TTL UART、LCD、WiFi、CSI 摄像头接口等&#xff0c;接口丰富&#xff0c;适用于工业现场应用需求&#xff0c;亦方便用户评估核心板及CPU的性能。 HD-G2UL-CORE系列工业级…

Linux介绍

目录 如何有一个Linux环境 使用终端工具登录 常见Linux命令 ls 查看指定目录中的内容 pwd 查看目录当前完成的绝对路径 cd 切换目录 touch 创建空的文件 cat 查看文件内容 echo 写文件 vim mkdir 创建目录 rm 删除文件/目录 mv 移动文件或目录 cp grep 字符串查找…

WebKitX ActiveX 6.0 X86 Crack

WebKitX ActiveX将 Chromium Embedded Framework (CEF3) 包装到一个进程外的 ActiveX 组件中&#xff0c;以便与 OLE/COM 语言一起使用。Chromium Embedded Framework 封装了 WebKit Blink HTML5 Renderer 和 Google V8 JavaScript Engine。这是一个用于商业用途的生产级稳定组…

Nginx学习(11)—— Nginx源码架构、configure是怎么执行的(编译的具体细节)

文章目录Nginx的源码目录结构Nginx中configure的原理auto脚本模块编译顺序Nginx的源码目录结构 nginx的源码目录与nginx的模块化以及功能的划分是紧密结合&#xff0c;这也使得我们可以很方便地找到相关功能的代码。我们先来看一下nginx源码的目录结构。 使用tree命令看一下&…

大学计划|关于举办《数字化转型赋能教育创新发展高峰论坛》的通知

随着科技革命和产业变革的深入发展&#xff0c;数字化赋能经济社会发展的作用日益凸显&#xff0c;也影响着教育生态的发展变革。目前&#xff0c;社会对复合型人才的需求量不断提高&#xff0c;培养符合社会发展需要的创新型人才也成为高等院校教育数字化转型的重点目标之一。…

第二个项目 基于React技术学习的pc端项目

介绍&#xff1a; 1.本项目使用umi脚手架搭建项目&#xff0c;并将umi升级到3.5.0版本&#xff0c;采用模块化、组件化、工程化模块开发方式&#xff1b; umi脚手架开发有什么好处 为什么要用umi进行项目开发&#xff1f; 2.用react-router-dom实现前端路由部署&#xff0c;使…

Node-包管理工具整套下载使用讲解(nvm、npm、yarn、cnpm、pnpm、nrm)

前言 包管理工具npm&#xff1a; Node Package Manager&#xff0c;就是Node包管理器现在已经不仅仅是node的包管理器了&#xff0c;我们前端项目也都会用它来进行管理项目依赖的包 如何下载和安装npm工具 npm属于node的一个管理工具&#xff0c;所以我们需要先安装Node&#x…

嵌入式软件架构

总目录链接>> AutoSAR入门和实战系列总目录 总目录链接>> AutoSAR BSW高阶配置系列总目录 文章目录1 嵌入式软件分类 – BAREMETAL2 嵌入式软件分类 – 实时操作系统 (RTOS)3 嵌入式软件分类 – “通用”操作系统4 嵌入式软件分类 – 容器/微服务微服务容器5 嵌…

python 绘制训练曲线--Savitzky-Golay 滤波平滑处理

文章目录1 训练曲线--震荡的非常厉害2 Savitzky-Golay 滤波器--平滑曲线3 python 绘制训练曲线--插值法 曲线平滑处理4 python 绘制训练曲线--基于Numpy.convolve曲线平均滤波5 用python自己绘制训练曲线1 训练曲线–震荡的非常厉害 上一篇文章用python自己绘制训练曲线震荡的…

FFmpeg编程入门

音视频术语 容器&#xff0f;文件&#xff08;Conainer/File&#xff09;&#xff1a;即特定格式的多媒体文件&#xff0c;比如mp4、flv、mkv等。媒体流&#xff08;Stream&#xff09;&#xff1a;表示时间轴上的一段连续数据&#xff0c;如一段声音数据、一段视频数据或一段…

强化学习之入门笔记(二)

文章目录强化学习一、Qlearning算法QlearningTD之于Q值估算麻烦来了SARSAQlearning二、深度强化学习三、DQNDeep network Qlearning DQN神经网络的目标四、Policy Gradient策略梯度(Policy Gradient)直观感受PG算法五、Actor-Critic什么是ACTD-error参考强化学习 一、Qlearn…

零售数据分析之操作篇8:用历史聚合巧算库存

各位数据的朋友&#xff0c;大家好&#xff0c;我是老周道数据&#xff0c;和你一起&#xff0c;用常人思维数据分析&#xff0c;通过数据讲故事。 上期内容回顾与作业讲解 上一讲讲了鞋服零售企业非常关注的一个指标售罄率的概念与实现方法&#xff0c;这里我们用到了历史聚…

【最小生成树】一文学懂prim、kruskal算法

博主简介&#xff1a;努力学习的大一在校计算机专业学生&#xff0c;热爱学习和创作。目前在学习和分享&#xff1a;算法、数据结构、Java等相关知识。博主主页&#xff1a; 是瑶瑶子啦所属专栏: 算法 &#xff1b;该专栏专注于蓝桥杯和ACM等算法竞赛&#x1f525;近期目标&…

【教程】使用ChatGPT制作基于Tkinter的桌面时钟

目录 描述 代码 效果 说明 下载 描述 给ChatGPT的描述内容&#xff1a; python在桌面上显示动态的文字&#xff0c;不要显示窗口边框。窗口背景和标签背景都是透明的&#xff0c;但标签内的文字是有颜色。使用tkinter库实现&#xff0c;并以class的形式书写&#xff0c;方…
最新文章