【Redis-03】Redis数据库的实现原理

news2025/1/9 1:17:40

 在之前的文章我们介绍过,Redis服务器在启动之初,会初始化RedisServer的实例,在这个实例中存在很多重要的属性结构,同理本篇博客中介绍的数据库实现原理也会和其中的某些属性相关,我们继续看一下吧。

1.服务器和客户端实现的数据库

 Redis服务器在启动时,会根据redis.conf文件的中databases xx这个配置决定创建多少个数据库(默认配置是16),启动后默认使用的0号数据库,当然可以使用select dbnum这个命令来切换。需要注意的是在redis集群模式下,只有0号数据库可以用,是无法切换到其他库的。
 Redis服务器会将所有的数据库都保存在服务器状态的redisServer的db数组中,数组的每一项都代表了一个数据库,用redisDb结构来表示,首先看一下redisServer.db的源码:

struct redisServer {
	...
	// 代表数据库的数组
    redisDb *db;
    // 这个记录的配置文件中数据库的数量
	int dbnum;  
    ...
}

 我们通过客户端向Redis写入的任何数据都会记录到这个db数组中,根据前面描述,我们知道可以通过select命令切换到另一个目标数据库,但是客户端是怎么记录的它当前操作的哪个数据库呢?我们继续看一下源码:

typedef struct client {
    ...
    // 指针指向当前客户端正在操作的数据库
    redisDb *db;            /* Pointer to currently SELECTed DB. */
	...
} client;

 看,在client客户端状态中,有一个db指针,指向了server.db数组中的某一项,代表了当前客户端正在操作的数据库。所以通过切换client.db的指针,调整客户端操作的数据库,这就是select命令的实现原理。
在这里插入图片描述

2.数据库字典的实现

 Redis是支持key-value键值对存储的,这其实是通过dict结构来实现的,在前面讲到的内容中,服务器和客户端都指向了一个redisDb的结构,在这个db结构中,就包含存储了键值对的字典结构,首先看一下源码:

typedef struct redisDb {
	...
	// 这个存放的就是键值对
    dict *dict;                 /* The keyspace for this DB */
    // 这个存放的是键值对的过期时间,下面一节会说到
    dict *expires;              /* Timeout of keys with a timeout set */
    ...
} redisDb;

 dict这个指针就指向了存储键值对的字典结构,key是字符串robj类型,value可以是任何的robj类型。当我们分别新增、删除、更新或者查询的时候,其实就是根据输入的key在这个字典上做curd的操作。我们在Redis写入两个键值对,图示如下:
在这里插入图片描述
 除了对数据库键值对的curd操作,基于整个数据操作的一些命令也是在这个dict上面实现的,比如清空所有键值对的flushdb,或者exists、del、dbsize命令等等。在执行命令前后,redis还会执行一些其他操作,比如检查是否超出最大内存,更新lru时间,记录慢查询日志,或者向monitor客户端发送命令等等,这就是redis数据字典的实现原理。

3.键值对的生命周期管理

 这里说的生命周期,其实就是指键值对的过期时间。通常我们使用expire key这个命令设置键的过期时间,但其实Redis是有四个命令支持设置过期时间的:

  • expire key seconds 将key的生命周期设置为second秒;
  • pexpire key milliseconds 将key的生命周期设置为milliseconds毫秒;
  • expireat key timestamp 将key的过期时间设置在timestamp这个秒的时间戳过期;
  • pexpireat key timestamp 将key的过期时间设置在timestamp这个毫秒的时间戳过期;

 值得说明的是,虽然有这么多命令支持设置过期时间,但是最终经过转换都是指向pexpireat这一个命令来实现。现在的问题是,这么多键值对的过期时间,在redis服务端是怎么保存和维护的呢?
 前面在看redisDb源码的时候,有一个expires属性,我们再把源码拿过来看一下:

typedef struct redisDb {
	...
    // 这个存放的是键的过期时间
    dict *expires;              /* Timeout of keys with a timeout set */
    ...
} redisDb;

 这就很清晰了,通过expires这个指针,指向了一个dict结构,字典中记录的就是所有键值对的过期时间。其中,key是键值对的键,value是long类型的毫秒精度的unix时间戳,即过期的时间点。值得注意的是,保存键值对的dict字典和保存过期时间的expires字典,key指针都指向相同的一个键字符串对象,所以在内存空间上是不会存在浪费的。
在这里插入图片描述

 除此之外,跟过期时间操作相关的两个命令,当然也是基于expires这个字典来实现的:

  • ttl 返回键值对的剩余时间
  • persist删除键值对的过期时间

4.过期键的管理策略

 既然有过期时间,那么键值对过期之后,是不是立即被删除了呢?答案肯定不是,redis通过惰性删除和定期删除两种策略实现对过期键的管理:

  • 惰性删除策略:当程序访问到某个键值对的时候,会对过期时间检查,如果过期就删除,否则不处理。
  • 定期删除策略:基于serverCron时间事件函数,从一定数量的数据库中取出一定数量的随机键进行检查,并删除其中过期的键值对。

 使用这两种过期键管理策略可以最大程度上在合理使用CPU时间和避免浪费内存空间之间取得平衡。

5.持久化对过期键的处理

  • rdb 持久化
    • save或者bgsave会检查键的过期时间,已过期的键不会保存到的持久化的rdb文件中。
    • 服务器启动载入rdb文件时,如果是主服务器,过期键会被忽略加载;如果是从服务器,不论是否过期,都会被加载。
  • aof 持久化
    • 写入aof文件时,key是会写入的,过期之后,通过追加del命令,才会显示的删除此过期键。
    • bgrewriteaof 重写时会检查键的过期时间,已过期的键不会写入新的aof文件中。
    • 服务器启动载入aof文件时,过期键也会被忽略,不会被加载。

6.主从复制对过期键的处理

 主从复制,为了保证数据的一致性,通常由主服务器执行更新的操作,然后将命令发送给从服务器。在3.2版本之前,由于惰性删除策略的存在,主服务器遇到对过期键的访问,会删除此键值对,并给客户端返回null值,但是从服务器由于不能执行删除操作,即便是此键已过期,也会返回对应的value值,出现数据不一致导致的脏读问题。
 在3.2版本之后,这个问题得到了修改,从服务器会判断当前键是否过期,如果已过期并且是从服务器的话,也会返回null值。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

基于改进萤火虫算法的图像分割的应用(Matlab代码实现)

🍒🍒🍒欢迎关注🌈🌈🌈 📝个人主页:我爱Matlab 👍点赞➕评论➕收藏 养成习惯(一键三连)🌻🌻🌻 🍌希…

可执行文件的装载与进程

进程虚拟地址空间 每个程序被运行起来以后,它将拥有自己独立虚拟空间地址,这个虚拟地址空间的大学由计算机的硬件平台决定,具体地说是由CPU的位数决定。硬件决定了地址空间的最大理论上限,即硬件的寻址空间大小,比如32…

欢迎使用Markdown编辑器

欢迎使用Markdown编辑器欢迎使用Markdown编辑器新的改变功能快捷键合理的创建标题,有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants创建一个自定义列表如何创建一个…

HTML的常用结构标签(详细)

1.文本标题 &#x1f340; <h1> </h1>~~~<h6> </h6>,从h1到h6字体由大到小 2.段落 &#x1f340; <p> </p> 3.加粗 &#x1f340; <b> </b> 和 <strong> </strong> 4.倾斜 &#x1f340; <i></i&…

[MQ] SpringBoot使用扇型(广播)交换机/主题交换机

✨✨个人主页:沫洺的主页 &#x1f4da;&#x1f4da;系列专栏: &#x1f4d6; JavaWeb专栏&#x1f4d6; JavaSE专栏 &#x1f4d6; Java基础专栏&#x1f4d6;vue3专栏 &#x1f4d6;MyBatis专栏&#x1f4d6;Spring专栏&#x1f4d6;SpringMVC专栏&#x1f4d6;SpringBoot专…

刷式过滤器 不锈钢全自动刷式过滤器

原理概述 当水从进水口进入过滤器滤筒内部&#xff0c;杂质被拦截在过滤筒内壁&#xff0c;过滤后的干净水从出水口流出&#xff0c;当滤筒内壁的杂质越积越多时&#xff0c;自清洗过滤器进出口的压差达到预设值、达到清洗时间或手动预制时&#xff0c;过滤器将开始自清洗过程…

Feng Office 3.7.0.5 - 文件上传

Feng Office 3.7.0.5 - 文件上传 POST /ck_upload_handler.php HTTP/1.1 Host: www.baidu1.com Content-Length: 213 Cache-Control: max-age0 Upgrade-Insecure-Requests: 1 Origin: http://www.baidu1.com Content-Type: multipart/form-data; boundary----WebKitFormBoundar…

Linux基本指令2——时间相关

Linux内核&#xff1a;Centos 7.6 64位 date指令默认的date不适合阅读date 指定格式显示时间&#xff1a; date %Y:%m:%ddate 用法&#xff1a;date [OPTION]... [FORMAT]在显示方面&#xff0c;使用者可以设定欲显示的格式&#xff0c;格式设定为一个加号后接数个标记&#…

AlexNet学习笔记

AlexNet 概述 AlexNet是由2012年ImageNet竞赛参赛者Hinton和他的学生Alex Krizhevsky设计的。 创新点 非线性激活函数ReLU 选取了非线性非饱和的relu函数,ReLU函数的表达式为F(x)max(0&#xff0c;z)。若输入小于0&#xff0c;那么输出为0&#xff1b;若输入大于0&#xff…

内蒙古海天公司企业网的规划与设计

目 录 摘要 I ABSTRACT II 目 录 III 第一章 引 言 - 1 - 第二章 需求分析 - 3 - 2.1 背景分析 - 3 - 2.2 应用需求分析 - 3 - 2.2.1 内蒙古海天公司网的管理策略 - 3 - 2.2.2 网络中服务器简介 - 4 - 2.2.3 操作系统的选择…- 5 - 2.3 安全需求 - 5 - 2.4 网络扩展性需求 - 5 …

JVM 一张图带你了解内存分配过程 搞懂逃逸分析|标量替换|指针碰撞|空闲列表|TLAB

面试题 在栈上分配对象&#xff0c;使用标量替换的目的是什么&#xff1f; 内存分配过程 逃逸分析 如何确定对象是否在栈上进行分配&#xff0c;当然得通过逃逸分析了。 逃逸分析是什么意思呢&#xff1f;我们直接看两段代码 代码1: public Student get(){Student student …

CEAC之《企业信息管理》2

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;微微的猪食小窝 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! 本文由 微微的猪食小窝 原创 收录于专栏 【CEAC证书】 1在每个文本框、组合框和列表框的属性表中&#xff0c;都可以找到3个属性&#xff0c;分别…

30.nacos做注册中心入门实例(springcloud)

一、新建nacos-client-a 1.因为官方的springboot没有集成nacos的依赖&#xff0c;所以不再使用springboot的官方下载依赖地址 2. 因为我的idea是2019版本&#xff0c;更改了springboot下载路径后&#xff0c;不会自动更新&#xff0c;第一次选中依赖时&#xff0c;仍然时sprin…

回溯算法(回溯搜索法)

回溯是递归的副产品&#xff0c;只要有递归就会有回溯。 回溯算法&#xff0c;不是一个高效的算法&#xff0c;纯暴力算法&#xff0c;实际上是递归算法的一部分&#xff0c;最多再剪枝⼀下。 回溯的本质是穷举&#xff0c;穷举所有可能&#xff0c;然后选出我们想要的答案&a…

Mac无法打开CORE Keygen

背景 显示如下图&#xff0c;无法打开CORE Keygen&#xff0c;不要方。一个神器即可解决。 方案-使用UPX&#xff1a; &#xff08;1&#xff09;先安装upx (什么&#xff0c;你说你没有brew&#xff1f;&#xff1f;&#xff1f;看看这篇文章 Mac安装brew_Hero.Lin的博…

一篇文章彻底理解 HDFS 的安全模式

一篇文章彻底理解 HDFS 的安全模式 1 什么是 HDFS 的安全模式 Hdfs 的安全模式&#xff0c;即 HDFS safe mode, 是 HDFS 文件系统的一种特殊状态&#xff0c;在该状态下&#xff0c;hdfs 文件系统只接受读数据请求&#xff0c;而不接受删除、修改等变更请求&#xff0c;当然也…

【攻破css系列——第九天】常规流

文章目录1. 常规流2. 常规流布局2.1 定义2.2 包含块2.3 块盒2.3.1 每个块盒的总宽度&#xff0c;必须等于包含块的宽度2.3.2 每个块盒垂直方向上的auto值2.3.3 百分比取值2.3.4 上下外边距合并&#xff08;margin塌陷&#xff09;2.4 行盒2.4.1 盒子沿着内容延伸2.4.2 宽高不可…

Redis基础入门教程 - 概览

Redis基础教程 欢迎加好友一起讨论问题 知识地图&#xff1a;Redis概述与安装https://blog.csdn.net/lili40342/article/details/127852124Redis的5大数据类型https://blog.csdn.net/lili40342/article/details/127897689Redis的发布和订阅https://blog.csdn.net/lili40342/art…

C++模拟OpenGL库——图片处理及纹理系统(四):UV纹理坐标

目录 引入UV纹理坐标及三角形绘制设置 纹理过滤 引入UV纹理坐标及三角形绘制设置 上图其实不是很直观。 UV坐标要解决的问题就是&#xff1a; 假设我有一张500500的纹理图片&#xff1b; 我要把它映射到一张200200的图片中&#xff1b; 这个问题要怎么去解决。 这里提出…

【附源码】Python计算机毕业设计网络考试系统设计

项目运行 环境配置&#xff1a; Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术&#xff1a; django python Vue 等等组成&#xff0c;B/S模式 pychram管理等等。 环境需要 1.运行环境&#xff1a;最好是python3.7.7&#xff0c;…