工作中缓存使用重点
大促中缓存使用场景比较多,需要预防缓存使用中问题:
缓存穿透问题:
一个根本不存在的请求过来时,查询mysql或者接口发现不存在,正常情况下是不进行缓存,直接返回空,但是这样如果是恶意请求,会每次都请求mysql或者接口。这种方式可以通过为该请求设置一个key(过期时间可以设置长一点),值为一个特殊标记的值,如果下次请求过来,发现key的value是该特殊标记值,则直接返回;
热key问题:
对热key进行hash一定数量的拷贝,例如key-0到key-24,至于hash的数量可以配置在zookeeper中,动态调整copy数量,已应对大并发情况;
key的缓存过期问题:
这个问题需要和热key一起考虑,进行热key缓存时,进行了多份copy,为了防止一个key的所有copy一起失效,可以考虑为每个copy单独设置缓存过期时间,例如:key-0的过期时间为10,key-1的过期时间为11,key-20的过期时间为20,一次类推;
一次操作多个key(原子性问题):
在其他文章已经介绍过,使用lua脚本实现;
实际缓存使用中,我一般会设置一个主key,然后再把key拷贝24份,
当访问cache时,会先0到24取随机数A,拼接key-A,
如果key-A缓存存在则返回value,
如果不存在,则查询主key缓存,
如果主key缓存存在,则返回value,
如果主key缓存也不存在,则重新查询并且缓存;
这里有个问题,为什么查询key-A不存在后,会再查询主key?
因为在缓存一个key时,会先缓存主key(最短过期时间),再缓存copy的其他key(过期时间一次递增一秒,使用pipline方式),查询key-A不存在时,再查询主key可以防止A请求来时发现缓存都过期了,并且A正在查询缓存key(优先缓存主key),这是B请求过来也发现缓存过期了,但是A已经把主key缓存成功了,如果这时B再重新查询一遍主key,就可以拿到A缓存的值了;
另一种更好的方式就是当重新查询缓存时设置一个锁,防止多个请求一起进行缓存,比如A正在进行缓存(设置了并发Cache-A),B请求发现Cache-A已经被设置,则进行循环等待3次,再重新查询缓存。;