首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >Redis学习笔记(三)redis 的键管理

Redis学习笔记(三)redis 的键管理

作者头像
落寞的鱼丶
发布于 2022-02-07 11:01:13
发布于 2022-02-07 11:01:13
46700
代码可运行
举报
文章被收录于专栏:afjhahfhahajkafjhahfhahajk
运行总次数:0
代码可运行

Redis 的键管理

一、Redis 数据库管理#

Redis 是一个键值对(key-value pair)的数据库服务器,其数据保存在 src/server.h/redisDb 中(网上很多帖子说在 redis.h 文件中,但是 redis 6.x版本目录中都没有这个文件。redisDb 结构应该在 server.h文件中)

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
typedef redisServer {
    ....
    
    // Redis数据库
    redisDb *db;
    
    ....
}

Redis 默认会创建 16 个数据库,每个数据库是独立互不影响。其默认的目标数据库是 0 号数据库,可以通过 select 命令来切换目标数据库。在 redisClient 结构中记录客户端当前的目标数据库:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
typedef struct redisClient {

    // 套接字描述符
    int fd;

    // 当前正在使用的数据库
    redisDb *db;

    // 当前正在使用的数据库的 id (号码)
    int dictid;

    // 客户端的名字
    robj *name;             /* As set by CLIENT SETNAME */

} redisClient;

下面是客户端和服务器状态之间的关系实例,客户端的目标数据库目前为 1 号数据库:

通过修改 redisClient.db 的指针来指向不同数据库,这也就是 select 命令的实现原理。但是,到目前为止,Redis 仍然没有可以返回客户端目标数据库的命令。虽然在 redis-cli 客户端中输入时会显示:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
redis> SELECT 1
Ok
redis[1]> 

但是在其他语言客户端没有显示目标数据库的号端,所以在频繁切换数据库后,会导致忘记目前使用的是哪一个数据库,也容易产生误操作。因此要谨慎处理多数据库程序,必须要执行时,可以先显示切换指定数据库,然后再执行别的命令。

二、Redis 数据库键#

2.1 数据库键空间#

Redis 服务器中的每一个数据库是由一个 server.h/redisDb 结构来表示的,其具体结构如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
typedef struct redisDb {
    //数据库键空间
    dict *dict;                 /* The keyspace for this DB */
    //键的过期时间,字典的值为过期事件 UNIX 时间戳
    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 */
    //正在被 WATCH 命令监视的键
    dict *watched_keys;         /* WATCHED keys for MULTI/EXEC CAS */
    //数据库号端
    int id;                     /* Database ID */
    //数据库键的平均 TTL,统计信息
    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;

键空间和用户所见的数据库是直接对应:

  • 键空间的 key 就是数据库的 key, 每个 key 都是一个字符串对象
  • 键空间的 value 是数据库的 value, 每个 value 可以是字符串对象、列表对象和集合对象等等任意一种 Redis 对象

举个实例,若在空白数据库中执行一下命令:插入字符串对象、列表对象和哈希对象

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
# 插入一个字符串对象
redis> SET message "hello world"
OK
# 插入包含三个元素的列表对象
redis> RPUSH alphabet "a" "b" "c"
(integer)3
# 插入包含三个元素的哈希表对象
redis> HSET book name "Redis in Action"
(integer) 1
redis> HSET book author "Josiah L. Carlson"
(integer) 1
redis> HSET book publisher "Manning"
(integer) 1

所以说 redis 对数据的增删改查是通过操作 dict 来操作 Redis 中的数据

2.2 数据库键的过期#

我们可以通过两种方式设置键的生命周期:

  • 通过 EXPIRE 或者 PEXPIRE 命令来为数据库中的某个键设置生存时间(TTL,Time To Live)。在经过 TTL 个生存时间后,服务器会自动删除生存时间为0 的键。比如: redis> set key value OK # 设置键的 TTL 为 5 redis> EXPIRE key 5 (integer)1
  • 此外,客户端也可以通过 EXPIREAT 或者PEXPIREAT 命令,为数据库中的某个键设置过期时间(expire time)。过期时间是一个 UNIX 时间戳,当过期时间来临时,服务器就会自动从数据库中删除这个键。比如 redis> SET key value OK redis> EXPIREAT key 1377257300 (integer) 1 # 当前系统时间 redis> TIME 1)"1377257296" # 过一段时间后,再查询key redis> GET key // 1377257300 (nil)
2.2.1 过期时间#

redisDb 中的dict *dictdict *expires 字典 分别保存了数据库中的键和键的过期时间,分别叫做键空间过期字典

  • 过期字典的键是一个指向键空间中的某个键对象
  • 过期字典的值是一个 long long 类型的整数,这个整数保存了键所指向的数据库键的过期时间

2.3 过期键的删除策略#

对于已经过期的数据是如何删除这些过期键的呢?主要有两种方式:惰性删除和定期删除:

1.惰性删除#

是指 Redis 服务器不主动删除过期的键值,而是通过访问键值时,检查当前的键值是否过期

  • 如果过期则执行删除并返回 null
  • 没有过期则正常访问值信息给客户端

惰性删除的源码在 src/db.c/expireIfNeeded 方法中

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
int expireIfNeeded(redisDb *db, robj *key) {

    // 判断键是否过期

    if (!keyIsExpired(db,key)) return 0;

    if (server.masterhost != NULL) return 1;

    /* 删除过期键 */

    // 增加过期键个数

    server.stat_expiredkeys++;

    // 传播键过期的消息

    propagateExpire(db,key,server.lazyfree_lazy_expire);

    notifyKeyspaceEvent(NOTIFY_EXPIRED,

        "expired",key,db->id);

    // server.lazyfree_lazy_expire 为 1 表示异步删除,否则则为同步删除

    return server.lazyfree_lazy_expire ? dbAsyncDelete(db,key) :

                                         dbSyncDelete(db,key);

}

// 判断键是否过期

int keyIsExpired(redisDb *db, robj *key) {

    mstime_t when = getExpire(db,key);

    if (when < 0) return 0; 

    if (server.loading) return 0;

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

    return now > when;

}

// 获取键的过期时间

long long getExpire(redisDb *db, robj *key) {

    dictEntry *de;

    if (dictSize(db->expires) == 0 ||

       (de = dictFind(db->expires,key->ptr)) == NULL) return -1;

    serverAssertWithInfo(NULL,key,dictFind(db->dict,key->ptr) != NULL);

    return dictGetSignedIntegerVal(de);

}
2.定期删除#

与惰性删除不同,定期删除是指 Redis 服务器会每隔一段时间就会检查一下数据库,看看是否有过期键可以清除,默认情况下,Redis 定期检查的频率是每秒扫描 10 次,这个值在 redis.conf 中的 "hz" , 默认是 10 ,可以进行修改。

定期删除的扫描并不是遍历所有的键值对,这样的话比较费时且太消耗系统资源。Redis 服务器采用的是随机抽取形式,每次从过期字典中,取出 20 个键进行过期检测,过期字典中存储的是所有设置了过期时间的键值对。如果这批随机检查的数据中有 25% 的比例过期,那么会再抽取 20 个随机键值进行检测和删除,并且会循环执行这个流程,直到抽取的这批数据中过期键值小于 25%,此次检测才算完成。

定期删除的源码在 expire.c/activeExpireCycle 方法中:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
void activeExpireCycle(int type) {

    static unsigned int current_db = 0; /* 上次定期删除遍历到的数据库ID */

    static int timelimit_exit = 0;      

    static long long last_fast_cycle = 0; /* 上次执行定期删除的时间点 */

    int j, iteration = 0;

    int dbs_per_call = CRON_DBS_PER_CALL; // 需要遍历数据库的数量

    long long start = ustime(), timelimit, elapsed;

    if (clientsArePaused()) return;

    if (type == ACTIVE_EXPIRE_CYCLE_FAST) {

        if (!timelimit_exit) return;

        // ACTIVE_EXPIRE_CYCLE_FAST_DURATION 快速定期删除的执行时长

        if (start < last_fast_cycle + ACTIVE_EXPIRE_CYCLE_FAST_DURATION*2) return;

        last_fast_cycle = start;

    }

    if (dbs_per_call > server.dbnum || timelimit_exit)

        dbs_per_call = server.dbnum;

    // 慢速定期删除的执行时长

    timelimit = 1000000*ACTIVE_EXPIRE_CYCLE_SLOW_TIME_PERC/server.hz/100;

    timelimit_exit = 0;

    if (timelimit <= 0) timelimit = 1;

    if (type == ACTIVE_EXPIRE_CYCLE_FAST)

        timelimit = ACTIVE_EXPIRE_CYCLE_FAST_DURATION; /* 删除操作花费的时间 */

    long total_sampled = 0;

    long total_expired = 0;

    for (j = 0; j < dbs_per_call && timelimit_exit == 0; j++) {

        int expired;

        redisDb *db = server.db+(current_db % server.dbnum);

        current_db++;

        do {

            // .......

            expired = 0;

            ttl_sum = 0;

            ttl_samples = 0;

            // 每个数据库中检查的键的数量

            if (num > ACTIVE_EXPIRE_CYCLE_LOOKUPS_PER_LOOP)

                num = ACTIVE_EXPIRE_CYCLE_LOOKUPS_PER_LOOP;

            // 从数据库中随机选取 num 个键进行检查

            while (num--) {

                dictEntry *de;

                long long ttl;

                if ((de = dictGetRandomKey(db->expires)) == NULL) break;

                ttl = dictGetSignedInteger

                // 过期检查,并对过期键进行删除

                if (activeExpireCycleTryExpire(db,de,now)) expired++;

                if (ttl > 0) {

                    ttl_sum += ttl;

                    ttl_samples++;

                }

                total_sampled++;

            }

            total_expired += expired;

            if (ttl_samples) {

                long long avg_ttl = ttl_sum/ttl_samples;

                if (db->avg_ttl == 0) db->avg_ttl = avg_ttl;

                db->avg_ttl = (db->avg_ttl/50)*49 + (avg_ttl/50);

            }

            if ((iteration & 0xf) == 0) { /* check once every 16 iterations. */

                elapsed = ustime()-start;

                if (elapsed > timelimit) {

                    timelimit_exit = 1;

                    server.stat_expired_time_cap_reached_count++;

                    break;

                }

            }

            /* 判断过期键删除数量是否超过 25% */

        } while (expired > ACTIVE_EXPIRE_CYCLE_LOOKUPS_PER_LOOP/4);

    }

    // .......

}

以上就是Redis 的删除策略。下面来看一个面试题:

面试题:你知道 Redis 内存淘汰策略和键的删除策略的区别吗?#

Redis 内存淘汰策略#

我们可以通过 config get maxmemory-policy 命令来查看当前 Redis 的内存淘汰策略:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
127.0.0.1:6379> config get maxmemory-policy

1) "maxmemory-policy"

2) "noeviction"

当前服务器设置的是 noeviction 类型的,对于 redis 6.x版本,主要有以下几种内存淘汰策略

  • noeviction:不淘汰任何数据,当内存不足时,执行缓存新增操作会报错,它是 Redis 默认内存淘汰策略。
  • allkeys-lru:淘汰整个键值中最久未使用的键值。
  • allkeys-random:随机淘汰任意键值。
  • volatile-lru:淘汰所有设置了过期时间的键值中最久未使用的键值。
  • volatile-random:随机淘汰设置了过期时间的任意键值。
  • volatile-ttl:优先淘汰更早过期的键值。
  • volatile-lfu: 淘汰所有设置了过期时间的键值中最少使用的键值。
  • alkeys-lfu: 淘汰整个键值中最少使用的键值

也就是 alkeys 开头的表示从所有键值中淘汰相关数据,而 volatile 表示从设置了过期键的键值中淘汰数据。

Redis 内存淘汰算法#

内存淘汰算法主要分为 LRU 和 LFU 淘汰算法

LRU(Least Recently Used) 淘汰算法

是一种常用的页面置换算法,LRU 是基于链表结构实现,链表中的元素按照操作顺序从前往后排列。最新操作的键会被移动到表头,当需要进行内存淘汰时,只需要删除链表尾部的元素。

Redis 使用的是一种近似 LRU 算法,目的是为了更好的节约内存,给现有的数据结构添加一个额外的字段,用于记录此键值的最后一次访问时间。Redis 内存淘汰时,会使用随机采样的方式来淘汰数据,随机取5个值,然后淘汰最久没有使用的数据。

LFU(Least Frequently Used)淘汰算法

根据总访问次数来淘汰数据,核心思想是如果数据过去被访问多次,那么将来被访问的频率也更高

本文系转载,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文系转载,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
[深度学习][原创]使用labelImg+yolov5完成所有slowfast时空动作检测项目-训练测试篇
当我们准备好训练集和配置文件后就可以着手训练的事情了。首先安装好slowfast环境,然后打开源码,slowfast/configs/AVA/SLOWFAST_32x2_R50_SHORT.yaml作如下配置
云未归来
2025/07/18
880
[深度学习][原创]使用labelImg+yolov5完成所有slowfast时空动作检测项目-训练测试篇
detectron2 使用总结
detectron2 主要是用于检测和分割的代码框架,像分类这种任务的代码暂时没有
王云峰
2023/10/21
5830
使用Detectron2识别自己的图片数据
最近迷上了实例分割,开始的时候最先研究的是Mask_RCNN,在github上找了一下开源的库,发现很多都是基于Tensorflow的,而我又比较喜欢Pytorch,所以就找了Detectron2
陶陶name
2022/09/28
5790
使用Detectron2识别自己的图片数据
Detectron2入门教程
0. 前言目标:走马观花,两天时间浏览Detectron2源码,稍微记录一下。 与 TensorFlow Object Detection API、mmdetection 一样,Detectron2 也是通过配置文件来设置各种参数,所有的相关内容都像搭积木一样一点一点拼凑起来。我自己感觉,一般所有代码都可以分为三个部分,所以看Detectron2的源码也分为以下三个部分。 数据处理:包括数据读取、数据增强以及其他数据预处理。模型构建:没啥好说的。模型训练、预测、评估:包括模型存取、优化器、学习率、损失函数、
狼啸风云
2020/02/14
10.6K1
Caffe2 - Detectron 图片测试结果
Detectron 图片测试结果 随机找的图片进行测试,结果很惊喜,很赞!!! 1. Box 与 Mask python2 tools/infer_simple.py \ --cfg configs/12_2017_baselines/e2e_mask_rcnn_R-101-FPN_2x.yaml \ --output-dir /tmp/detectron-visualizations \ --image-ext jpg \ --wts https://s3-us-
AIHGF
2019/02/18
6370
Caffe2 - Detectron 图片测试结果
Detectron2 实用上手教程
随着最新的 Pythorc1.3 版本的发布,下一代完全重写了它以前的目标检测框架,新的目标检测框架被称为 Detectron2。本教程将通过使用自定义 COCO 数据集训练实例分割模型,帮助你开始使用此框架。如果你不知道如何创建 COCO 数据集,请阅读我之前的文章——如何创建自定义 COCO 数据集用于实例分割(https://www.dlology.com/blog/how-to-create-custom-coco-data-set-for-instance-segmentation/)。
AI研习社
2019/10/16
8.2K0
Mask R-CNN源代码终于来了,还有它背后的物体检测平台
夏乙 编译整理 量子位 出品 | 公众号 QbitAI “等代码吧。” 从Mask R-CNN论文亮相至今的10个月里,关于它的讨论几乎都会以这句话收尾。 现在,官方版开源代码终于来了。同时发布的,
量子位
2018/03/02
1.1K0
Mask R-CNN源代码终于来了,还有它背后的物体检测平台
深度学习 | Detectron2使用指南
Detectron2是Facebook AI Research的检测和分割框架,其主要基于PyTorch实现,但具有更模块化设计,因此它是灵活且便于扩展的,具体简介可见Github库和Meta AI Blog Post。
Justlovesmile
2022/03/22
7.3K0
深度学习 | Detectron2使用指南
Detectron2 快速开始,使用 WebCam 测试
从其 model zoo 选择一个感兴趣的模型进行推断。这里以 COCO R50-FPN 3x 训练的各类模型进行演示。
GoCoding
2021/05/06
1.4K0
Detectron2 快速开始,使用 WebCam 测试
Detectron2学习五:build_backbone_model配置及实现流程
本文主要讲build_backbone_model的配置及创建流程,目的则是希望大家看完本章节后能够对detectron2中模型创建及使用有清晰的认识,便于后续自定义网络层。若有需要补充希望大家在评论中提出。
CV_Learner
2019/12/16
6.1K1
使用transformer的YOLOv7及TensorRT部署
最近在github上看到一个博主开源的YOLOv7仓库都惊呆了,YOLOv6都还没出来怎么就到YOLOv7了
墨明棋妙27
2022/09/23
1.4K0
[深度学习][原创]slowfast的demo跑起来的正确方法
项目地址:GitHub - facebookresearch/SlowFast: PySlowFast: video understanding codebase from FAIR for reproducing state-of-the-art video models.
云未归来
2025/07/18
780
在 PyTorch 中使用 Detectron2 进行对象检测的指南
目标检测是深度学习的流行应用之一。让我们首先考虑一个现实生活中的例子。大多数人会在手机中使用 Google 相册,它会根据“事物”选项下的照片自动将照片分组。我在下面附上一个片段。
磐创AI
2021/10/27
1.8K0
在 PyTorch 中使用 Detectron2 进行对象检测的指南
目标检测四大开源神器:Detectron2/mmDetectron/darknet/SimpleDet
去年Amusi 盘点过:目标检测三大开源神器:Detectron2/mmDetectron/SimpleDet。大家反映内容很棒,不少同学开始用起来这些目标检测工具,不管用于发Paper还是做项目。
Amusi
2020/07/22
3.9K0
目标检测四大开源神器:Detectron2/mmDetectron/darknet/SimpleDet
专栏 | 从论文到测试:Facebook Detectron开源项目初探
机器之心专栏 作者:陈惠婵 从 RCNN 到 Faster RCNN,再到最近的 FPN 和获得 ICCV Best Paper 的 Mask RCNN,深度学习在物体检测中以绝对优势从众多机器学习算法中脱引而出。大家对 Facebook 的计算机视觉研究项目的开源期盼已久,经过 1 年多的漫长等待,今天 Facebook 终于开源了 Detectron,Detectron 开源项目使用 caffe2 和 python 接口。实现了 10 多篇计算机视觉最新的成果。下面我们简单介绍一下 Detectron
机器之心
2018/05/10
1K0
业界 | Facebook开源Mask R-CNN的PyTorch 1.0基准,比mmdetection更快、更省内存
项目地址:https://github.com/facebookresearch/maskrcnn-benchmark
机器之心
2018/11/30
1.2K0
业界 | Facebook开源Mask R-CNN的PyTorch 1.0基准,比mmdetection更快、更省内存
GitHub榜首:最强目标检测平台Detectron2 ,基于PyTorch完全重构
虽然在某些特定的场景下计算机可以比人类更快、更精准的识别出目标,但实际上,由于各类物体在不同的观测角度、不同的光照成像、遮挡等因素的干扰,计算机视觉的图像识别迄今为止还未能完全达到人类的水平,更遑论超越了。因此目标检测一直以来都是计算机视觉非常基础、也最具有挑战性的课题。
新智元
2019/10/15
14.6K1
GitHub榜首:最强目标检测平台Detectron2 ,基于PyTorch完全重构
Caffe2 - Detectron 初步使用
Detectron 提供了基于 COCO Dataset 的推断和训练使用说明 - Using Detectron.
AIHGF
2019/02/18
1.1K0
从图像标注开始,用 Byzer 加 Xtreme1 完成图像实例分割训练
要完整的支持深度学习,需要一个很长的 Pipeline,通常我们的工作起步于标注平台, 尽管Byzer 也可以作为标注平台的上游,比如对图片和视频做一个统一的处理(诸如缩放成统一大小等),然后再放到标注平台里。
用户2936994
2022/11/12
1.1K0
从图像标注开始,用 Byzer 加 Xtreme1 完成图像实例分割训练
从论文到测试:Facebook Detectron开源项目初探
机器之心专栏 作者:陈惠婵 从 RCNN 到 Faster RCNN,再到最近的 FPN 和获得 ICCV Best Paper 的 Mask RCNN,深度学习在物体检测中以绝对优势从众多机器学习算法中脱引而出。大家对 Facebook 的计算机视觉研究项目的开源期盼已久,经过 1 年多的漫长等待,今天 Facebook 终于开源了 Detectron,Detectron 开源项目使用 caffe2 和 python 接口。实现了 10 多篇计算机视觉最新的成果。下面我们简单介绍一下 Detectron
企鹅号小编
2018/02/02
1K0
从论文到测试:Facebook Detectron开源项目初探
推荐阅读
相关推荐
[深度学习][原创]使用labelImg+yolov5完成所有slowfast时空动作检测项目-训练测试篇
更多 >
交个朋友
加入腾讯云官网粉丝站
蹲全网底价单品 享第一手活动信息
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档