如何解决redis缓存击穿?代码实例

背景

缓存击穿,是指对那些会失效的key有高并发的请求,然后在失效的那一瞬间,高并发请求来了,读不到redis的值,全都会把请求打到数据库,导致数据库压力过大

解决方案

1、分布式锁

当发现缓存失效的时候,先使用setnx设置分布式锁,只有获取锁成功的那个线程可以查数据库并回写缓存。

优点:能解决问题。

缺点:
1、如果不支持原子操作的话,这个setnx的分布式锁可能会发生死锁。
2、性能一般

2、本地锁

同上,只是使用本地锁,解决死锁问题。

优点:没有死锁问题

缺点:有多少个机器实例,最多就有多少个“查数据库然后回写redis”的操作

3、软过期

把过期时间写在value中,当读到的时候,发现过期的时候,立马更新过期时间,然后再读数据库、回写。

优点:没有死锁问题

缺点:所谓的“立马”更新过期时间,其实也不是原子性的,高并发的时候会有大量的线程认为过期,然后去读数据库。其实也没完全解决大量查数据库的问题,只是减少了查数据库的量。

贴个代码吧,分布式锁

 /**
     * 先查redis,没有的话查mysql,注意防止缓存击穿(分布式锁)
     */
    private GetRuleChainResponse getRuleChainByIdWithCache(String ruleChainId, int retry) throws InterruptedException {
        retry--;
        if (retry < 0) {
            throw new RuntimeException("getRuleChainById: [" + ruleChainId + "] failed after retry");
        }
        GetRuleChainResponse getRuleChainResponse;
        String getRuleChainKey = RedisConstant.GET_RULE_CHAIN_KEY_PREFIX + ruleChainId;
        String strResponse = jedis.get().get(getRuleChainKey);
        if (null == strResponse) {
            // 缓存失效了
            log.info("cache invalid for:" + getRuleChainKey);
            String lockKey = RedisConstant.GET_RULE_CHAIN_KEY_LOCK_PREFIX + ruleChainId;
            if (jedis.get().setnx(lockKey, RedisConstant.EXISTS_VAL) == 1) {
                log.info("get lock ok for:" + getRuleChainKey);
                // 获取锁成功 才能查数据库 锁自动释放
                jedis.get().expire(lockKey, RULE_CHAIN_QL_EXPRESS_ZSET_LOCK_EXPIRE_SECS);
                // 读取数据库
                getRuleChainResponse = getRuleChainByIdFromDb(ruleChainId);
                // 设置redis
                jedis.get().setex(getRuleChainKey, GET_RULE_CHAIN_KEY_EXPIRE_SECS, gson.toJson(getRuleChainResponse));
                // 解锁
                jedis.get().del(lockKey);
            } else {
                // 获取锁失败 歇一会再重新查
                log.info("get lock failed for:" + getRuleChainKey);
                Thread.sleep(GET_RULE_CHAIN_RETRY_INTERVAL);
                return getRuleChainByIdWithCache(ruleChainId, retry);
            }
        } else {
            // 读取成功,反序列化后返回
            getRuleChainResponse = gson.fromJson(strResponse, GetRuleChainResponse.class);
        }
        return getRuleChainResponse;
    }

参考

aliyun
csdn

你可能感兴趣的:(redis,redis)