godis源码分析——database存储核心1
前言
redis的核心是数据的快速存储,下面就来分析一下godis的底层存储是如何实现,先分析单机服务。
此文采用抓大放小原则,先大的流程方向,再抓细节。
流程图
源码分析
现在以客户端连接,并发起set key val命令为例子
在单机部署的时候,服务启动,会创建一个处理实例,并创建一个单机的db
// redis/server.go
// 创建一个处理实例
// MakeHandler creates a Handler instance
func MakeHandler() *Handler {// redis的一个存储引擎var db database.DB// 创建是集群还是单例if config.Properties.ClusterEnable {db = cluster.MakeCluster()} else {db = database2.NewStandaloneServer()}return &Handler{db: db,}
}
有客户端连接,会生成一个异步方法处理每个客户端,一旦有客户端的消息,都会进入Handle方法。
// redis/server/server.go
// 处理接收到客户端的命令
// Handle receives and executes redis commands
func (h *Handler) Handle(ctx context.Context, conn net.Conn) {if h.closing.Get() {// closing handler refuse new connection_ = conn.Close()return}client := connection.NewConn(conn)// 存储一个客户端h.activeConn.Store(client, struct{}{})// 获取字符串ch := parser.ParseStream(conn)// 接收客户端数据for payload := range ch {// 遍历消息体// ......... 经过各种校验// 获取到客户端信息r, ok := payload.Data.(*protocol.MultiBulkReply)if !ok {logger.Error("require multi bulk protocol")continue}// 执行结果result := h.db.Exec(client, r.Args)// 结果回复if result != nil {_, _ = client.Write(result.ToBytes())} else {_, _ = client.Write(unknownErrReplyBytes)}}
}
客户端的各种命令进行判断,set是属于正常的数据操作命令,直接通过判断,获取数据库,并在当前数据库中执行
// database/server.go
func (server *Server) Exec(c redis.Connection, cmdLine [][]byte) (result redis.Reply) {defer func() {if err := recover(); err != nil {logger.Warn(fmt.Sprintf("error occurs: %v\n%s", err, string(debug.Stack())))result = &protocol.UnknownErrReply{}}}()cmdName := strings.ToLower(string(cmdLine[0]))// pingif cmdName == "ping" {return Ping(c, cmdLine[1:])}// authenticateif cmdName == "auth" {return Auth(c, cmdLine[1:])}// ........// 各种各样的判断,暂时不管// 获取当前的数据索引// normal commandsdbIndex := c.GetDBIndex()// 获取当前数据库selectedDB, errReply := server.selectDB(dbIndex)if errReply != nil {return errReply}// 以当前数据库,执行命令return selectedDB.Exec(c, cmdLine)
}
命令名称解析出来后,从cmdTable获取对应的执行方法,如prepare、executor
// Exec executes command within one database
func (db *DB) Exec(c redis.Connection, cmdLine [][]byte) redis.Reply {// transaction control commands and other commands which cannot execute within transactioncmdName := strings.ToLower(string(cmdLine[0]))// ...return db.execNormalCommand(cmdLine)
}func (db *DB) execNormalCommand(cmdLine [][]byte) redis.Reply {// 获取到正常的执行命令cmdName := strings.ToLower(string(cmdLine[0]))// 获取到commondcmd, ok := cmdTable[cmdName]if !ok {return protocol.MakeErrReply("ERR unknown command '" + cmdName + "'")}if !validateArity(cmd.arity, cmdLine) {return protocol.MakeArgNumErrReply(cmdName)}prepare := cmd.preparewrite, read := prepare(cmdLine[1:])db.addVersion(write...)// 数据库上锁db.RWLocks(write, read)// 命令执行完后解锁defer db.RWUnLocks(write, read)// 执行命令方法fun := cmd.executorreturn fun(db, cmdLine[1:])
}
set命令对应的方法,从代码可以发现,其实数据是存储在定义的map结构的集合中,自此,命令已经执行完毕,返回执行结果。
func execSet(db *DB, args [][]byte) redis.Reply {// 提取keykey := string(args[0])// 提取valvalue := args[1]// 提取策略policy := upsertPolicy// 提取过期时间ttl := unlimitedTTL// parse options// 如何参数大于2个,说明有其他参数,需要做其他处理// .....entity := &database.DataEntity{Data: value,}var result int// 更新策略switch policy {case upsertPolicy:// 默认策略db.PutEntity(key, entity)result = 1case insertPolicy:result = db.PutIfAbsent(key, entity)case updatePolicy:result = db.PutIfExists(key, entity)}if result > 0 {if ttl != unlimitedTTL {expireTime := time.Now().Add(time.Duration(ttl) * time.Millisecond)// 设置过期时间db.Expire(key, expireTime)db.addAof(CmdLine{[]byte("SET"),args[0],args[1],})db.addAof(aof.MakeExpireCmd(key, expireTime).Args)} else {db.Persist(key) // override ttldb.addAof(utils.ToCmdLine3("set", args...))}}if result > 0 {return &protocol.OkReply{}}return &protocol.NullBulkReply{}
}// database.go
// 将数据放入DB
// PutEntity a DataEntity into DB
func (db *DB) PutEntity(key string, entity *database.DataEntity) int {// 当前数据库的数据字段ret := db.data.PutWithLock(key, entity)// db.insertCallback may be set as nil, during `if` and actually callback// so introduce a local variable `cb`if cb := db.insertCallback; ret > 0 && cb != nil {cb(db.index, key, entity)}return ret
}// datastruct/dict/concurrent.go
// ConcurrentDict is thread safe map using sharding lock
// 这里可以看出,数据其实就是存在map集合里面
type ConcurrentDict struct {table []*shardcount int32shardCount int
}type shard struct {m map[string]interface{}mutex sync.RWMutex
}// datastruct/dict/concurrent.go
func (dict *ConcurrentDict) PutWithLock(key string, val interface{}) (result int) {if dict == nil {panic("dict is nil")}hashCode := fnv32(key)index := dict.spread(hashCode)s := dict.getShard(index)// 将数据放入map中if _, ok := s.m[key]; ok {s.m[key] = valreturn 0}dict.addCount()// 存储kv结构数据,完成s.m[key] = valreturn 1
}
其实还有一个问题,就是cmdTable怎么来的,为什么fun(db, cmdLine[1:])就完成了?
在router.go这个代码中,是生成一个新的cmdTable的map集合;registerCommand这个函数是将各种命令塞入cmdTable里面。每个数据结构如string等都有定义的方法。
main启动前都会调用init(),这个是golang特殊的函数,顺序按照文件的顺序执行。
这里就是在服务启动前,将所有命令注册到cmdTable集合。
// database/router.go
// 命令集
var cmdTable = make(map[string]*command)
// ....
// 注册命令,将命令存放在cmdTable集合里面
// registerCommand registers a normal command, which only read or modify a limited number of keys
func registerCommand(name string, executor ExecFunc, prepare PreFunc, rollback UndoFunc, arity int, flags int) *command {name = strings.ToLower(name)cmd := &command{name: name,executor: executor,prepare: prepare,undo: rollback,arity: arity,flags: flags,}cmdTable[name] = cmdreturn cmd
}//========================================// database/string.gofunc execSet(db *DB, args [][]byte) redis.Reply {
//....
}// execSetNX sets string if not exists
func execSetNX(db *DB, args [][]byte) redis.Reply {// .....
}// execSetEX sets string and its ttl
func execSetEX(db *DB, args [][]byte) redis.Reply {// ...
}func init() {// 调用注册命令函数,注册方法,如Set则是执行execSet方法registerCommand("Set", execSet, writeFirstKey, rollbackFirstKey, -3, flagWrite).attachCommandExtra([]string{redisFlagWrite, redisFlagDenyOOM}, 1, 1, 1)registerCommand("SetNx", execSetNX, writeFirstKey, rollbackFirstKey, 3, flagWrite).attachCommandExtra([]string{redisFlagWrite, redisFlagDenyOOM, redisFlagFast}, 1, 1, 1)registerCommand("SetEX", execSetEX, writeFirstKey, rollbackFirstKey, 4, flagWrite).attachCommandExtra([]string{redisFlagWrite, redisFlagDenyOOM}, 1, 1, 1)// .....
}
ExecFunc是规范方法,每个命令对应的执行都按照规范定义。
// database/router.gotype command struct {// 命令名称name string// 执行方法executor ExecFunc// prepare returns related keys commandprepare PreFunc// undo generates undo-log before command actually executed, in case the command needs to be rolled backundo UndoFunc// arity means allowed number of cmdArgs, arity < 0 means len(args) >= -arity.// for example: the arity of `get` is 2, `mget` is -2arity intflags intextra *commandExtra
}// ========================================// database/database.go
// 执行方法接口
// ExecFunc is interface for command executor
// args don't include cmd line
type ExecFunc func(db *DB, args [][]byte) redis.Reply
相关文章:
![](https://i-blog.csdnimg.cn/direct/8aca9fb869374a9297d0345d55131abb.png)
godis源码分析——database存储核心1
前言 redis的核心是数据的快速存储,下面就来分析一下godis的底层存储是如何实现,先分析单机服务。 此文采用抓大放小原则,先大的流程方向,再抓细节。 流程图 源码分析 现在以客户端连接,并发起set key val命令为例…...
![](https://i-blog.csdnimg.cn/direct/26fe589ad8da4892bdd7c7551b16c1f6.png)
【UE5.1】Chaos物理系统基础——06 子弹破坏石块
前言 在前面我们已经完成了场系统的制作(【UE5.1】Chaos物理系统基础——02 场系统的应用_ue5)以及子弹的制作(【UE5.1 角色练习】16-枪械射击——瞄准),现在我们准备实现的效果是,角色发射子弹来破坏石柱。…...
![](https://www.ngui.cc/images/no-images.jpg)
Django是干什么的?好用么?
Django是一个开源的Python Web框架,用于快速开发高质量的Web应用程序。它提供了许多功能和工具,以简化常见的Web开发任务,如路由、请求处理、数据库管理等。 Django的优点包括: 简单易用:Django提供了清晰的文档和丰…...
![](https://www.ngui.cc/images/no-images.jpg)
C语言实现数据结构B树
B树(B-Tree)是一种自平衡的树数据结构,它维护着数据的有序性,并允许搜索、顺序访问、插入、删除等操作都在对数时间内完成。B树广泛用于数据库和操作系统的文件系统中。 B树的基本特性 根节点:根节点至少有两个子节点…...
![](https://i-blog.csdnimg.cn/direct/5cafcc7076dc4d0aaae502cf92354fcc.png)
[论文阅读]MaIL: Improving Imitation Learning with Mamba
Abstract 这项工作介绍了mamba模仿学习(mail),这是一种新颖的模仿学习(il)架构,为最先进的(sota)变换器策略提供了一种计算高效的替代方案。基于变压器的策略由于能够处理具有固有非…...
![](https://www.ngui.cc/images/no-images.jpg)
在HTML中使用JavaScript
在 HTML 中使用 JavaScript 有以下几种常见的方式: 一、内联脚本 (一)基本语法 内联脚本是将 JavaScript 代码直接嵌入到 HTML 文件的 <script> 标签内部。 <!DOCTYPE html> <html lang"en"> <head> <…...
![](https://img-blog.csdnimg.cn/img_convert/ca94718a7746096d572a0aae3a236717.png)
InjectFix 热更新解决方案
简介 今天来谈一谈,项目种的客户端热更新解决方案。InjectFix是腾讯xlua团队出品的一种用于Unity中C#代码热更新热修复的解决方案。支持Unity全系列,全平台。与xlua的思路类似,InjectFix解决的痛点主要在于Unity中C#代码写的逻辑在发包之后无…...
![](https://i-blog.csdnimg.cn/direct/41c38cca1b0b4a9bba82a969a6d12597.png)
PHP7.4安装使用rabbitMQ教程(windows)
(1),安装rabbitMQ客户端erlang语言 一,erlang语言安装 下载地址1—— 下载地址2——https://www.erlang.org/patches/otp-27.0 二,rabbitMQ客户端安装 https://www.rabbitmq.com/docs/install-windows (…...
![](https://i-blog.csdnimg.cn/direct/ed3b4e309ccb402e909b0b4b9d46146d.png)
分页以及tab栏切换,动态传类型
<view class"disTitle"><view class"disName">账户明细</view><view class"nav"><u-tabs lineWidth"0" :activeStyle"{color: #FD893F }" :list"navList" change"tabsChange&quo…...
![](https://i-blog.csdnimg.cn/direct/ef4fce99b88e43aaa9a586790a7fae6c.png#pic_center)
【算法】平衡二叉树
难度:简单 题目 给定一个二叉树,判断它是否是 平衡二叉树 示例: 示例1: 输入:root [3,9,20,null,null,15,7] 输出:true 示例2: 输入:root [1,2,2,3,3,null,null,4,4] 输出&…...
![](https://i-blog.csdnimg.cn/direct/5e4c33fadf8b47da99b973e3108fa5bf.png)
五、 计算机网络(考点篇)
1 网络概述和模型 计算机网络是计算机技术与通信技术相结合的产物,它实现了远程通信、远程信息处理和资源共享。计算机网络的功能:数据通信、资源共享、管理集中化、实现分布式处理、负载均衡。 网络性能指标:速率、带宽(频带宽度或传送线路…...
![](https://www.ngui.cc/images/no-images.jpg)
如何解决数据分析问题:IPython与Pandas结合
如何解决数据分析问题:IPython与Pandas结合 数据分析是现代科学研究、商业决策和技术开发中的一个重要环节。IPython和Pandas是两个强大的工具,它们可以大大简化和加速数据分析的过程。本文将为初学者详细介绍如何结合使用IPython和Pandas来解决数据分析…...
![](https://i-blog.csdnimg.cn/direct/53cdfc2ea70a412ca9a0673f904534c9.png)
如何在 Microsoft Edge 上使用开发人员工具
Microsoft Edge 提供了一套强大的开发人员工具,可帮助 Web 开发人员检查、调试和优化他们的网站或 Web 应用程序。 无论您是经验丰富的 Web 开发人员还是刚刚起步,了解如何有效地使用这些工具都可以对开发过程产生重大影响。 在本文中,我们…...
![](https://i-blog.csdnimg.cn/direct/c509b81030014413b01bfb4e2e626e40.png)
《Linux系统编程篇》认识在linux上的文件 ——基础篇
前言 Linux系统编程的文件操作如同掌握了一把魔法钥匙,打开了无尽可能性的大门。在这个世界中,你需要了解文件描述符、文件权限、文件路径等基础知识,就像探险家需要了解地图和指南针一样。而了解这些基础知识,就像学会了魔法咒语…...
![](https://i-blog.csdnimg.cn/direct/ea4f6c69b89143ed9688bb02dbb76ba2.png)
Qt:22.鼠标相关事件(实例演示——鼠标进入/离开某控件的事件、鼠标按下事件、鼠标释放事件、鼠标双击事件)
目录 1.实例演示——鼠标进入/离开某控件的事件: 2.鼠标按下事件: 3.鼠标释放事件: 4.鼠标双击事件: 1.实例演示——鼠标进入/离开某控件的事件: 首先创建一个C类文件 Label,填写好要继承的父类 QLabe…...
![](https://i-blog.csdnimg.cn/direct/99bf491d80dd4e06a916e074fc08309c.png)
笔记 4 :linux 0.11 中继续分析 0 号进程创建一号进程的 fork () 函数
(27)本条目开始, 开始分析 copy_process () 函数,其又会调用别的函数,故先分析别的函数。 get_free_page () ; 先 介绍汇编指令 scasb : 以及 指令 sstosd :…...
![](https://i-blog.csdnimg.cn/direct/406b9599dd2d410e8b80b269caa132e5.png)
Vue3 引入Vanta.js使用
能搜到这篇文章 想必一定看过demo效果图了吧 示例 Vanta.js - Animated 3D Backgrounds For Your Website (vantajs.com) 1. 引入 在根目录 index.html中引入依赖 <script src"https://cdnjs.cloudflare.com/ajax/libs/three.js/r134/three.min.js"></sc…...
![](https://i-blog.csdnimg.cn/direct/bb254161b24742619559b81d71ab41ba.png)
LeetCode --- 134双周赛
题目 3206. 交替组 I 3207. 与敌人战斗后的最大分数 3208. 交替组 II 3209. 子数组按位与值为 K 的数目 一、交替组 I & II 题目中问环形数组中交替组的长度为3的子数组个数,主要的问题在于它是环形的,我们要考虑首尾相接的情况,如何…...
![](https://i-blog.csdnimg.cn/direct/27785dc454a547f796bccc545eef984d.png)
快速读出linux 内核中全局变量
查问题时发现全局变量能读出来会提高效率,于是考虑从怎么读出内核态的全局变量,脚本如下 f open("/proc/kcore", rb) f.seek(4) # skip magic assert f.read(1) b\x02 # 64 位def read_number(bytes):return int.from_bytes(bytes, little,…...
![](https://i-blog.csdnimg.cn/direct/58693513d93541a8a699fad79fb239a5.png)
postman录制设置
一、前言: postman是一个很好接口调试或是测试工具,简单方便,不需要很复杂的流程与技术,并且也具备录制条件。对于接口不了解,没有明确对应的说明,但又想通过接口进行一些测试使用其录制是一个不错的办…...
![](https://i-blog.csdnimg.cn/direct/bc19d7fded664ca092f00ea8bbdc4cc9.png)
redis消息队列
redis 的list类型实现消息队列: list结构实现的优缺点: 2、pubsub模式(消息发布订阅)实现消息队列 pubsub的优缺点: 命令行实现: pub:第一次发送有两个接收,第二个只有一个接收 sub接收&#x…...
![](https://i-blog.csdnimg.cn/direct/1a498df91d0a401aa9e1f82c3ba02d41.png)
Linux vim的使用(一键安装则好用的插件_forcpp),gcc的常见编译链接操作
vim 在Linux系统上vim是个功能还比较完善的软件。但是没装插件的vim用着还是挺难受的,所以我们直接上一款插件。 我们只需要在Linux上执行这个命令就能安装(bite提供的) curl -sLf https://gitee.com/HGtz2222/VimForCpp/raw/master/install.sh -o ./install.sh …...
![](https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=C%3A%5CUsers%5CaDMIN%5CAppData%5CRoaming%5CTypora%5Ctypora-user-images%5Cimage-20240713150844844.png&pos_id=img-UDcJW3Ot-1720872886067)
css基础(1)
CSS CCS Syntax CSS 规则由选择器和声明块组成。 CSS选择器 CSS选择器用于查找想要设置样式的HTML元素 一般选择器分为五类 Simple selectors (select elements based on name, id, class) 简单选择器(根据名称、id、类选择元素) //页面上的所有 …...
![](https://i-blog.csdnimg.cn/direct/e3f247a8bb38412abdf00863f2ecc6c0.png)
高并发线程池设计Nginx线程池源码剖析
为什么我们需要线程池?Why? 省流: 为了解决: 1.访问磁盘速度慢 2.等待设备工作 3..... 我们使用多线程技术,在IO繁忙的时候优先处理别的任务 为了解决多线程的缺陷: 1.创建、销毁线程时间消耗大 2.创建线程太多使系统资源不足或者线程频繁切换…...
![](https://i-blog.csdnimg.cn/direct/94ee638781e147bb8884b20c0c3be4ee.jpeg)
SEO:6个避免被搜索引擎惩罚的策略-华媒舍
在当今数字时代,搜索引擎成为了绝大多数人获取信息和产品的首选工具。为了在搜索结果中获得良好的排名,许多网站采用了各种优化策略。有些策略可能会适得其反,引发搜索引擎的惩罚。以下是彭博社发稿推广的6个避免被搜索引擎惩罚的策略。 1. 内…...
STM32之六:SysTick系统滴答定时器
目录 1. SysTick简介 2. 时钟来源 3. SysTick寄存器 3.1 CTRL—SysTick控制及状态寄存器 3.2 RELOAD—SysTick重装载数值寄存器 3.3 CURRENT—SysTick当前数值寄存器 4. systick系统定时器配置 5. 延时函数实现 5.1 延时函数编写步骤 5.2 微秒级延时函数delay_us 5.…...
![](https://i-blog.csdnimg.cn/direct/02508da0900d4be2bd02fee89661be00.png)
全栈物联网项目:结合 C/C++、Python、Node.js 和 React 开发智能温控系统(附代码示例)
1. 项目概述 本文详细介绍了一个基于STM32微控制器和AWS IoT云平台的智能温控器项目。该项目旨在实现远程温度监控和控制,具有以下主要特点: 使用STM32F103微控制器作为主控芯片,负责数据采集、处理和控制逻辑采用DHT22数字温湿度传感器,精确采集环境温湿度数据通过ESP8266 W…...
![](https://i-blog.csdnimg.cn/direct/e7a5562ac8684b8d9e890816a8b3d746.png)
WPF学习(3) -- 控件模板
一、操作过程 二、代码 <Window x:Class"学习.MainWindow"xmlns"http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x"http://schemas.microsoft.com/winfx/2006/xaml"xmlns:d"http://schemas.microsoft.com/expressio…...
![](https://www.ngui.cc/images/no-images.jpg)
Netty Websocket SpringBoot Starter
netty websocket starter Quick Start Demo 项目 添加依赖 <!--添加源--> <repository><id>github</id><url>https://maven.pkg.github.com</url><snapshots><enabled>true</enabled></snapshots> </reposit…...
![](https://i-blog.csdnimg.cn/direct/798118bb4fda4ba9a34ee230cfad36a4.png)
数据结构(4.2)——朴素模式匹配算法
字符串模式匹配 在主串中找到模式串相同的子串,并返回其所在的位置。 子串和模式串的区别 子串:主串的一部分,一定存在 模式串:不一定能在主串中找到 字符串模式匹配 朴素模式匹配算法 主串长度为n,模式串长度为…...
![](/images/no-images.jpg)
网站案例/电商平台运营方案
今天笔试的东西,重载,覆盖和隐藏 重载与覆盖成员函数被重载的特征:(1)相同的范围(在同一个类中); // 这个很重要,如在不同类中则是隐藏(2)函数名字…...
![](/images/no-images.jpg)
找人做网赌网站需要多少钱/seowhy培训
先简单看一下主过程: int MAXFLOW() {hights();prepare();while (!Q.empty()) {u Q.get;for each e in G (from u) push(e);if (!fixed(u)) reCalc(u);} }接下来介绍算法 预流推进算法给每一个顶点一个标号h(v),表示该点到t的最短路(在残量…...
![](https://www.oschina.net/img/hot3.png)
html 网站源码 卖手机/互联网营销的方法
2019独角兽企业重金招聘Python工程师标准>>> (index):11 Uncaught ReferenceError: testAlert is not definedat HTMLInputElement.onclick ((index):11)解决: if后面漏了{。 要从chrome控制台看。否则很难定位。 Uncaught SyntaxError: Unexpected toke…...
![](/images/no-images.jpg)
规范网站维护 建设 管理/seo推广费用
<< Back to linux.chinaitlab.com 红帽企业 Linux 4安全指南版权 © 2005 Red Hat, Inc.ISBN: N/A 目录介绍1. 体系特有的信息2. 文档约定3. 激活订阅3.1. 提供红帽登录帐号3.2. 提供你的订阅号码3.3. 连接系统4. 未来的扩充4.1. 递交你的反馈I. 安全问题概述1章 . 安…...
![](/images/no-images.jpg)
汽车网站建设页面/梅州seo
在交换机中使用一个函数就可以了:将调用该函数,并返回一个值 – 这是一个将用于案例的值.它和写作完全一样:$my_var function_foo($bar,$bar2);switch ($my_var) {// ...}即使我更喜欢使用变量,所以代码更容易阅读.在案例中使用变量是您经常看不到的;但…...
![](/images/no-images.jpg)
网站建设的相关新闻/郑州网站制作公司哪家好
Tomcat 服务器网页部署,登录需用户名/密码,编写了一个简单的Python脚本来测试一些简单的弱口令。 测试环境:Tomcat版本 7.0 登录界面采用basic认证,Base 64加密一下,模拟浏览器进行发包 据测试,每个用户名输…...