Redis的数据过期策略 - Wangxiaoman/tech-note GitHub Wiki

Redis设置过期的方式

-- 针对key值配置过期
expire ${key} ${seconds}
expireat ${key} ${timestamp}
pexpire ${key} ${milliSeconds}
pexpireat ${key} ${milliseconds-timestamp}

-- 字符串独有方式
setex ${key} ${seconds} ${value} 

persist ${key} — 去掉过期

ttl ${key} — 查看剩余时间

常用过期策略

定时删除

  • 方式:写入key值的时候,为key设置定时器,在key的过期时间到达,主动来删除key值
  • 优点:删除时间精准
  • 缺点:性能开销较大

惰性删除

  • 方式:写入key值得时候,同时记录过期时间,当有该key的访问到达,检测该值的过期时间,如果该值过期,那么将该key删除
  • 优点:性能开销较小
  • 缺点:key的过期完全取决于key值的访问,内存碎片可能会很严重

定期删除

  • 方式:启动一个定时任务,来扫描所有带有expire设置的key值,将过期的key淘汰。
  • 优点:主动删除,占用性能较低
  • 缺点:也会造成一些内存碎片和CPU性能的问题,但是消耗相对适中

Memcached采用的是惰性删除,而Redis采用的是惰性删除+定期删除

ttl在redis中的存储方式

redisdb的结构,expires是和kv存储一样的dict结构

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 */
    struct evictionPoolEntry *eviction_pool;    /* Eviction pool of keys */
    // 数据库号码
    int id;                     /* Database ID */
    // 数据库的键的平均 TTL ,统计信息
    long long avg_ttl;          /* Average TTL, just for stats */
} redisDb;

Redis主动过期设置(hz)

Redis中hz的设置:3.0之后默认为10,也就是代表定时任务每秒会调用10次key值的淘汰任务

// 淘汰时间的计算公式:
#define ACTIVE_EXPIRE_CYCLE_SLOW_TIME_PERC 25 /* CPU max % for keys collection */  
...  
timelimit = 1000000*ACTIVE_EXPIRE_CYCLE_SLOW_TIME_PERC/server.hz/100;  
  • 这里每秒钟的最长淘汰占用时间是固定的250ms(1000000*ACTIVE_EXPIRE_CYCLE_SLOW_TIME_PERC/100),而淘汰频率和每次淘汰的最长时间是通过hz参数控制的。

  • 具体的淘汰过程如下:在redis.c 中的activeExpireCycle() 函数,针对每一个db都有循环,每次从db->expires(参考下面的db数据结构) 集合中获取20个key,如果超过5个(25%)过期则继续循环,直到过期key的数量小于25%,或者整个循环时间超过timelimit的最大值,则停止该秒的淘汰过程。

  • 从分析看出,当redis的过期key不超过25%的时候,提高hz的值可以明显提高扫描key值的最小个数。假设hz为10,则一秒内最少扫描200个key(一秒调用10次*每次最少随机取出20个key),如果hz改为100,则一秒内最少扫描2000个key;另一方面,如果过期key比率超过25%,则扫描key的个数无上限,但是cpu时间每秒钟最多占用250ms。

  • redis.conf(hz的配置建议在10-100之间)

# tasks to perform according to the specified "hz" value.
#
# By default "hz" is set to 10. Raising the value will use more CPU when
# Redis is idle, but at the same time will make Redis more responsive when
# there are many keys expiring at the same time, and timeouts may be
# handled with more precision.
#
# The range is between 1 and 500, however a value over 100 is usually not
# a good idea. Most users should use the default of 10 and raise this up to
# 100 only in environments where very low latency is required.
hz 10