Redis-06 Redis面试高频问题、Redis日常开发规避问题

众所周知,使用Redis可极大提升查询效率,也可提供分布式锁解决方案,本章节将根据日常开发中可能出现的实际场景重点讲解使用Redis的架构设计相关问题,以及如何设计高效的分布式锁。

1,缓存穿透

查询缓存和数据库中都没有的数据,导致请求都下沉至数据库,造成数据库压力过大。

常用场景:

1,程序判断机制有误,并发场景下大量查询下沉至数据库;

2,黑客攻击,通过缓存穿透方式打垮数据库,造成系统崩溃。

解决方案:

1,前端程序做限频率

1,后端程序做基本判断。ID<0直接返回;

2,缓存空对象并设置过期时间。可使相同数据第二次请求时快速返回;

3,使用布隆过滤器。通过bitmap将所有对象ID缓存至布隆过滤器,请求先通过布隆过滤器判断对象是否存在,不存在则直接返回,存在再去Redis或数据库拿取数据。

2,缓存击穿

查询缓存中有但数据库没有的数据,导致请求都下沉至数据库,造成数据库压力过大。

常用场景:

1,大批量缓存数据在同一时间或临近时间失效,导致查询下沉;

2,少量缓存数据失效,大并发请求该数据,同样导致查询下沉。

解决方案:

1,查询时为缓存续命,每次查询添加或重置过期时间,引导冷热数据分离;

2,设置缓存的过期时间时,在时间基数上添加随机时间,造成缓存过期时间不同。

3,缓存雪崩

解释一:Redis服务宕机,所有查询直接流向数据库,导致数据压力变大,查询变慢甚至宕机;

解释二:大批量数据在同一时间或临近时间段内失效,后续大量查询流向数据库,导致数据压力变大,查询变慢甚至宕机。

解决方案:同缓存击穿

4,热点key重建优化

常用场景:

某个热点消息或热点商品的缓存过期后,大并发请求查询,可能造成多个线程重建缓存,导致程序和Redis服务压力过大

解决方案:

重建缓存时加分布式锁,并使用双重验证法。

加分布式锁可使得只能有一个线程重建缓存;双重验证可使得其余线程执行时直接从缓存获取。伪代码如下:

java 复制代码
String get(String key) {
    // 从Redis中获取数据
    String value = redis.get(key);
    if (value != null){
        return value;
    }

    // 如果value为空, 则开始重构缓存
    // 只允许一个线程重建缓存, 使用nx, 并设置过期时间ex
    String lockKey = "lock:key:" + key;
    if (redis.set(lockKey , "1", "ex 180", "nx")) {
        
        // 双重验证。从Redis中获取
        value = redis.get(key);

        if (value ==null){
            // 从数据源获取数据
            value = db.get(key);
            // 回写Redis, 并设置过期时间
            redis.setex(key, timeout, value);
        } else {
            // 重置过期时间
            redis.expire(key, timeout);
        }      
        
        // 删除锁
        redis.delete(mutexKey);
    }
    // 其他线程休息50毫秒后重试
    else {
        Thread.sleep(50);
        get(key);
    }

    return value;
}

5,缓存与数据库双写不一致

如下图:线程1在写完数据库后,写Redis前因程序或网络导致延迟,线程2在延迟期间通过数据库查询到该值,更新后写至数据库和Redis。线程1再写Redis时,写入的就是错误的旧值。

解决方案:

使用redisson的读写锁,读写锁对Redis的读读不互斥,读写/写写互斥,保证在同一时刻,只能有一个线程写Redis。

同上例,若使用了读写锁,线程1在写Redis时卡顿,线程2对Redis的写操作将会阻塞,等待线程1写完Redis并释放锁后,线程2才能获取锁再写Redis,保证了Redis数据为最新值。

注意:

1,双写不一致现象为小概率事件。并发量不高的系统,几乎不用考虑;

2,Redis数据一般都会设置过期时间,如果可以容忍短期数据不一致,也不用考虑。

相关推荐
用户479492835691523 分钟前
🤫 你不知道的 JavaScript:`"👦🏻".length` 竟然不是 1?
前端·javascript·面试
沐怡旸27 分钟前
【底层机制】稀疏文件--是什么、为什么、好在哪、实现机制
c++·面试
WHFENGHE28 分钟前
输电线路分布式故障监测装置技术解析
分布式
童话ing1 小时前
Redis常见问题及其处理策略
数据库·redis·缓存
a587692 小时前
消息队列(MQ)高级特性深度剖析:详解RabbitMQ与Kafka
java·分布式·面试·kafka·rabbitmq·linq
hmb↑2 小时前
Kafka 3.9.x 安装、鉴权、配置详解
分布式·kafka·linq
灵感蛙2 小时前
《苍穹外卖》项目日记_Day7
java·spring boot·redis
AAA修煤气灶刘哥3 小时前
缓存世界的三座大山:穿透、击穿、雪崩,今天就把它们铲平!
redis·分布式·后端
月阳羊3 小时前
【硬件-笔试面试题-81】硬件/电子工程师,笔试面试题(知识点:详细讲讲同步时钟与异步时钟通信)
java·经验分享·单片机·嵌入式硬件·面试
奔跑吧邓邓子4 小时前
【Java实战㊱】Spring Boot邂逅Redis:缓存加速的奇妙之旅
java·spring boot·redis·缓存·实战