Redis数据库redisDb源码分析

news2025/8/8 2:44:06

写在前面

以下内容是基于Redis 6.2.6 版本整理总结

一、组织方式

Redis服务器将所有的数据库 都保存在src/server.h/redisServer结构中的db数组中。db数组的每个entry都是src/server.h/redisDb结构,每个redisDb结构代表一个数据库。Redis默认有16个数据库。

1.1 redisServer结构定义

struct redisServer {
    /* General */
    pid_t pid;                  /* Main process pid. */
    pthread_t main_thread_id;         /* Main thread id */
	...
    redisDb *db;   // db数组
    ...
    int dbnum;     // redis db的数量
    ...
};

在这里插入图片描述

1.2 redisDb 结构定义

typedef struct redisDb {
    dict *dict;                 /* The keyspace for this DB */ //键空间,保存数据库中所有的键值对
    dict *expires;              /* Timeout of keys with a timeout set */
    dict *blocking_keys;        /* Keys with clients waiting for data (BLPOP)*/
    dict *ready_keys;           /* Blocked keys that received a PUSH */
    dict *watched_keys;         /* WATCHED keys for MULTI/EXEC CAS */
    int id;                     /* Database ID */
    long long avg_ttl;          /* Average TTL, just for stats */
    unsigned long expires_cursor; /* Cursor of the active expire cycle. */
    list *defrag_later;         /* List of key names to attempt to defrag one by one, gradually. */
} redisDb;

各字段含义解释

  • dict保存了数据库中的所有键值对,这个字典也被称为:键空间(key space)。键空间的键就是数据库的键,每个键都是字符串对象;键空间的值就是数据库的值,每个值可以是五种对象中的任意一种对象。
  • expires用来处理键的过期行为;
  • blocking_keys使用比较少,redis只有blpop、brpop等命令造成主动阻塞。
  • ready_keys和blocking_keys配合使用,比如:一个客户端blpop阻塞等待数据,另一个客户端在push时,会检查blocking_keys中是否存在相应的key,如果有就将该key移动到ready_keys中,阻塞的客户端收到数据。
  • watched_keys用来实现WATCH功能,实际线上环境不会使用,影响redis性能。

1.3 redisdb初始化

// src/server.c

void initServer(void) {
    int j;
    // ...
	server.db = zmalloc(sizeof(redisDb)*server.dbnum);
	// ...
	/* Create the Redis databases, and initialize other internal state. */
    for (j = 0; j < server.dbnum; j++) {
        server.db[j].dict = dictCreate(&dbDictType,NULL);
        server.db[j].expires = dictCreate(&dbExpiresDictType,NULL);
        server.db[j].expires_cursor = 0;
        server.db[j].blocking_keys = dictCreate(&keylistDictType,NULL);
        server.db[j].ready_keys = dictCreate(&objectKeyPointerValueDictType,NULL);
        server.db[j].watched_keys = dictCreate(&keylistDictType,NULL);
        server.db[j].id = j;
        server.db[j].avg_ttl = 0;
        server.db[j].defrag_later = listCreate();
        listSetFreeMethod(server.db[j].defrag_later,(void (*)(void*))sdsfree);
    }
   //...
}

二、增删改查源码分析

2.1 新增key

/* Add the key to the DB. It's up to the caller to increment the reference
 * counter of the value if needed.
 *
 * The program is aborted if the key already exists. */
void dbAdd(redisDb *db, robj *key, robj *val) {
    sds copy = sdsdup(key->ptr);
    int retval = dictAdd(db->dict, copy, val);

    serverAssertWithInfo(NULL,key,retval == DICT_OK);
    signalKeyAsReady(db, key, val->type);
    if (server.cluster_enabled) slotToKeyAdd(key->ptr);
}

2.2 删除key

/* This is a wrapper whose behavior depends on the Redis lazy free
 * configuration. Deletes the key synchronously or asynchronously. */
int dbDelete(redisDb *db, robj *key) {
    return server.lazyfree_lazy_server_del ? dbAsyncDelete(db,key) :
                                             dbSyncDelete(db,key);
}

int dbSyncDelete(redisDb *db, robj *key) {
    /* Deleting an entry from the expires dict will not free the sds of
     * the key, because it is shared with the main dictionary. */
    if (dictSize(db->expires) > 0) dictDelete(db->expires,key->ptr);
    dictEntry *de = dictUnlink(db->dict,key->ptr);
    if (de) {
        robj *val = dictGetVal(de);
        /* Tells the module that the key has been unlinked from the database. */
        moduleNotifyKeyUnlink(key,val);
        dictFreeUnlinkedEntry(db->dict,de);
        if (server.cluster_enabled) slotToKeyDel(key->ptr);
        return 1;
    } else {
        return 0;
    }
}

#define LAZYFREE_THRESHOLD 64
int dbAsyncDelete(redisDb *db, robj *key) {
    /* Deleting an entry from the expires dict will not free the sds of
     * the key, because it is shared with the main dictionary. */
    if (dictSize(db->expires) > 0) dictDelete(db->expires,key->ptr);

    /* If the value is composed of a few allocations, to free in a lazy way
     * is actually just slower... So under a certain limit we just free
     * the object synchronously. */
    dictEntry *de = dictUnlink(db->dict,key->ptr);
    if (de) {
        robj *val = dictGetVal(de);

        /* Tells the module that the key has been unlinked from the database. */
        moduleNotifyKeyUnlink(key,val);

        size_t free_effort = lazyfreeGetFreeEffort(key,val);

        /* If releasing the object is too much work, do it in the background
         * by adding the object to the lazy free list.
         * Note that if the object is shared, to reclaim it now it is not
         * possible. This rarely happens, however sometimes the implementation
         * of parts of the Redis core may call incrRefCount() to protect
         * objects, and then call dbDelete(). In this case we'll fall
         * through and reach the dictFreeUnlinkedEntry() call, that will be
         * equivalent to just calling decrRefCount(). */
        if (free_effort > LAZYFREE_THRESHOLD && val->refcount == 1) {
            atomicIncr(lazyfree_objects,1);
            bioCreateLazyFreeJob(lazyfreeFreeObject,1, val);
            dictSetVal(db->dict,de,NULL);
        }
    }

    /* Release the key-val pair, or just the key if we set the val
     * field to NULL in order to lazy free it later. */
    if (de) {
        dictFreeUnlinkedEntry(db->dict,de);
        if (server.cluster_enabled) slotToKeyDel(key->ptr);
        return 1;
    } else {
        return 0;
    }
}

2.3 查找key

robj *lookupKey(redisDb *db, robj *key, int flags) {
    dictEntry *de = dictFind(db->dict,key->ptr);
    if (de) {
        robj *val = dictGetVal(de);

        /* Update the access time for the ageing algorithm.
         * Don't do it if we have a saving child, as this will trigger
         * a copy on write madness. */
        if (!hasActiveChildProcess() && !(flags & LOOKUP_NOTOUCH)){
            if (server.maxmemory_policy & MAXMEMORY_FLAG_LFU) {
                updateLFU(val);
            } else {
                val->lru = LRU_CLOCK();
            }
        }
        return val;
    } else {
        return NULL;
    }
}

/* Lookup a key for read operations, or return NULL if the key is not found
 * in the specified DB.
 *
 * As a side effect of calling this function:
 * 1. A key gets expired if it reached it's TTL.
 * 2. The key last access time is updated.
 * 3. The global keys hits/misses stats are updated (reported in INFO).
 * 4. If keyspace notifications are enabled, a "keymiss" notification is fired.
 *
 * This API should not be used when we write to the key after obtaining
 * the object linked to the key, but only for read only operations.
 *
 * Flags change the behavior of this command:
 *
 *  LOOKUP_NONE (or zero): no special flags are passed.
 *  LOOKUP_NOTOUCH: don't alter the last access time of the key.
 *
 * Note: this function also returns NULL if the key is logically expired
 * but still existing, in case this is a slave, since this API is called only
 * for read operations. Even if the key expiry is master-driven, we can
 * correctly report a key is expired on slaves even if the master is lagging
 * expiring our key via DELs in the replication link. */
robj *lookupKeyReadWithFlags(redisDb *db, robj *key, int flags) {
    robj *val;

    if (expireIfNeeded(db,key) == 1) {
        /* If we are in the context of a master, expireIfNeeded() returns 1
         * when the key is no longer valid, so we can return NULL ASAP. */
        if (server.masterhost == NULL)
            goto keymiss;

        /* However if we are in the context of a slave, expireIfNeeded() will
         * not really try to expire the key, it only returns information
         * about the "logical" status of the key: key expiring is up to the
         * master in order to have a consistent view of master's data set.
         *
         * However, if the command caller is not the master, and as additional
         * safety measure, the command invoked is a read-only command, we can
         * safely return NULL here, and provide a more consistent behavior
         * to clients accessing expired values in a read-only fashion, that
         * will say the key as non existing.
         *
         * Notably this covers GETs when slaves are used to scale reads. */
        if (server.current_client &&
            server.current_client != server.master &&
            server.current_client->cmd &&
            server.current_client->cmd->flags & CMD_READONLY)
        {
            goto keymiss;
        }
    }
    val = lookupKey(db,key,flags);
    if (val == NULL)
        goto keymiss;
    server.stat_keyspace_hits++;
    return val;

keymiss:
    if (!(flags & LOOKUP_NONOTIFY)) {
        notifyKeyspaceEvent(NOTIFY_KEY_MISS, "keymiss", key, db->id);
    }
    server.stat_keyspace_misses++;
    return NULL;
}

三、增删改查图示

3.1 新增键值对

举例:我们在一个空的redis数据库中执行分别执行以下命令:

127.0.0.1:6379[1]> keys *
(empty array)  // 表示此时数据库中没有任何数据
127.0.0.1:6379[1]> set msg "hello world"
OK
127.0.0.1:6379[1]>

在这里插入图片描述

127.0.0.1:6379[1]> hmset student name panda age 20 addr beijing
OK
127.0.0.1:6379[1]>

在这里插入图片描述

127.0.0.1:6379[1]> rpush teacher Darren Mark King
(integer) 3
127.0.0.1:6379[1]>

在这里插入图片描述

3.2 更新键值对

127.0.0.1:6379[1]> set msg "redis"
OK
127.0.0.1:6379[1]> get msg
"redis"
127.0.0.1:6379[1]> hset student sex male
(integer) 1
127.0.0.1:6379[1]>

在这里插入图片描述

3.3 获取键的值

127.0.0.1:6379[1]> get msg
"redis"
127.0.0.1:6379[1]> hmget student name age addr sex
1) "panda"
2) "20"
3) "beijing"
4) "male"
127.0.0.1:6379[1]>

3.4 删除键值对

127.0.0.1:6379[1]> keys *
1) "msg"
2) "student"
3) "teacher"
127.0.0.1:6379[1]> del student
(integer) 1
127.0.0.1:6379[1]> keys *
1) "msg"
2) "teacher"
127.0.0.1:6379[1]>

在这里插入图片描述

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

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

相关文章

TDengine安装使用

引言 近期&#xff0c;听说了时序数据库TDengine&#xff0c;本人的好奇心又出来了&#xff0c;同是时序数据库的InfluxDB不也挺好的嘛&#xff1f;通过一些网上的资料以及些简单的实际操作&#xff0c;本人得出的结论是&#xff1a; 数据量少时&#xff0c;InfluxDB的性能好些…

MCE | TGF-β 信号通路

转化生长因子 (Transforming growth factor beta&#xff0c;TGF-β) 是一类多功能的细胞因子&#xff0c;可由多种组织细胞产生。TGF-β 信号通路是由众多成员的多功能细胞因子&#xff0c;与相应的受体、细胞内信号转导分子组成的通路&#xff0c;能影响疾病发生和发展&#…

win10利用minikube在自己的电脑上搭建k8s

首先默认你的电脑上装了docker&#xff0c;没有的话参考这篇 下面开始步入正题&#xff1a; 步骤讲解 首先下载minikube,点击这个链接&#xff0c;根据自己的环境生成相应的配置命令&#xff0c;我自己的话是64位win10系统&#xff0c;管理员打开cmd运行命令如下&#xff1a…

Flutter高仿微信-第33篇-单聊-图片

Flutter高仿微信系列共59篇&#xff0c;从Flutter客户端、Kotlin客户端、Web服务器、数据库表结构、Xmpp即时通讯服务器、视频通话服务器、腾讯云服务器全面讲解。 详情请查看 效果图&#xff1a; 详情请参考 Flutter高仿微信-第29篇-单聊 &#xff0c; 这里只是提取图片实现的…

更简洁的参数校验,使用 SpringBoot Validation 对参数进行校验

在开发接口时&#xff0c;如果要对参数进行校验&#xff0c;你会怎么写&#xff1f;编写 if-else 吗&#xff1f;虽然也能达到效果&#xff0c;但是不够优雅。 今天&#xff0c;推荐一种更简洁的写法&#xff0c;使用 SpringBoot Validation 对方法参数进行校验&#xff0c;特…

k8s dashboard安装部署实战详细手册

文章目录一、k8s dashboard搭建1.选择版本2.下载yaml3.执行yaml4.访问dashboard5.token登录6.配置权限结尾一、k8s dashboard搭建 1.选择版本 dashboard和k8s存在版本对应关系&#xff0c;具体可以去github查找https://github.com/kubernetes/dashboard/releases 由于我的k8s…

亮相2022南京软博会,创邻科技携Galaxybase图平台展现信创硬核实力

11月23日&#xff0c;2022中国&#xff08;南京&#xff09;国际软件产品和信息服务交易博览会&#xff08;以下简称”软博会“&#xff09;在南京博览中心隆重开幕。此次展会以“软件赋能 数智转型”为主题&#xff0c;由江苏省工业和信息化厅、南京市人民政府、中国工业技术软…

iwebsec靶场 数据库漏洞通关1-MySQL数据库漏洞

iwebsec靶场的数据库漏洞第一项内容为MySQL弱口令漏洞渗透&#xff0c;如下所示我们可以使用kali的msf模块对其进行渗透。 一、获取iwebsec虚拟机环境的MySQL服务映射的端口号 由于iwebsec靶场是通过docker搭建在ubuntu这个宿主虚拟机中 接下来在机器在ubuntu64位这台虚拟机里…

基于JAVA的物流信息管理平台【数据库设计、源码、开题报告】

数据库脚本下载地址&#xff1a; https://download.csdn.net/download/itrjxxs_com/86406113 摘要 随着全球供应链持续受到 COVID-19 的影响&#xff0c;许多物流公司正在考虑如何重构新常态运营环境&#xff0c;实现降本增效。对于业务网点遍布全球的物流公司而言&#xff0…

【看球和学Go】错误和异常、CGO、fallthrough

这篇文章将详解「Go必知必会」的知识点&#xff1a; 错误和异常的对比、发生panic后如何执行代码&#xff1f;会执行到defer代码段吗&#xff1f;CGO是什么&#xff1f;CGO的作用是什么&#xff1f;switch中的fallthrough 错误&异常 错误指的是可能出现问题的地方出现了…

云原生系列 六【轻松入门容器基础操作】

✅作者简介&#xff1a; CSDN内容合伙人&#xff0c;全栈领域新星创作者&#xff0c;阿里云专家博主&#xff0c;华为云享专家博主&#xff0c;掘金后端评审团成员 &#x1f495;前言&#xff1a; 最近云原生领域热火朝天&#xff0c;那么云原生是什么&#xff1f;何为云原生&a…

深度学习第一次作业 - 波士顿房价预测

文章目录划分训练集和测试集建立线性回归模型特征选择重建模型尝试使用GradientBoostingimport pandas as pd import numpy as np import seaborn as sns from sklearn.linear_model import LinearRegression from sklearn.model_selection import train_test_split from sklea…

第一个SpringBoot项目的创建

目录 一、SpringBoot是什么&#xff1f; 初识springboot springboot的优点 二、SpringBoot项目的创建与简单运行 &#x1f351;使用idea创建springboot项目 &#x1f351; Spring Boot 项目目录介绍 &#x1f351;springboot项目的简单运行与使用 一、SpringBoot是什么&a…

emq证书过期问题

近期在进行远程漏洞扫描后发现&#xff0c;有存在服务器证书过期的情况&#xff0c;可以通过以下步骤进行问题的处理&#xff1a; 1、先根据对应出现漏洞的端口进行检查&#xff0c;看端口对应的服务是哪个服务&#xff1a; netstat -tunlp|grep 端口号 2、通过命令&#xf…

40 - 前置操作符和后置操作符

---- 整理自狄泰软件唐佐林老师课程 问题 下面代码的区别&#xff1f;why&#xff1f; 1.1 编程实验 汇编中的处理是一样的&#xff0c;所以不可能从编译后的二进制程序还原 i 还是 i 1.2 事实 现代编译器产品会对代码进行优化优化使得最终的二进制程序更加高效优化后的二…

[附源码]java毕业设计渔具店管理系统

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

【Linux】系统编程之网络编程(socket)

目录一、Socket编程1、TCP/UDP&#xff08;1&#xff09;TCP/UDP区别&#xff08;2&#xff09;TCP/UDP服务端和客户端的通信流程2、IP地址和端口号&#xff08;1&#xff09;IP地址&#xff08;2&#xff09;端口号二、字节序1、字节系相关概念2、有关字节序转换的函数&#x…

小小王总,如何变成任正非、化腾、强东这样的巨人!

原创&#xff1a;小姐姐味道&#xff08;微信公众号ID&#xff1a;xjjdog&#xff09;&#xff0c;欢迎分享&#xff0c;非公众号转载保留此声明。王总特别迷信外面的企业培训。当遇到问题时&#xff0c;他喜欢去取经。这个经不像唐僧取经一样&#xff0c;需要历经九九八十一难…

ImmunoChemistry艾美捷通用阻断ELISA阻断缓冲液说明书

ImmunoChemistry艾美捷通用阻断ELISA阻断缓冲液包含适用于大多数抗体捕获ELISA格式和肽或蛋白质抗原下调ELISA格式的哺乳动物蛋白质阻断剂。这种封闭缓冲液为干燥的抗原或抗体外壳蛋白提供了长期稳定的环境&#xff0c;并使测定过程中的非特异性结合相互作用最小化。 General B…

中石油测井-技术研发岗回顾

前提&#xff1a; 时间&#xff1a;2022年11月25日 结果&#xff1a;暂未可知 阶段&#xff1a;面试结束 等结果 整个过程中&#xff0c;注意查看官网 中石油招聘 投递之前关注一下基本要求&#xff08;学历-专业&#xff09; 招聘人数&#xff1a;应聘人数 &#xff08;通过能…