数据存储和有效期
在 redis 工作流程中,过期的数据并不需要马上就要执行删除操作。因为这些删不删除只是一种状态表示,可以异步的去处理,在不忙的时候去把这些不紧急的删除操作做了,从而保证 redis 的高效
数据的存储在redis中数据的存储不仅仅需要保存数据本身还要保存数据的生命周期,也就是过期时间。在redis 中 数据的存储结构如下图:
获取有效期Redis是一种内存级数据库,所有数据均存放在内存中,内存中的数据可以通过TTL指令获取其状态
删除策略在内存占用与CPU占用之间寻找一种平衡,顾此失彼都会造成整体redis性能的下降,甚至引发服务器宕机或内存泄漏。
定时删除创建一个定时器,当key设置过期时间,且过期时间到达时,由定时器任务立即执行对键的删除操作
优点节约内存,到时就删除,快速释放掉不必要的内存占用
缺点CPU压力很大,无论CPU此时负载多高,均占用CPU,会影响redis服务器响应时间和指令吞吐量
总结用处理器性能换取存储空间
惰性删除数据到达过期时间,不做处理。等下次访问该数据,如果未过期,返回数据。发现已经过期,删除,返回不存在。这样每次读写数据都需要检测数据是否已经到达过期时间。也就是惰性删除总是在数据的读写时发生的。
expireIfNeeded函数对所有的读写命令进行检查,检查操作的对象是否过期。过期就删除返回过期,不过期就什么也不做~。
执行数据写入过程中,首先通过expireIfNeeded函数对写入的key进行过期判断。
/* * 为执行写入操作而取出键 key 在数据库 db 中的值。 * * 和 lookupKeyRead 不同,这个函数不会更新服务器的命中/不命中信息。 * * 找到时返回值对象,没找到返回 NULL 。 */ robj *lookupKeyWrite(redisDb *db, robj *key) { // 删除过期键 expireIfNeeded(db,key); // 查找并返回 key 的值对象 return lookupKey(db,key); }执行数据读取过程中,首先通过expireIfNeeded函数对写入的key进行过期判断。
/* * 为执行读取操作而取出键 key 在数据库 db 中的值。 * * 并根据是否成功找到值,更新服务器的命中/不命中信息。 * * 找到时返回值对象,没找到返回 NULL 。 */ robj *lookupKeyRead(redisDb *db, robj *key) { robj *val; // 检查 key 释放已经过期 expireIfNeeded(db,key); // 从数据库中取出键的值 val = lookupKey(db,key); // 更新命中/不命中信息 if (val == NULL) server.stat_keyspace_misses++; else server.stat_keyspace_hits++; // 返回值 return val; }执行过期动作expireIfNeeded其实内部做了三件事情,分别是:
查看key判断是否过期
向slave节点传播执行过期key的动作并发送事件通知
删除过期key
/* * 检查 key 是否已经过期,如果是的话,将它从数据库中删除。 * * 返回 0 表示键没有过期时间,或者键未过期。 * * 返回 1 表示键已经因为过期而被删除了。 */ int expireIfNeeded(redisDb *db, robj *key) { // 取出键的过期时间 mstime_t when = getExpire(db,key); mstime_t now; // 没有过期时间 if (when < 0) return 0; /* No expire for this key */ /* Don't expire anything while loading. It will be done later. */ // 如果服务器正在进行载入,那么不进行任何过期检查 if (server.loading) return 0; // 当服务器运行在 replication 模式时 // 附属节点并不主动删除 key // 它只返回一个逻辑上正确的返回值 // 真正的删除操作要等待主节点发来删除命令时才执行 // 从而保证数据的同步 if (server.masterhost != NULL) return now > when; // 运行到这里,表示键带有过期时间,并且服务器为主节点 /* Return when this key has not expired */ // 如果未过期,返回 0 if (now <= when) return 0; /* Delete the key */ server.stat_expiredkeys++; // 向 AOF 文件和附属节点传播过期信息 propagateExpire(db,key); // 发送事件通知 notifyKeyspaceEvent(REDIS_NOTIFY_EXPIRED, "expired",key,db->id); // 将过期键从数据库中删除 return dbDelete(db,key); }判断key是否过期的数据结构是db->expires,也就是通过expires的数据结构判断数据是否过期。
内部获取过期时间并返回。
节约CPU性能,发现必须删除的时候才删除。
缺点内存压力很大,出现长期占用内存的数据。
总结用存储空间换取处理器性能
定期删除周期性轮询redis库中时效性数据,采用随机抽取的策略,利用过期数据占比的方式删除频度。
优点CPU性能占用设置有峰值,检测频度可自定义设置
内存压力不是很大,长期占用内存的冷数据会被持续清理
缺点需要周期性抽查存储空间
定期删除详解