Redis源码剖析】 - Redis之数据库redisDb

【Redis实现系列】RedisDB实现 RedisDB 核心实现 概述 Redis 服务器将所有数据库都保存在服务器状态 redis.h/redisServer 结构的 db 数组中,db 数组的每个项都是一个 redis.h/redisDb 结构,每个redisDb结构代表一个数据库。 定义: struct redisServer { // ... // 一个数组,保存着服务器中的所有数据库 redisDb *db; // 初始化服务器时,程序会根据服务器状态的dbnum属性来决定应该创建多少 阅读详情

Redis源码剖析】 - Redis之数据库redisDb

原创作品,转载请标明:http://blog.youkuaiyun.com/xiejingfa/article/details/51321282

Redis源码剖析系列文章汇总:传送门

今天,我们来讨论两点内容:一是Redis是如何存储类型对象的,二是Redis如何实现键的过期操作。

本文介绍的内容主要涉及db.c和redis.h两个文件。


1、redisDb介绍

Redis中存在“数据库”的概念,该结构由redis.h中的redisDb定义。我们知道Redis提供string、list、set、zset、hash五种数据类型的存储,在Redis运行时,服务器中会存在许多的不同类型的对象,当我们需要操作某个具体的对象时,首先需要快速定位到该对象。比如往一个list中插入一个元素,第一步先要在众多对象实例中找到该list,然后再进行插入操作。如何快速获取指定对象呢?在我们前面介绍过的基础数据结构中字典dict就可以实现该功能。具体的做法是:Redis每生成一个对象实例都需要关联一个key,利用dict保存key和对象实例之间的映射关系。这样就可以在O(1)的时间复杂度下根据key找到对应的对象,而Redis中的“数据库”redisDb就是对上述过程的实现。下面我们来看看该结构体的定义。

1.1、redisDb的存储结构

redisDb结构体的定义如下:

/* Redis数据库结构体 */
typedef struct redisDb {
    // 数据库键空间,存放着所有的键值对(键为key,值为相应的类型对象)
    dict *dict;                 
    // 键的过期时间
    dict *expires;              
    // 处于阻塞状态的键和相应的client(主要用于List类型的阻塞操作)
    dict *blocking_keys;       
    // 准备好数据可以解除阻塞状态的键和相应的client
    dict *ready_keys;           
    // 被watch命令监控的key和相应client
    dict *watched_keys;         
    // 数据库ID标识
    int id;
    // 数据库内所有键的平均TTL(生存时间)
    long long avg_ttl;         
} redisDb;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

我们看到:

(1)、redisDb中的dict *dict成员就是将key和具体的对象(可能是string、list、set、zset、hash中任意类型之一)关联起来,存储着该数据库中所有的键值对数据。该字段又称为键空间key space。 
(2)、expires成员用来存放key的过期时间。 
(3)、id成员是数据库的编号,以整型表示。

接下来我们主要基于上面3个redisDb的成员来展开讲解。其余几个成员在前面我们已经介绍过,大家可以参看前面的几篇博客,这里给出给相关文章链接:

  1. blocking_keys、ready_keys: 【Redis源码剖析】 - Redis数据类型之列表List
  2. watched_keys:【Redis源码剖析】 - Redis之事务的实现原理

1.2、数据库的切换操作

当redis 服务器初始化时,会预先分配 16 个数据库,该数字配置在redis.conf配置文件中,如下:

# Set the number of databases. The default database is DB 0, you can select
# a different one on a per-connection basis using SELECT <dbid> where
# dbid is a number between 0 and 'databases'-1
databases 16
  • 1
  • 2
  • 3
  • 4

所有数据库保存到结构 redisServer 的一个成员 redisServer.db 数组中,而redisClient中存在一个名叫db的指针指向当前使用的数据库(默认为0号数据库)。

typedef struct redisClient {
    ...
    // 当前所使用的数据库
    redisDb *db;
    ...
} redisClient;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

Redis提供了select 命令用于切换到指定的数据库,该命令的具体格式为:

SELECT db_index 
  • 1

其中数据库索引号db_ index 用数字值指定,以 0 作为起始索引值。该命令的实现也很简单,当我们选择数据库 select number 时,程序直接通过 redisServer.db[number] 来切换数据库,源码如下:

/* SELECT命令,切换到指定数据库。*/
void selectCommand(redisClient *c) {
    long id;

    // 取得目标数据库id,如果输入值不合法则返回
    if (getLongFromObjectOrReply(c, c->argv[1], &id,
        "invalid DB index") != REDIS_OK)
        return;

    // 切换到指定数据库
    if (selectDb(c,id) == REDIS_ERR) {
        addReplyError(c,"invalid DB index");
    } else {
        addReply(c,shared.ok);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

由selectDb函数执行真正的切换操作:

/* 切换为参数id指定的数据库,如果操作成功返回REDIS_OK,否则返回REDIS_ERR。   */
int selectDb(redisClient *c, int id) {
    // 验证参数id是否正确,server.dbnum默认值为16
    if (id < 0 || id >= server.dbnum)
        return REDIS_ERR;
    // 切换数据库,就是简单地设置指针
    c->db = &server.db[id];
    return REDIS_OK;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

2、redisDb中的键空间

在【Redis源码剖析】系列文章中,我们前面花了很多篇幅介绍了Redis中内置数据结构和数据类型的实现原理,这里我们就要把它们综合起来,看看如何利用这些数据结构组成一个初步的key-value系统。

2.1、键空间的存储结构

键空间实际就是一个字典dict结构,存储着该库所有的键值对数据,其中字典dict的key是一个字符串对象,字典dict的值可能是string、list、set、zset、hash中任意类型之一的对象实例。弄明白键空间的底层结构,我们不难画出其存储结构:

这里写图片描述

2.2、键空间的相关操作

Redis提供了许多与key相关的操作命令,这些命令我们先前没有涉及到,接下来简单介绍一下:

命令作用
del key删除key
exists key检查key是否存在
randomkey从当前数据库中随机返回一个key
keys pattern查找所有符合给定模式的key
scan cursor迭代当前数据库中的key
dbsize返回当前数据库中key的数量
type key返回key所存储的值的类型
rename key newkey修改key的名称
renamenx key newkey当newkey不存在时,修改key的名称为newkey
move key db将当前数据库中的key移动到给定的数据库中

由于键空间是一个字典dict结构,所以对键空间的操作基本上就是对字典dict的操作,主要包含下面这些函数:

// 从Redis数据库db中取出指定key的对象
robj *lookupKey(redisDb *db, robj *key);
// 为读操作而从数据库db中取出指定key的对象
robj *lookupKeyRead(redisDb *db, robj *key);
// 为写操作而从数据库db中取出指定key的对象
robj *lookupKeyWrite(redisDb *db, robj *key);
// 带有回复功能的lookupKeyRead函数
robj *lookupKeyReadOrReply(redisClient *c, robj *key, robj *reply);
// 带有回复功能的lookupKeyWrite函数
robj *lookupKeyWriteOrReply(redisClient *c, robj *key, robj *reply);
// 往数据库db中添加一个由参数key和参数val指定的键值对
void dbAdd(redisDb *db, robj *key, robj *val);
// 重写一个key关联的值,即为一个存在的key设置一个新值
void dbOverwrite(redisDb *db, robj *key, robj *val);
// 为一个key设置新值
void setKey(redisDb *db, robj *key, robj *val);
// 判断某个key是否存在
int dbExists(redisDb *db, robj *key);
// 随机从数据库db中取出一个key并返回
robj *dbRandomKey(redisDb *db);
// 从数据库中删除一个给定的key、相应的值value以及该key的过期时间
int dbDelete(redisDb *db, robj *key);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

这些操作并没有什么难点,大家可以参看后文提供的加以注释的源码进行学习,这里就不一一解释每个函数的实现。

3、键的过期操作

Redis支持为给定的key设置过期时间,并提供以下命令实现该功能:

命令作用
expire key seconds以秒为单位为给定key设置过期时间
expireat key timestamp以秒为单位为给定key设置过期时间戳
pexpire key milliseconds以毫秒为单位设置key的过期时间
pexpireat key milliseconds以毫秒为单位设置key过期时间戳
persist key移除key的过期时间,key将持久保存
pttl key以毫秒为单位返回key的剩余过期时间
ttl key以秒为单位返回key的剩余生存时间

接下来,我们就来介绍一下Redis如果保存key的过期信息和如何删除过期key的。

3.1、底层结构

前面我们讲过,key的过期时间保存在redisDb结构中的expires字段中:

typedef struct redisDb {
    ...               
    // 键的过期时间
    dict *expires;              
    ...        
} redisDb;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

expires字段也是一个字典dict结构,字典的键为key,值为该key对应的过期时间,过期时间为long long类型整数,是以毫秒为单位的过期 UNIX 时间戳。我们可以看看setExpire函数加以佐证,该函数的作用是为指定key设置过期时间。

/* 为指定key设置过期时间 */
void setExpire(redisDb *db, robj *key, long long when) {
    dictEntry *kde, *de;

    /* Reuse the sds from the main dict in the expire dict */
    // db->dict和db->expires是共用key字符串对象的
    // 取出key
    kde = dictFind(db->dict,key->ptr);
    redisAssertWithInfo(NULL,key,kde != NULL);
    // 取出过期时间
    de = dictReplaceRaw(db->expires,dictGetKey(kde));
    // 重置key的过期时间
    dictSetSignedIntegerVal(de,when);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

有了过期时间戳我们就很容易判断某个key是否过期:只要将当前时间戳跟过期时间戳比较一下即可,如果当前时间戳大于过期时间戳显然该key已经过期了。在Redis中,如果没有为一个key设置过期时间,那么该key就不会出现在db->expires字典中。也就是说db->expires字段只保存了设置有过期时间的key。

有一点需要提出来的是:redisDb中的db->dict和db->expires两个字典是共享同一个key的,即它们都指向了同一个key字符串,而不是将同一个key复制两份。这点利用指针很容易实现。

3.2、相关操作

3.2.1、设置过期时间

Redis提供了expire、expireat、pexpire、pexpireat四个命令来设置key的过期时间,这四个命令底层都是通过调用expireGenericCommand函数来实现的,该函数的原型如下:

void expireGenericCommand(redisClient *c, long long basetime, int unit)
  • 1

其中:

参数basetime用来指明基准时间,对于expireat和pexpireat命令,basetime的值为0,对于expire和pexpire命令,basetime的值为当前时间。参数basetime总是以毫秒为单位的。

参数unit用于指定argv[2]的格式:UNIT_SECONDS或者UNIT_MILLISECONDS,前者指明以秒为单位计算,后者指明以毫秒为单位计算。

从expireGenericCommand函数的实现上我们可以看出,虽然expire、expireat、pexpire、pexpireat四个命令分别使用了不同的单位(秒/毫秒)、不同的形式(过期时间/过期时间戳)来设置key的过期时间,但最后都会转换为统一的形式即以毫秒为单位的UNIX 时间戳作为过期时间戳。

void expireGenericCommand(redisClient *c, long long basetime, int unit) {
    robj *key = c->argv[1], *param = c->argv[2];
    // 以毫秒为单位的unix时间戳
    long long when; /* unix time in milliseconds when the key will expire. */

    // 获取过期时间
    if (getLongLongFromObjectOrReply(c, param, &when, NULL) != REDIS_OK)
        return;

    // 如果传入的过期时间是以秒为单位,则转换为毫秒为单位
    if (unit == UNIT_SECONDS) when *= 1000;
    // 加上basetime得到过期时间戳
    when += basetime;

    /* No key, return zero. */
    // 取出key,如果该key不存在直接返回
    if (lookupKeyRead(c->db,key) == NULL) {
        addReply(c,shared.czero);
        return;
    }

    if (when <= mstime() && !server.loading && !server.masterhost) {
        // 如果when指定的时间已经过期,而且当前为服务器的主节点,并且目前没有载入数据
        robj *aux;

        redisAssertWithInfo(c,key,dbDelete(c->db,key));
        server.dirty++;

        /* Replicate/AOF this as an explicit DEL. */
        // 传播一个显式的DEL命令
        aux = createStringObject("DEL",3);
        rewriteClientCommandVector(c,2,aux,key);
        decrRefCount(aux);
        signalModifiedKey(c->db,key);
        notifyKeyspaceEvent(REDIS_NOTIFY_GENERIC,"del",key,c->db->id);
        addReply(c, shared.cone);
        return;
    } else {
        // 设置key的过期时间(when提供的时间可能已经过期)
        setExpire(c->db,key,when);
        addReply(c,shared.cone);
        signalModifiedKey(c->db,key);
        notifyKeyspaceEvent(REDIS_NOTIFY_GENERIC,"expire",key,c->db->id);
        server.dirty++;
        return;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47

3.2.2、删除过期key

对于过期的key,Redis负责将该key删除,为了提高运行效率,Redis采取这么一种处理方式:只有当真正要访问该key时才检查该key是否过期。如果过期就删除,如果没过期就正常访问。通常我们把这种只有在访问时才检查过期的策略叫做“惰性删除”。

具体实现上,Redis对所有读写数据库的命令在执行之前都会调用expireIfNeeded函数来检查对应key是否过期。该函数如下:

int expireIfNeeded(redisDb *db, robj *key) {
    // 获取key的过期时间
    mstime_t when = getExpire(db,key);
    mstime_t now;

    // 如果该key没有过期时间,返回0
    if (when < 0) return 0; /* No expire for this key */

    /* Don't expire anything while loading. It will be done later. */
    // 如果服务器正在加载操作中,则不进行过期检查,返回0
    if (server.loading) return 0;

    now = server.lua_caller ? server.lua_time_start : mstime();

    // 如果当前程序运行在slave节点,该key的过期操作是由master节点控制的(master节点会发出DEL操作)
    // 在这种情况下该函数先返回一个正确值,即如果key未过期返回0,否则返回1。
    // 真正的删除操作等待master节点发来的DEL命令后再执行
    if (server.masterhost != NULL) return now > when;

    /* Return when this key has not expired */
    // 如果未过期,返回0
    if (now <= when) return 0;

    /* Delete the key */
    // 如果已过期,删除该key
    server.stat_expiredkeys++;
    propagateExpire(db,key);
    notifyKeyspaceEvent(REDIS_NOTIFY_EXPIRED,
        "expired",key,db->id);
    return dbDelete(db,key);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32

在expireIfNeeded函数中,我们看到对于过期key,Redis主(master)节点和附属(slave)节点有不同的处理策略,具体如下:

  1. 如果当前Redis服务器是主节点,即if (server.masterhost != NULL)语句判断为false,那么当它发现一个过期key后,会调用propagateExpire函数向所有附属节点发送一个 DEL 命令,然后再删除该key。这种做法使得对key的过期操作可以集中在一个地方处理。
  2. 如果当前Redis服务器是附属节点,即即if (server.masterhost != NULL)语句判断为true,那么它立即向程序返回该key是否已经过期的信息。即便该key已经过期也不会真正的删除该key。直到该节点接到从主节点发来的DEL 命令之后,才会真正执行删除操作。

我们再来看看下面这两个函数。当Redis从数据库db中取出指定key的对象时,总是先调用调用expireIfNeeded函数来检查对应key是否过期,然后再从数据库中查找对象。

/*  该函数是为读操作而从数据库db中取出指定key的对象。
    如果敢函数执行成功则返回目标对象,否则返回NULL。
    该函数最后还根据是否成功取出对象更新服务器的命中/不命中次数。 */
robj *lookupKeyRead(redisDb *db, robj *key) {
    robj *val;

    // 如果key已过期,删除该key
    expireIfNeeded(db,key);
    // 从数据库db中找到指定key的对象
    val = lookupKey(db,key);
    if (val == NULL)
        // 更新“未命中”次数
        server.stat_keyspace_misses++;
    else
        // 更新“命中”次数
        server.stat_keyspace_hits++;
    return val;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
/*  该函数是为写操作而从数据库db中取出指定key的对象。
    如果敢函数执行成功则返回目标对象,否则返回NULL。*/
robj *lookupKeyWrite(redisDb *db, robj *key) {
    // 如果key已过期,删除该key
    expireIfNeeded(db,key);
    // 从数据库db中找到指定key的对象
    return lookupKey(db,key);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

上面就是Redis中对于过期键的删除策略,但是这并不完整。我们还需要考虑另外一种情况:如果数据库db中有许多过期key,但又没有任何一个客户端访问这些key,那么在使用“惰性删除”策略下它们就永远也不会被删除。

为了解决这个问题Redis还会定期检查过期键并将其删除,这个过程由redis.c文件中的activeExpireCycle函数执行。关于该函数,等以后我们分析到redis.c文件时再解释。


db.c文件的源码:https://github.com/xiejingfa/the-annotated-redis-2.8.24/blob/master/db.c

【Redis | 1】Redis如何删除指定DB 用docker 运行了一个redis容器,怎么用命令删除redis 的指定的db数据缓存? 阅读详情

相关推荐

Redis 源码简洁剖析 - SDS 字符串

C 语言的字符串函数 C 语言 string 函数 ,在 C 语言中可以使用 char* 字符数组实现字符串,C 语言标准库 string.h 也定义了多种字符串操作函数。 字符串使用广泛,需要满足: 高效的字符串操作,比如追加、拷贝、比较、获取长度 能保存任意的二进制数据,比如图片 尽可能省内存 为什么 Redis 不直接使用 C 语言的字符串? C 语言 char* 以 '\0'标识字符串的结束,则中间含有'\0'的字符串无法被正确表示;也正因为如此,没有办法保存图像等二进制数据。 C 语

jinggege_795的博客 1217

ASP.NET MVC5 入门篇之EF6-EntityFrameWork6

EF=EntityFrameWork是微软自己的ORM框架。 ORM:对象关系映射(英语:Object Relational Mapping,简称ORM,或O/RM,或O/R mapping) 暂时只要记住ORM是将数据库(关系型数据库)表映射成类,数据库表数据映射成类对象。 ORM框架让开发者可以通过操作对象的方式来操作数据库。 //ORM之前我们都是使用数据库语句与数据库交互 例如: string sqlstr = "select user_id,user_name from users"; //然

艾克的博客 549

Redis系列之淘汰策略介绍

Redis系列之淘汰策略介绍。由于Redis内存是有大小的,当内存快满的时候,又没有过期数据,这个时候就会导致内存被占满,内存满了,自然就不能再放入新的数据。所以,就需要Redis的淘汰策略来保证可用性。

Nicky's blog 1708

Redis基本介绍

文章目录1.Redis简介2.Redis应用场景3.Redis单线程模型4.Redis常用命令5.Redis数据结构6.SpringBoot整合Redis框架 参考蚂蚁课堂 1.Redis简介 Redis属于No-sql数据库类似于MongoDB,是一个完全开源免费的一个高性能的key-value数据库。它是基于内存的数据库,非关系型数据库不同于mysql,sqlserver,Oracle他们都是存放在硬盘的,我们每次操作都相当于IO操作性能非常低,非关系型数据库的数据都是存放在内存中的,如果redis一旦

weixin_46120888的博客 4095

无需云服务器:飞牛云NAS+Docker搭建本地WordPress站点全球访问

在数字化浪潮中,网站搭建已成为个人和企业在推广自身品牌与业务的首要选择。飞牛云新推出的fnOS,这款集多功能于一身的nas系统,以其卓越的性能和灵活性,成为理想的本地网站部署平台。本文将带你一探如何在飞牛云NAS上通过Docker部署WordPress,并利用cpolar实现全球访问。

LT15171009269的博客 6528

华为交换机实战:如何用ACL+流分类精准限制特定IP的网速(附完整命令)

本文详细介绍了华为交换机如何通过ACL+流分类技术实现精准IP限速,包括ACL规则配置、流分类关联、流行为定义及流策略应用等完整步骤。文章提供了实战命令和配置验证方法,帮助网络管理员有效管理带宽资源,解决特定IP占用过多带宽的问题。

weixin_29229055的博客 100

【Redis源码剖析】 - Redis之数据库redisDb

原创作品,转载请标明:http://blog.youkuaiyun.com/xiejingfa/article/details/51321282 Redis源码剖析系列文章汇总:传送门 今天,我们来讨论两点内容:一是Redis是如何存储类型对象的,二是Redis如何实现键的过期操作。 本文介绍的内容主要涉及db.c和redis.h两个文件。 1、redisDb介绍 Redis中存在“数据库”的概...

上善若水,人淡如菊 1万+

5个免费股票数据API实测对比:从AkShare到BaoStock,哪个最适合你的AI量化项目?

本文深度评测了AkShare、BaoStock、yfinance、Alpha Vantage和EOD Historical Data这五个免费股票数据API,从数据质量、覆盖范围、更新频率、稳定性及易用性进行对比。针对AI量化项目,文章提供了详细的选型策略,帮助开发者根据项目阶段与需求,精准选择最适合的数据源,并强调了多源验证与数据清洗的重要性。

weixin_29290947的博客 5090

【Redis源码剖析】 - Redis之数据库redisDb

今天,我们来讨论两点内容:一是Redis是如何存储类型对象的,二是redis如何实现键的过期操作。 本文介绍的内容主要涉及db.c和redis.h两个文件。 1、redisDb介绍 Redis中存在“数据库”的概念,该结构由redis.h中的redisDb定义。我们知道Redis提供string、list、set、zset、hash五种数据类型的存储,在Redis运行时,服务器中会存在许

Franco的博客 576

EtherCAT丢包问题深度解析:从原理到实战排查指南

在工业自动化与运动控制领域,实时通讯网络的稳定性是系统可靠运行的基石。EtherCAT(以太网控制自动化技术)作为一种高性能工业以太网协议,其核心在于独特的“飞读飞写”机制和纳秒级的分布时钟同步,以实现多设备间的精准协同。然而,物理链路干扰、配置不当或硬件性能瓶颈都可能导致数据帧丢失,破坏时钟同步,进而引发伺服抖动、定位精度下降乃至生产线停机。本文聚焦于EtherCAT丢包这一典型故障,结合SSC(Slave Stack Code Tool)从站协议栈开发与实时系统调优等工程实践,系统阐述其成因、影响及一套

weixin_32050773的博客 341

Trae+雪球MCP,化身专业的股票分析师(玩转100个MCP系列第二弹)

大家好呀,我是程序员夏叶,深度AI编程使用者,专注于分享AI编程方面的使用技巧、经验以及前沿资讯,有兴趣的可以关注我的公众号,一起学习,共同进步。第二弹,使用Trae+雪球MCP,获取股票实时行情,分析股票走向等,写代码间隙看看股票价格,研究研究行情,哈哈。真的很不错呀,最关键的是,足够隐蔽,同事以为你在写代码,实际你在浏览股票行情,哈哈,感兴趣的可以试一下哦。说实话,沧州明珠这只股我算是砸手里了,不过还好买的不多,哈哈。

cpp加油站的专栏 518

从源码看 Redis:深入理解 redisDb 和 redisObject

Redis 是一个广泛使用的内存数据库,以其高性能和丰富的数据结构而闻名。不同于磁盘数据库,磁盘数据库将数据读取到文件中维护,而内存数据库将数据存储在内存中,意味着其想要维护数据,必须在代码中维护一个保存数据的结构,而redis由c语言编写,对应的其一定是通过结构体来保存数据的结构。

dxh9231028的博客 1518

学Simulink--基于Simulink的无迹卡尔曼滤波(UKF)目标跟踪仿真建模示例

本文详细介绍了基于Simulink的无迹卡尔曼滤波(UKF)目标跟踪仿真建模方法。首先分析了UKF相比EKF在强非线性场景下的优势,包括无线性化误差、计算效率高和鲁棒性强等特点。接着阐述了UKF的核心原理,重点讲解了无迹变换(UT)的三个关键步骤:Sigma点生成、非线性传播和加权统计重构。文章还详细说明了二维机动目标的状态向量定义、非线性状态转移模型和雷达观测模型。 在Simulink实现部分,提供了完整的建模步骤和核心代码,包括目标运动模型、雷达传感器模型和UKF算法模块的实现。通过蛇形机动场景的仿真实

xiaoheshang_123的博客 89

Redis源码剖析--数据库db

按照Redis源码剖析–源码结构解析一文中给自己规定的六个阶段来学习Redis。目前前三个阶段的学习以及完成了,这些都是和系统的耦合性比较小的部分,所以看起来也比较轻松。从这篇博客开始,就进入到第四阶段的源码剖析了。Redis的各个功能的实现将会顺着我们的逐步深入而变得清晰明了,如果读者跟着我的步伐一起学习,到了这一刻,想必也是兴奋的。废话也不多说了,前面所有的数据结构都是为后面的功能实现做铺垫。...

mhHao的博客 227

YOLOv8【第一章:基础入门篇·第2节】一文搞定,数据集制作与标注完全指南!

🏆 本文收录于 《YOLOv8实战:从入门到深度优化》 专栏,该专栏专注于分享我在YOLOv8目标检测模型中的实战经验,涵盖从入门基础到深度优化的全方位解决方案。无论你是计算机视觉的新手,还是具有多年深度学习经验的专家,本专栏都将为你提供高效、可复现的技术方案,助你掌握YOLOv8的核心技术,提升目标检测性能,突破瓶颈,优化模型,并加速项目开发🚀!

**My Coding Family** 1507

KingSCADA3.8保姆级安装教程

大家好,我是雷工!最近开始学习KingSCADA,今天这篇详细记录安装KingSCADA3.8的过程。首先下载需要的安装版本,此处以从官网下载的最新版本KingSCADA3.8为例,双击:Setup.exe;

做有价值的事,积累有价值的笔记! 1万+

Redis字典实现揭秘:从redisDb到hash冲突

因为散列表的存储是通过hash(key)%size=index 确定索引,sizemask是对取余长度的优化,将hash(key)%size变成hash(key) &sizemask,把除法优化为二进制的运算,从而提高执行速度,这种优化的前提是 数组的长度必须是2的n次幂。扩容时,rehash的key存储的index要么在oldIndex,要么在oldIndex+oldSize,不会存放到其他的index中,因为同一个key经过hash得到的值是不会变的,只是取余的size变成了2*size。

2509_93923260的博客 972

IEM模型,地表粗糙度模拟

利用微波反演土壤水分时候,利用IEM模型可以实现对随机地表的模拟

深入底层C源码剖析Redis底层数据结构

Redis 基本特性 1. 非关系型的键值对数据库,可以根据键以O(1) 的时间复杂度取出或插入关联值 2. Redis 的数据是存在内存中的 3. 键值对中键的类型可以是字符串,整型,浮点型等,且键是唯一的 4. 键值对中的值类型可以是string,hash,list,set,sorted set 等 5. Redis 内置了复制,磁盘持久化,LUA脚本,事务,SSL, ACLs,客户端缓存,客户端代理等功能 ...

java_huizhen的博客 234

SIMATIC S7-1200, S7-1500 PID 控制[手册]

SIMATIC S7-1200, S7-1500 PID 控制[手册]

STM32F407中文手册

STM32f407中文手册 本参考手册面向应用开发人员,提供有关使用 STM32F405xx/07xx、STM32F415xx/17xx、 STM32F42xxx 和 STM32F43xxx 微控制器存储器与外设的完整信息。STM32F405xx/07xx、STM32F415xx/17xx、STM32F42xxx 和 STM32F43xxx 构成一个微 控制器系列,各产品具有不同的存储器大小、封装和外设。有关订购信息以及器件的机械与电气特性,请参见数据手册。有关 ARM Cortex™-M4F 内核的信息,请参见《Cortex™-M4F 技术参考手册》。

Redis数据库底层源码剖析

按照Redis源码剖析–源码结构解析一文中给自己规定的六个阶段来学习Redis。目前前三个阶段的学习以及完成了,这些都是和系统的耦合性比较小的部分,所以看起来也比较轻松。从这篇博客开始,就进入到第四阶段的源码剖析了。Redis的各个功能的实现将会顺着我们的逐步深入而变得清晰明了,如果读者跟着我的步伐一起学习,到了这一刻,想必也是兴奋的。废话也不多说了,前面所有的数据结构都是为后面的功能实现做铺垫。...

至宝屠夫的博客 439

数学建模模型总结

这是本人在两年内准备参加数学建模竞赛搜集到的模型资料,已经经过了本人的筛选和整理,希望能够对准备参加数学建模竞赛的学弟学妹们提供一些帮助!

Redis源码剖析之数据库结构

以下涉及到的源码均为redis5.0-rc3版本的代码【点击查看官方源码】 Redis服务器初始化数据库的个数为16个,由server.h头文件中的宏定义而来,如下所示: #define CONFIG_DEFAULT_DBNUM 16 并且每个数据库的结构都有一个redisDb构成,如下是redisDb的数据结构定义(server.h头文件中),各字段的含义可见上面的源码注释: ...

相逢一笑 229

redis源码阅读之数据结构(浅易说明)

好一段时间只在有道笔记上上写笔记而写博客了,最近忽然发现,只写笔记的话容易造成信息丢失,所以想把笔记的内容整理成博客留存下来。 redis是一个内存型nosql数据库,之前阅读其源码,就感觉到作者的牛逼之处,源码中的数据结构透露出对内存的极高的利用率(ps:我看的是redis 5.05版本的源码)。 从redis给出的用法知道,redis顶级的对象是db,然后下面存储了string、hash、list、set、sortset、stream(少有人用)这几种数据; ...

Gavin_new的博客 601

redis单机数据库源码解析

1:在读取完数据后,如果要返回内容,如果内容比较小,那么可以直接放在输出数组中返回,如果比较大就需要防止输出链表中返回并激活写事件,写完之后记得取消。2:并同时将这个redis-clent文件描述符添加到epoll事件管理机制中,进行监听的读事件以及写事件。3:同时根据argv[0]寻找到该函数的执行函数复制给client对象中的command函数。2:解析输入缓冲区中的内容,分解到client对象中的argc以及argv中。1:读取内容到redis-client结构体中的输入缓冲区中。

qq_41409979的博客 291

【Redis从入门到精通】第25篇:Redis数据库那些事——16个数据库的选择与键空间管理

Redis多数据库机制深度解析 Redis默认配置16个逻辑数据库(0-15),通过SELECT命令切换,底层每个数据库对应一个redisDb结构体,包含独立的键空间字典(dict)和过期字典(expires)。键空间本质是哈希表,存储所有键值对,键为字符串,值为redisObject对象。虽然支持多库,但生产环境通常推荐使用不同Redis实例而非多库,原因包括:缺乏跨库原子操作、SCAN遍历需指定库、集群模式仅支持单库等。键命名建议采用"业务域:子域:ID"的层级结构(如"user:session:100

xyghehehehe的专栏 274

Redis100篇 - 从源码角度看:Redis是怎么处理客户端请求的

Redis请求处理流程解析:从连接建立到命令执行 本文深入剖析Redis处理客户端请求的核心机制,通过源码分析揭示其高性能背后的设计原理。主要内容包括: 架构概览:Redis基于单线程事件驱动模型,核心组件包括网络I/O、事件循环、命令处理器和数据库。 连接建立:通过acceptTcpHandler接收新连接,创建client结构体并注册可读事件到事件循环。 请求读取:readQueryFromClient以非阻塞方式读取数据到动态缓冲区,限制单请求最大1GB防止OOM。 协议解析:支持Inline和Bul

千淘万漉虽辛苦,吹尽狂沙始到金 2万+
上一篇: dict
下一篇: extends多个接口
TuxedoLinux
博客等级 码龄9年 268粉丝 34原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值