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