Redis缓存击穿问题及解决思路
1. 什么是缓存击穿2. 解决方案2.1 使用锁来解决(互斥锁)2.2 逻辑过期2.3 进行对比1. 什么是缓存击穿
缓存击穿也叫热点key问题,就是一个被高并发访问并且缓存重建业务比较复杂的key突然失效了,无数的请求会在一瞬间给数据库带来巨大的冲击。也是缓存雪崩中的一个特例。
2. 解决方案
常见的解决方案有两种:
互斥锁逻辑过期
逻辑分析:假设线程1在查询缓存之后,本来应该去查询数据库,然后把这个数据重新加载到缓存的,此时要线程1走完这个逻辑,其他线程就可以从缓存中获取这个数据了,但是线程1在没有走完之前,后续的线程2 3 4 同事来访问这个方法,那么这些线程都不能从缓存中获取数据,那么他们就会同一时间去查缓存,缓存没数据,接着同时去查数据库,同时执行数据库代码,给数据库带来巨大压力。
2.1 使用锁来解决(互斥锁)
因为锁能实现互斥性。假设线程过来,只能一个个的来访问数据库,从而避免对数据库访问压力过大,但是这也会影响查询性能,因为此时会让查询的性能从并行编程串行,我们可以采用tryLocak方法+double check来解决这样的问题。
假设现在线程1过来访问,他查询的缓存没有命中,但是此时他获得了锁的资源,那么线程1一个人去执行逻辑,假设线程2过来,线程2在执行过程中,并没有获得锁,那么线程2就可以进行休眠,直到线程1把锁释放后,线程2获得锁,然后再来执行逻辑,此时就能从缓存中拿到数据了。
2.2 逻辑过期
方案分析:我们之所以会出现这个缓存击穿问题,主要原因是在于我们对key设置了过期时间,假设我们不设置过期时间,其实就不会有缓存击穿的问题,但是不设置过期时间,这样数据不就一直占用我们内存了吗,我们可以采用逻辑过期方案。
我们把过期时间设置在redis的value中,注意:这个过期时间并不会直接作用于redis,而是我们后续通过逻辑代码去处理。假设线程1去查询缓存,然后从value中判断出来当前的数据已经过期了,此时线程1去获得互斥锁,那么其他的线程会进行阻塞,获得了锁的线程他会开启一个线程去进行以前的重构数据的逻辑,直到新开的线程完成这个了逻辑后,才释放锁,而线程1直接进行返回,假设现在线程3过来访问,由于线程2持有着锁,所以线程3无法获得锁,线程3也直接返回数据,只有等到新开的线程2把重建的数据构建完后,其他线程才能走返回正确的数据。
这种方案巧妙在于,异步的构建缓存,缺点在于在构建完缓存之前,返回的都是脏数据。
2.3 进行对比
互斥锁方案:由于保证了互斥性,所以数据一致,且实现简单,因此仅仅只需要加一把锁而已,也没有其他的事情需要操心,所以没有额外的内存消耗,缺点在于有锁就会产生死锁,且只能串行执行性能肯定受到影响。
逻辑过期方案:线程读取过程不需要等待,性能好,有一个额外的线程持有锁去重构数据,但是在重构数据完成之前,其他线程只能返回之前的数据,且实现起来麻烦。
参考资料:
黑马Redis资料。
若有错误,希望大佬指出。
对你有帮助给点个👍再走呗。