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

相关推荐
liang_jy7 小时前
Android UID
android·面试
C雨后彩虹8 小时前
任务总执行时长
java·数据结构·算法·华为·面试
小鸡吃米…8 小时前
Python编程语言面试问题二
开发语言·python·面试
算法与双吉汉堡9 小时前
【短链接项目笔记】Day2 用户注册
java·redis·笔记·后端·spring
LYFlied9 小时前
【每日算法】LeetCode 84. 柱状图中最大的矩形
前端·算法·leetcode·面试·职场和发展
zwjapple9 小时前
全栈开发面试高频算法题
算法·面试·职场和发展
程序员爱钓鱼10 小时前
Node.js 编程实战:Redis缓存与消息队列实践
后端·面试·node.js
用户479492835691512 小时前
0.1加0.2为什么不等于0.3-答不上来的都挂了
前端·javascript·面试
ChaITSimpleLove12 小时前
基于 .NET Garnet 1.0.91 实现高性能分布式锁(使用 Lua 脚本)
分布式·.net·lua