【高并发下Redis可能存在的问题及解决方案】

一、缓存穿透(面试问题)

在实际开发中,添加缓存工具的目的,减少对数据库的访问次数,增加访问效率。

肯定会出现Redis中不存在的缓存数据。例如:访问id=-1的数据。可能出现绕过redis依然频繁访问数据库的情况,称为缓存穿透,多出现在查询为null的情况不被缓存时。

解决办法:

如果查询出来为null数据,把null数据依然放入到redis缓存中,同时设置这个key的有效时间比正常有效时间更短一些。


if(list==null){
    // key value 有效时间 时间单位
    redisTemplate.opsForValue().set(navKey,null,10, TimeUnit.MINUTES);
}else{
    redisTemplate.opsForValue().set(navKey,result,7,TimeUnit.DAYS);
}


二、缓存击穿(面试问题)

实际开发中,考虑redis所在服务器中内存压力,都会设置key的有效时间。一定会出现键值对过期的情况。如果正好key过期了,此时出现大量并发访问,这些访问都会去访问数据库,这种情况称为缓存击穿。

解决办法:永久数据。

加锁:防止出现数据库的并发访问。

1. ReentrantLock(重入锁)

JDK对对于并发访问处理的内容都放入了java.util.concurrent中


mac上redis无法保存 redis set不能存储大量数据_缓存


ReentrantLock的性能和synchronized是没有区别的,但是API使用起来更加方便。


@SpringBootTest
public class MyTest {
    @Test
    public void test(){
        new Thread(){
            @Override
            public void run() {
                test2("第一个线程111111");
            }
        }.start();
        new Thread(){
            @Override
            public void run() {
                test2("第二个线程222222");
            }
        }.start();
        try {
            Thread.sleep(20000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    ReentrantLock lock = new ReentrantLock();
    public void test2(String who){
        lock.lock();
        if(lock.isLocked()) {
            System.out.println("开始执行:" + who);
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("执行完:" + who);
            lock.unlock();
        }
    }
}


2. 解决缓存击穿实例代码

只有在第一次访问时和Key过期时才会访问数据库。对于性能来说没有过大影响,因为平时都是直接访问redis。


private ReentrantLock lock = new ReentrantLock();
@Override
public Item selectByid(Integer id) {
    String key = "item:"+id;
    if(redisTemplate.hasKey(key)){
        return (Item) redisTemplate.opsForValue().get(key);
    }
    lock.lock();
    if(lock.isLocked()) {
        Item item = itemDubboService.selectById(id);
        // 由于设置了有效时间,就可能出现缓存击穿问题
        redisTemplate.opsForValue().set(key, item, 7, TimeUnit.DAYS);
        lock.unlock();
        return item;
    }
    // 如果加锁失败,为了保护数据库,直接返回null
    return null;
}


三、缓存雪崩(面试问题)

在一段时间内,出现大量缓存数据失效,这段时间内数据库的访问频率骤增,这种情况称为缓存雪崩。

解决办法:永久生效。

自定义算法,例如:随机有效时间。让所有key尽量避开同一时间段。


int seconds = random.nextInt(10000);
redisTemplate.opsForValue().set(key, item, 100+ seconds, TimeUnit.SECONDS);