缓存如何实现高性能
缓存如何实现高并发
redis和memcached的区别
- redis拥有更多的数据结构和丰富的数据操作
- redis内存利用率高于memcache
- redis是单线程,memcache是多线程,在存储大量数据的情况下,redis的速度比memcache差一些
memcache没有原生的集群模式,redis官方支持redis cluster集群模式
用缓存可能出现的问题
数据不一致
- 缓存雪崩
- 缓存穿透
- 缓存并发竞争
当查询缓存报错,如何提高可用性?
缓存可以极大提高查询性能,但是缓存数据丢失和缓存不可用不能影响应用的正常工作。因此,一般情况下,如果缓存出现异常,需要手动捕获这个异常,并且记录日志,然后从数据库查询数据返回给用户,而不是导致业务不可用如何避免缓存”穿透”的问题
缓存穿透,是指查询一个一定不存在的数据,由于缓存是不命中时被动写,并且出于容错考虑,如果从db查不到数据则不写入缓存,这个将导致这个不存在的数据每次都需要请求db查询,失去了缓存的意义。
如何解决:
方案一:缓存空对象
当从db查询数据为空时,我们仍然将这个空结果进行缓存,具体的值需要使用特数据的标识,能和真正缓存的数据区分开。另外,需要设置较短的过期时间,一般建议不要超过五分钟
方案二:bloomfilter 布隆过滤器
在缓存服务的基础上,构建BloomFilter数据结构,在bloomfilter中存储对应的key是否存在,如果存在,说明该key对应的值不为空如何避免缓存”雪崩”的问题*使用golang如何实现本地缓存?
缓存雪崩,是指缓存由于某些原因无法提供服务(如:缓存挂掉了),所有请求全部到达DB中,导致DB负荷大增,最终导致雪崩一样的挂掉。
如何解决:
方案一:缓存高可用
通过搭建缓存高可用,避免缓存挂掉无法提供服务的情况,从而降低出现缓存雪崩的情况。假设我们使用redis作为缓存,则可以使用redis sentinel或redis cluster实现高可用
方案二:本地缓存
如果使用本地缓存时,即使分布式缓存挂了,也可以将db查询的结果缓存到本地,避免后续请求全部到达DB中。*如何避免缓存”击穿”的问题
缓存击穿,是指某个极度”热点“数据在某个时间点过期时,恰好在这个时间对这个key有大量的并发请求过来,这些请求发现缓存过期一般都会从db加载数据并回设到缓存,但这个时候大量并发请求可能会瞬间压垮DB
如何解决:对于一些设置了过期时间的key,如果这些key可能会在某个时间点被高并发访问,是一种”热点“数据。这个时候,需要考虑这个问题:
方案一:使用互斥锁。请求发现缓存不存在后,去查询DB前,使用分布式锁,保证只有一个线程去查询DB,并更新到缓存。
方案二:手动过期,缓存上不设置过期时间,功能上将过期时间存在key对应的value中,方案如下
1.获取缓存。通过value的过期时间,判断是否过期。如果未过期,则直接返回;如果已过期,继续向下执行
2.通过一个后台的异步线程进行缓存的构建,也就是”手动“过期。通过后台的异步线程,保证有且只有一个线程访问db
3.同时,虽然value已经过期,但还是直接返回。通过这样的方式,保证服务的可用性,虽然损失了一些时效性
区别:
和缓存“穿透”的区别在于,这个key存在对应值
和缓存“雪崩”的区别在于,缓存雪崩针对某个一个key,而缓存击穿是很多个key什么是缓存预热,如何实现缓存预热
在刚启动的缓存系统中,如果缓存中没有任何数据,如果依靠用户请求的方式重建缓存数据,那么对数据库的压力非常大,而且系统的性能开销也是巨大。
此时,最好的策略是启动时就把热点数据加载好。这样,用户请求时,直接读取的就是缓存的数据,而无需去读取db重建缓存数据。举个例子,热门或推荐的商品,需要提前预热到缓存中。
如何实现:
数据量不大时,在项目启动的时候自动进行初始化。
数据量大时,写脚本或者在管理页面手动点击,预热对应的数据到缓存中。缓存数据的淘汰策略有哪些*自带了哪些失效策略
除了缓存服务器自带的缓存自动失效策略之外,我们还可以根据业务的需求进行自定义的“手动”缓存淘汰,常见的策略有两种:
1.定期去清理过期的缓存
2.当有用户请求过来时,再判断这个请求用到的缓存是否过期,过期的话,就去底层系统得到数据来更新缓存
第一种的缺点是维护大量缓存的key比较麻烦。
第二种的缺点是每次用户请求都要判断缓存是否失效,逻辑相对复杂
具体使用哪种方案,需要根据场景权衡。