redis:Redis数据过期删除策略深度解析与生产实践

Redis数据过期删除策略深度解析与生产实践

一、Redis过期删除策略流程图

检查过期
未检查
随机采样
未处理
设置过期时间
主动访问时
立即删除
定时任务触发
删除已过期key
继续存留
内存释放

二、过期key处理时序图

ClientRedisServerExpiryDictSETEX order:1001 3600 "pending"记录过期时间OK随机采样20个key返回过期key列表删除已过期keyloop[定时任务]GET order:1001检查过期时间已过期同步删除key(nil)ClientRedisServerExpiryDict

三、深度技术解析(结合电商订单系统实战)

在阿里订单系统中,我们使用Redis过期机制管理3000万+订单状态,以下是核心实践经验:

  1. 内存优化配置
# 调整过期删除频率(默认10hz)
config set hz 100  # 提高CPU使用率换取更及时删除

# 控制采样数量
config set maxmemory-samples 10  # 默认5,影响LRU精度
  1. 大key过期问题
  • 监控大key过期事件:
// 使用Keyspace通知
jedis.configSet("notify-keyspace-events","Ex");
jedis.psubscribe(new JedisPubSub() {
    @Override
    public void onPMessage(String pattern, String channel, String message) {
        if(channel.equals("__keyevent@0__:expired")) {
            monitorBigKeyExpire(message);
        }
    }
}, "__keyevent*__:expired");
  1. 性能对比数据
    | 策略组合 | 内存占用 | CPU使用 | 删除延迟 |
    |---------|--------|--------|---------|
    | 仅惰性删除 | 高(30%) | 低(5%) | 最高60s |
    | 惰性+定时(10hz) | 中(15%) | 中(20%) | 10-30s |
    | 惰性+定时(100hz) | 低(5%) | 高(50%) | 1-5s |

  2. 生产环境问题

  • 避免同时大量key过期(导致请求堆积):
// 随机分散过期时间
public void setOrderStatus(String orderId, String status) {
    int ttl = 3600 + ThreadLocalRandom.current().nextInt(600); // 1小时±10分钟
    jedis.setex("order:"+orderId, ttl, status);
}

四、大厂面试深度追问

追问1:Redis的主动过期与被动过期如何协同工作?详细说明其内存回收算法与性能影响

解决方案

Redis采用多策略协同的混合回收机制:

  1. 惰性删除实现
// db.c中查找key时检查
int expireIfNeeded(redisDb *db, robj *key) {
    if (!keyIsExpired(db,key)) return 0;
    if (server.masterhost != NULL) return 1;
    propagateExpire(db,key,server.lazyfree_lazy_expire);
    notifyKeyspaceEvent(NOTIFY_EXPIRED,"expired",key,db->id);
    return server.lazyfree_lazy_expire ? 
        dbAsyncDelete(db,key) : dbSyncDelete(db,key);
}
  1. 定时任务架构
// server.c中时间事件处理
int serverCron(struct aeEventLoop *eventLoop, long long id, void *clientData) {
    databasesCron();  // 每100ms执行一次
}

void databasesCron(void) {
    // 每DB随机采样
    for (j = 0; j < dbs_per_call; j++) {
        int expired = 0;
        redisDb *db = server.db+(current_db % server.dbnum);
        do {
            expired = activeExpireCycleTryExpire(db);
        } while (expired > 0 && timelimitExit());
    }
}
  1. 内存回收优化
  • 异步删除配置:
lazyfree-lazy-expire yes  # 过期key异步删除
lazyfree-lazy-server-del yes  # 命令删除异步化
  1. 生产环境调优
# 监控过期key堆积
redis-cli info stats | grep expired_keys
# 调整删除策略参数
config set active-expire-effort 2  # 1-10,默认1

追问2:如何解决海量key同时过期导致的Redis卡顿问题?详细说明预热方案与分片策略

解决方案

在字节跳动秒杀系统中,我们通过三级方案解决瞬时过期问题:

  1. 过期时间分散
// 基础分散算法
public int getRandomTtl(int baseTtl) {
    int jitter = (int)(baseTtl * 0.2);  // ±20%抖动
    return baseTtl - jitter + ThreadLocalRandom.current().nextInt(jitter * 2);
}

// 层级分散(结合业务)
public void setActivityData(String activityId, String data) {
    int shard = activityId.hashCode() % 10;
    int ttl = 3600 * (1 + shard/10);  // 1-1.9小时分片
    jedis.setex("activity:"+shard+":"+activityId, ttl, data);
}
  1. 预热删除策略
-- 提前触发删除脚本
local keys = redis.call('SCAN', 0, 'MATCH', 'activity:*', 'COUNT', 100)[2]
for _,key in ipairs(keys) do
    if redis.call('TTL', key) < 300 then  -- 5分钟内将过期
        redis.call('DEL', key)
    end
end
  1. 分片集群方案
// 基于时间的分片策略
public String getTimeShardKey(String bizKey) {
    int hour = LocalDateTime.now().getHour();
    return "shard:" + (hour % 4) + ":" + bizKey;  // 4小时轮转
}

// 分片过期控制
public void setShardedData(String key, String value) {
    String shardKey = getTimeShardKey(key);
    jedis.setex(shardKey, 4 * 3600, value);  // 4小时固定过期
}
  1. 监控与应急方案
# 实时监控过期key数量
watch -n 1 "redis-cli info | grep expired_keys"

# 紧急处理命令
redis-cli --eval emergency_expire.lua "pattern:*" , 600  # 批量重置TTL

五、高级应用与源码分析

  1. 过期字典结构
typedef struct redisDb {
    dict *dict;                 // 主键空间
    dict *expires;              // 过期字典
    dict *blocking_keys;        // 阻塞key
    // ...其他字段
} redisDb;
  1. 内存回收监控
def monitor_eviction():
    while True:
        info = redis.info("memory")
        used_mem = int(info["used_memory"])
        max_mem = int(info["maxmemory"])
        if used_mem > max_mem * 0.7:
            alert("内存压力告警")
        time.sleep(60)
  1. 生产环境调优
# 优化内核参数
echo 1 > /proc/sys/vm/overcommit_memory
echo 512 > /proc/sys/net/core/somaxconn

# 禁用透明大页
echo never > /sys/kernel/mm/transparent_hugepage/enabled
  1. 集群环境方案
// 分布式过期监控
public class ClusterExpireMonitor {
    private ScheduledExecutorService scheduler;
    
    public void start() {
        scheduler.scheduleAtFixedRate(() -> {
            for(Jedis node : getAllNodes()) {
                long expired = node.dbSize() - node.getDBSize();
                if(expired > 10000) {
                    triggerNodeExpire(node);
                }
            }
        }, 5, 5, TimeUnit.MINUTES);
    }
}

Redis的过期策略设计体现了空间与时间的精妙平衡。在实际工程中,需要根据业务特征(数据量、访问模式、一致性要求)选择最适合的配置方案,并通过压力测试验证系统极限。建议建立完善的监控体系,特别关注expired_keysevicted_keys指标的变化趋势,提前发现潜在问题。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值