Redis(四):Key读写及过期策略
DB结构体
Redis默认有16个数据库,存储数据前必须先通过SELECT INDEX来指定DB(默认index为0,DB结构体对应server.h/redisDb),DB主要存储并维护键值对信息。值得注意的是Redis目前没有命令可以获取当前正在操作的库,所以比较好的做法是每次操作前select。
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 */struct evictionPoolEntry *eviction_pool; /* Eviction pool of keys */int id; /* Database ID */long long avg_ttl; /* Average TTL, just for stats */
} redisDb;
dict字典又称键空间,存储了所有键值对,值是五大类型之一;expires存储的是设置了过期时间的key与其失效时间的键值对;
Key读写
当用户读取某个Key时,如果dict中不存在该Key,则miss次数+1并返回客户端空;否则hit次数+1并且更新键的最后一次使用时间,通过这个值可以计算出key的闲置时间,闲置时间的长短又会影响回收策略,如果Key已失效则同时清除dict与expires中的键值对,并向Slave发送DEL命令。通过info stats可以查看hist和miss等信息,通过Object idletime key可以查看key的闲置时间。
当写入某个Key时,如果dict中已经存在该key则进行对应值的更新,否则存入新的键值对。对设置了过期时间的Key写入时同时也存入一份到expires字典当中,删除时同样也清除expires中对应键值对。
Key过期时间设置
redis有四个命令expire、pexpire、expireat、pexpireat可以绑定key的过期时间(persist可以解除绑定)。expire和pexpire设置key可以存活的时间,expireat和pexpireat 设置key失效的时间点,前者以秒为单位,后者以毫秒为单位,前三个命令底层使用的都是pexpireat。
Key过期清理策略
- 定时清理:在设置k/v对时,同时开启一个定时器,或将失效时间点注入某个定时器,在有效时间到达后移除该键值对。这种方式一般能尽快释放被占用的内存,但对CPU性能消耗很严重,特别是在大量数据或大量连续请求的情况下,根本没这么多CPU资源拿来消耗,实际中并不常见。
- 惰性清理:在客户端请求某个key时才检测其有效性。这种方式不会像定时清理一样大量消耗CPU资源,但对于内存却可能产生内存泄漏,如果客户端永远不请求那些失效的key,那么这些key就会一直驻留在内存中,永远无法释放(flush等操作除外)。
- 定期清理:周期性的检测key的有效性(不一定是全量key),这种方式即不会大量消耗CPU资源,也不会导致内存泄漏,但key基本上不会被及时清理,实际中清理的时机与频率并不容易确定。
Redis综合采用了惰性清理与定期清理策略,关于惰性清理在上方的已经说明了,这里分析一下定期清理的源码,在3.2.8版本中源码位于server.c/activeExpireCycle,这个函数通过周期性调用serverCron().databasesCron()来间接调用,频率默认为
#define CONFIG_DEFAULT_HZ 10 /* Time interrupt calls/sec. */ 每秒10次,源码如下:
/**type=ACTIVE_EXPIRE_CYCLE_FAST表示快清理; type=ACTIVE_EXPIRE_CYCLE_SLOW表示慢清理;*/
void activeExpireCycle(int type) {static unsigned int current_db = 0; /* 当前清理的DB */static int timelimit_exit = 0; /* 执行时长是否已到 */static long long last_fast_cycle = 0; /* 上次运行时间. */int j, iteration = 0;int dbs_per_call = CRON_DBS_PER_CALL; /* 默认请理DB数量:16*/long long start = ustime(), timelimit;if (clientsArePaused()) return;if (type == ACTIVE_EXPIRE_CYCLE_FAST) {/* 上次快清理尚未结束,本次不开启 */if (!timelimit_exit) return;if (start < last_fast_cycle + ACTIVE_EXPIRE_CYCLE_FAST_DURATION*2) return;last_fast_cycle = start;}/* 确定DB数量 */if (dbs_per_call > server.dbnum || timelimit_exit)dbs_per_call = server.dbnum;/* 慢清理的执行时长: 1000000 * 25/10/100 = 25000 == 25毫秒 */ timelimit = 1000000*ACTIVE_EXPIRE_CYCLE_SLOW_TIME_PERC/server.hz/100;timelimit_exit = 0;if (timelimit <= 0) timelimit = 1;/* 快清理执行时长:1毫秒 */if (type == ACTIVE_EXPIRE_CYCLE_FAST)timelimit = ACTIVE_EXPIRE_CYCLE_FAST_DURATION; /* in microseconds. *//* 迭代DB */for (j = 0; j < dbs_per_call; j++) {int expired;redisDb *db = server.db+(current_db % server.dbnum);/* DB索引加1,指向下一个要处理的DB */current_db++;/* 从当前DB中随机抽取最多20个key* 1:获取每个key的ttl,算出平均ttl* 2:清理失效的key,并记录失效key的个数,如果失效key的个数不超过1/4(少于5个),则认为当前DB暂不需要清理,执行下个DB的处理。* 3:每执行16次,则检测是否执行时长已到,执行时间已达上限则退出本次清理。等待下次周期性调用*/do {unsigned long num, slots;long long now, ttl_sum;int ttl_samples;if ((num = dictSize(db->expires)) == 0) {db->avg_ttl = 0;break;}slots = dictSlots(db->expires);now = mstime();if (num && slots > DICT_HT_INITIAL_SIZE &&(num*100/slots < 1)) break;expired = 0;ttl_sum = 0;ttl_samples = 0;/* 随机抽取最多20个key */if (num > ACTIVE_EXPIRE_CYCLE_LOOKUPS_PER_LOOP)num = ACTIVE_EXPIRE_CYCLE_LOOKUPS_PER_LOOP; while (num--) {dictEntry *de;long long ttl;if ((de = dictGetRandomKey(db->expires)) == NULL) break;ttl = dictGetSignedIntegerVal(de)-now;if (activeExpireCycleTryExpire(db,de,now)) expired++;/* 记录失效个数 */if (ttl > 0) { ttl_sum += ttl;ttl_samples++;}}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);}/* 每执行16次检测一次执行时长 */iteration++;if ((iteration & 0xf) == 0) { /* check once every 16 iterations. */long long elapsed = ustime()-start;latencyAddSampleIfNeeded("expire-cycle",elapsed/1000);if (elapsed > timelimit) timelimit_exit = 1;}/* 执行时长已到,退出本次清理. */if (timelimit_exit) return;/* 失效个数>1/4则继续,否则结束对当前DB的清理. */} while (expired > ACTIVE_EXPIRE_CYCLE_LOOKUPS_PER_LOOP/4);}
}
基本上这个函数的工作模式可以概括如下:
1)迭代一定数量的DB,最大数量16。
2)从每个DB中随机抽取最多20个key
* 1:获取每个key的ttl,用于统计求平均ttl
* 2:清理失效的key,并记录失效key的个数,如果失效key的个数不超过1/4(少于5个),则认为当前DB暂不需要清理,执行下个DB的处理。
* 3:为了防止长时间的阻塞,每执行16次检测是否执行时长已到,执行时间已达上限则退出本次清理,等待下次周期性调用。但当DB数量不足16个时,此步不会被执行,此时即使执行时长已到,也不会结束,直到最后清理完成才会终止。
RDB和AOF对过期Key的处理
RDB和AOF重写本质上都是存储当前内存中的数据,只不过RDB以数据文件的形式存储,而重写AOF则是以命令形式存储,但是对于内存中那些已经失效的Key,两者都不会存储,对于RDB来说只需要保存可用数据,没必要在加载后再进行清理无效数据,对于AOF来说只需要保存最小指令集,也没必要保存写入和删除两条指令。但正常的AOF对于失效Key,会追加一条Del指令到aof文件中。
主从节点对过期的Key的处理
对于从节点而言由于它的数据来源于主节点,为了保持与主节点状态一致,在Master发送删除指令之前,它不会采用任何策略来清理失效的key, 但针对失效Key的所有请求都会返回空。之所以返回空,是因为从节点在key失效但尚未接收到或丢失主节点del命令的情况下,需要与主节点保持一致的响应。
总结:
- Redis数据库主要由dict和expires两个字典构成,其中dict字典负责保存键值对,键是一个字符串,值是五大类型之一;expires字典则负责保存键与过期时间对。所以对键的操作都是建立在字典操作之上
- Redis使用惰性删除和定期删除两种策略来删除过期的键
- 一个失效键被删除之后,会追加一条DEL到现有AOF文件中,但对于RDB或AOF重写都不会包含已经过期的键
- 主节点删除某个键时,会向所有从节点发送一条DEL命令;对于从节点来说,为了保持与主节点的一致性,在未接收到DEL命令前不会采用任何策略删除它,但对失效key的所有请求都返回空。
Redis(四):Key读写及过期策略相关推荐
- Redis系列(三)--过期策略
制定Redis过期策略,是整个Redis缓存策略的关键之一,因为内存来说,公司不可能无限大,所以就要对key进行一系列的管控. 文章结构: (1)理解Redis过期设置API(命令与Java描述版本) ...
- redis同步效率秒_redis过期策略、内存淘汰策略、持久化方式、主从复制
一.Redis的过期策略以及内存淘汰策略: 1.过期策略:定期删除+惰性删除: ①定期删除:redis默认每隔100ms就随机抽取一些设置了过期时间的key,检查其是否过期,如果有过期就删除.注意这里 ...
- Redis的持久化机制、过期策略、淘汰策略
文章目录 一.持久化机制 1. RDB机制 2. AOF机制 RDB的优缺点 AOF的优缺点 持久化机制选择 二.过期策略 常见过期策略 Redis过期策略 1. 定期删除 2. 惰性删除 RDB对过 ...
- Redis(五)Redis内存维护方案(过期策略及淘汰策略)
目录 内存维护解决方案 过期策略 内存淘汰策略 内存维护解决方案 在Redis的配置文件中redis.conf 中memeory managment # maxmemory <bytes> ...
- 【Redis扩展篇(一)】过期策略
Redis过期策略 0. 前言 Redis所有的数据结构都可以设置过期时间,时间一到就会被自动删除.但是会不会因为统一时间太多的key过期,导致Redis执行执行出现卡顿.因为Redis是单线程的,收 ...
- Redis和Memcached:数据类型 过期策略 持久策略 虚拟内存 Value大小
1.Redis不仅仅支持简单的k/v类型的数据,同时还提供list,set,hash.bitmaps.hyperloglog.geo 2.虚拟内存Redis当物理内存用完时,可以将一些很久没用到的v ...
- Redis 更新key值导致过期时间失效问题
场景再现 首先,往redis里面存一个key, 然后,设置超时时间为300s, 如下图所示 紧接着,更新name的值, 问题来了,重新设置了name的值之后,这个key的过期时间是多少呢? A 过期时 ...
- Redis[5] key的过期时间删除策略、实现lru算法、持久化配置
文章目录 Redis[5] key的过期时间删除策略.持久化配置 **Redis6的key过期时间删除策略** Redis服务器实际使用的是惰性删除和定期删除两种策略:通过配合使用这两种删除策略,服务 ...
- Redis学习笔记--Redis数据过期策略详解==转
本文对Redis的过期机制简单的讲解一下 讲解之前我们先抛出一个问题,我们知道很多时候服务器经常会用到redis作为缓存,有很多数据都是临时缓存一下,可能用过之后很久都不会再用到了(比如暂存sessi ...
- php redis hset过期时间,详解Redis中数据过期策略
相信大家对Redis中数据过期有点了解,本文主要介绍了Redis中的数据过期策略,文中通过示例代码介绍的很详细,相信对大家的理解和学习具有一定的参考借鉴价值,有需要的朋友可以参考借鉴,希望能帮助到大家 ...
最新文章
- 基于PyTorch框架的多层全连接神经网络实现MNIST手写数字分类
- 最短路算法模板--SPFA
- 远程桌面mstsc命令参数的使用
- Python查找包含指定字符串的所有文件
- NodeJs将项目上传至服务器
- 项目经理需要具备的技能
- jpg/png格式的图片转换成eps格式
- 【渝粤题库】陕西师范大学201931 唐诗研究 作业
- PHPStorm+Xdebug配置(phpStudy)
- linux 误删文件恢复
- 学习安卓的简单心得,以及LinearLayout的简单使用
- android微信小程序自动填表_微信“填表”类小程序,你可能根本没用对
- 余淼杰老师 经济学原理复习笔记(宏观1) 2020-12-14
- java.io.IOException: Incomplete output stream
- 利用sEMG能量高斯分布特性提取动作信号的方法
- nexus5x刷最新android M
- 设置CCS的license
- 大数据仓库技术实训任务3
- 全球及中国汽车仿真硬件在环测试行业研究及十四五规划分析报告
- 电话主叫号码信息的识别及实现CID