设为首页 加入收藏

TOP

Redis面试题(六)
2023-09-23 15:44:12 】 浏览:216
Tags:Redis
规模的key失效,而缓存击穿是一个热点的Key,有大并发集中对其进行访问,突然间这个Key失效了,导致大并发全部打在数据库上,导致数据库压力剧增。这种现象就叫做缓存击穿。

解决方案

1、上面说过了,如果业务允许的话,对于热点的key可以设置永不过期的key。
2、使用互斥锁。如果缓存失效的情况,只有拿到锁才可以查询数据库,降低了在同一时刻打在数据库上的请求,防止数据库打死。当然这样会导致系统的性能变差。

缓存穿透

什么是缓存穿透?

我们使用Redis大部分情况都是通过Key查询对应的值,假如发送的请求传进来的key是不存在Redis中的,那么就查不到缓存,查不到缓存就会去数据库查询。假如有大量这样的请求,这些请求像“穿透”了缓存一样直接打在数据库上,这种现象就叫做缓存穿透。

分析

关键在于在Redis查不到key值,这和缓存击穿有根本的区别,区别在于缓存穿透的情况是传进来的key在Redis中是不存在的。假如有黑客传进大量的不存在的key,那么大量的请求打在数据库上是很致命的问题,所以在日常开发中要对参数做好校验,一些非法的参数,不可能存在的key就直接返回错误提示,要对调用方保持这种“不信任”的心态。

解决方案

1、把无效的Key存进Redis中。如果Redis查不到数据,数据库也查不到,我们把这个Key值保存进Redis,设置value="null",当下次再通过这个Key查询时就不需要再查询数据库。这种处理方式肯定是有问题的,假如传进来的这个不存在的Key值每次都是随机的,那存进Redis也没有意义。
2、使用布隆过滤器。布隆过滤器的作用是某个 key 不存在,那么就一定不存在,它说某个 key 存在,那么很大可能是存在(存在一定的误判率)。于是我们可以在缓存之前再加一层布隆过滤器,在查询的时候先去布隆过滤器查询 key 是否存在,如果不存在就直接返回。

突发性热点缓存重建导致系统压力暴增问题

大V带货,一般是冷门数据,热点商品一般不需要大v带货,由于冷门数据不在缓存,因此会导致大量访问直接请求到数据库。

加锁:
第一个请求进同步代码块会新建key,后面的请求排队过来就不会一直请求数据库,而是直接从缓存中查找了。写成单例设计模式中的DCL的形式。

public Product get(Long productId) {
    Product product = null;
    string productCacheKey = RedisKeyPrefixConst.PRODUCT_CACHE + productId;
    product = getProductFromcache(productCacheKey);
    if (product !=null ){
    	return product;
    }

    RLock hotCacheCreateLock = redisson.getLock( LOCK_HOT_CACHE_CREATE_PREFIX + productTd);
    hotCacheCreateLock.lock();
    try {
    	product = getProductFromCache(productCacheKey);
        if (product != null) {
    		return product;
    		}
    	
        product = productDao. get(productId);
        if (product != null) {
            redisutil.set(productCacheKey,JSON.toJSoNstring(product),
            genProductCacheTimeout(0),TimeUnit.SECONDS);
        }else {
            redisUtil.set(productCacheKey,
                          EMPTY_CACHE,
                          genEmptyCacheTimeout(),
                          TimeUnit.SECONDS);

    }finally {
    hotCacheCreateLock. unlockO); 
    }
	return product;
}

缺点:

  • synchronized锁是本地jvm的锁。
  • 如果有两个直播间:A和B,直播间A的商品上锁,B的商品也需要等待。即synchronized(对象){}

还要维护一个商品id对象,不同的商品用不同(商品id对象)的锁方式。或者使用分布锁,使用分布锁,所有问题都解决了

public static final String Lock_HOT_CREATE_PREFIX="lock:hot_cache_create";
redisson.getLock(Lock_HOT_CREATE_PREFIX+productId);
hotCacheCreateLock.lock();
try{
    // 逻辑代码 从缓存中获取key,如果获取不到就查数据库。
    // ......
    
}final{
    hotCacheCreateLock.unlock(); // del(lockKey)
}

分布式锁解决数据库和缓存双写不一致问题

image.png
线程3在更新缓存前,线程2执行了写数据库和更新缓存操作,结果缓存和数据库不一致

删除缓存的方法也不行,因为本来线程3就是缓存为空,就是要查数据库给key赋值。
image.png

解决方案

分布式锁。问题是由于并发读写同一条数据导致的。
读缓存和查找缓存都使用同一把锁,一个线程拿到锁另一个线程不能修改数据

public Product get(Long productId) {
    Product product = null;
    string productCacheKey = RedisKeyPrefixConst.PRODUCT_CACHE + productId;
    product = getProductFromcache(productCacheKey);
    if (product !=null ){
    	return product;
    }

    RLock hotCacheCreateLock = redisson.getLock( LOCK_HOT_CACHE_CREATE_PREFIX + productTd);
    hotCacheCreateLock.lock();
    try {
    	product = getProductFromCache(productCacheKey);
        if (product != null) {
    		return product;
    		}
    	RLock updateProductLock = 
        						redisson.getLock( LOCK_PRODUCT_UPDATE_PREFIX + productId);
        updateProductLock.lock();
    	try {
    		product = productDao. get(productId);
            if (product != null) {
    			redisutil.set(productCacheKey,JSON.toJSoNstring(product),
    			genProductCacheTimeout(0),TimeUnit.SECONDS);
    		}else {
        		redisUtil.set(productCacheKey,
                              EMPTY_CACHE,
                              genEmptyCacheTimeout(),
                              TimeUn
首页 上一页 3 4 5 6 7 下一页 尾页 6/7/7
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇Spring 多线程的事务处理 下一篇主动写入流对@ResponseBody注解的..

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目