缓存击穿
缓存击穿的就是给某一个热点key设置了过期时间,当key过期的时候,恰好这时间点对这个Key有大量的并发请求,这些请求发现缓存过期一般都会从后端 DB 加载数据并写回缓存(往往热点key的重新构建有比较耗时),这个时候大量并发的请求可能会瞬间把 DB 压垮。
解决方案有两种方式:
1.互斥锁(分布式锁), 发现热点key过期加互斥锁,新线程抢不到锁会不断重试 数据强一致,但会等待。
第一可以使用互斥锁(分布式锁):当缓存失效时,假设有线程过来,只能一个一个的来访问数据库,从而避免对于数据库访问压力过大,但这也会影响查询的性能,因为此时会让查询的性能从并行变成了串行,我们可以采用setnx方法 + double check(第一次获取锁的check,如果有线程已经更新DB并且重建缓存后,应先再次check一下缓存是否命中)来解决这样的问题。
2.逻辑过期,发现key过期,单开线程去查DB重建缓存,原来线程以及新线程直接返回旧数据,数据高可用
第二种方案可以设置当前key逻辑过期,大概是思路如下:
前提是默认该热点key是必命中的 在命令上把这个key设置为永不过期,但是要在逻辑上,也就是通过设置值vlaue去给它一个逻辑过期时间字段。
①:在设置key的时候,设置一个过期时间字段一块存入缓存中,不给当前key设置过期时间,而是设置他的逻辑过期(其实就一个时间戳)
②:当查询的时候,从redis取出数据后判断时间是否过期
③:如果过期则开通另外一个线程进行数据同步(同步之前肯定还是要获取互斥锁的,但此时的锁并不会造成线程间的串行执行,这是因为一旦线程获取不到互斥锁,就会直接放弃,返回过期的数据),当前线程正常返回数据,但是这个数据不是最新的,会造成数据的弱一致(软状态)。
当然两种方案各有利弊:
如果选择数据的强一致性,建议使用分布式锁的方案,性能上可能没那么高,锁需要等,也有可能产生死锁的问题。一般跟钱相关的业务通常要保持强一致性需要使用。
如果选择逻辑过期,则优先考虑的高可用性,性能比较高,但是数据同步这块做不到强一致。业务更注重用户的体验,互联网的行业一般使用逻辑过期。