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数据一般都会设置过期时间,如果可以容忍短期数据不一致,也不用考虑。

相关推荐
yx9o1 小时前
Kafka 源码 KRaft 模式本地运行
分布式·kafka
monkey_meng1 小时前
【Rust中多线程同步机制】
开发语言·redis·后端·rust
Gemini19951 小时前
分布式和微服务的区别
分布式·微服务·架构
G丶AEOM1 小时前
分布式——BASE理论
java·分布式·八股
hlsd#2 小时前
go 集成go-redis 缓存操作
redis·缓存·golang
阑梦清川2 小时前
在鱼皮的模拟面试里面学习有感
学习·面试·职场和发展
奶糖趣多多4 小时前
Redis知识点
数据库·redis·缓存
CoderIsArt5 小时前
Redis的三种模式:主从模式,哨兵与集群模式
数据库·redis·缓存
P.H. Infinity7 小时前
【RabbitMQ】03-交换机
分布式·rabbitmq
ketil279 小时前
Redis - String 字符串
数据库·redis·缓存