缓存中常见的问题及解决方案
2017-11-10 14:23
429 查看
缓存技术是网站服务端经常用到的一种技术,在读多写少的业务场景中,通过使用缓存可以有效地提高网站的性能,支撑高并发的访问量,对数据库做到很好的保护。我们在使用缓存的时候,如Redis、Memcached,基本上都遇到以下几个问题:缓存穿透、缓存并发、缓存失效(缓存雪崩)、热点key、DB缓存一致性。
本案例结合平台开发过程中遇到的问题,简要总结了缓存中常见的问题并提供一些解决方案。
图一、查询缓存
图二、删除缓存
图三、更新缓存
在项目中使用缓存通常都是先检查缓存中是否存在,如果存在直接返回缓存内容,如果不存在就查询数据库,然后再缓存查询结果返回。这个时候如果我们查询的某一个数据在缓存中一直不存在,就会造成每一次请求都查询DB,这样缓存就失去了意义,在流量很大时就会对DB造成一定的压力。这就是缓存穿透。如果有人利用不存在的key频繁攻击我们的应用,很可能直接对DB造成影响,这是在网站设计时需要考虑的问题。
有一个比较巧妙的做法是,将这个不存在的key预先设定一个值及过期时间,比如”key”-“null”等。当返回null值的时候,我们就认为这是一个不存在的key,然后决定是否继续等待访问取值,或者放弃操作。如果继续等待访问,过一个时间轮询点后再次请求这个key,如果取到的值不是”null”,则认为这时key有值了,从而避免了透传到数据库,把大量的类似请求挡在了缓存之中。
在我们搭建的平台中,有些地方需要特别注意防范缓存穿透问题,比如用户登录操作、找回密码操作等。我们可以分两步处理,在用户发起请求的时候,首先对请求内容的格式做校验,将非法内容的请求直接阻挡在前端或者服务层面;其次,在缓存中对频繁查询的字段做校验。比如最常用到的手机号,首先过滤非法手机号,然后再查询数据库,如果手机号不存在,则将该手机号放入缓存中,下次请求可直接从缓存中读取结果,减少数据库的压力。如果后续将手机号添加到了平台,同步更新对应的key即可。
处理方案是对缓存查询加锁,如果KEY不存在则加锁,查询DB放入缓存,然后解锁;其他进程如果发现有锁就等待,等解锁后查询数据或者进入DB查询。这种情况和刚才说的预先设定值问题有些类似,不过利用锁的方式,会造成部分请求等待。
关于此问题,方案之一就是将缓存失效时间均匀的分散开,防止同一时间点大量的缓存key失效。比如我们可以在原有的失效时间基础上增加一个随机值,比如1-5分钟随机,这样每一个缓存的过期时间的重复率就会降低,就很难引发集体失效的事件。
方案一:客户端热点key缓存:将热点key对应的value缓存在本地客户端,并设置一个失效时间。每次读取时,首先检查本地缓存中是否存在key,如果存在则直接返回,不存在再去访问分布式缓存集群。
方案二:将热点key分散为多个子key,存储到缓存集群上的不同机器上。这些子key对应的value都和热点key一样,当通过热点key去查询数据库时,通过某种hash算法随机选择一个子key,然后再去访问缓存机器。
由于操作缓存与操作数据库不是原子的,中间某一过程非常有可能执行失败。假设先写数据库,再淘汰缓存:第一步写数据库操作成功,第二步淘汰缓存失败,则会出现DB中是新数据,Cache中是旧数据,两方数据不一致。
图四、db中是新数据,cache中是旧数据
假设先淘汰缓存,再写数据库:第一步淘汰缓存成功,第二步写数据库失败,则只会引发一次Cache miss,但是不存脏数据及缓存不一致的现象。
图五、cache中无数据,db中是旧数据
所以我们应该选用:先淘汰缓存,再写数据库。
多级缓存也会存在一致性问题,如果我们需要强一致性的话,缓存与数据库同步是会存在时间差的,所以在具体开发的过程中,要根据场景来具体分析,二级缓存需要解决是缓存穿透与程序的健壮性,保证当集中式缓存出现问题的时候,我们的平台能继续运行。
注:缓存并发是针对同一个缓存来讲,缓存失效是针对很多缓存而言。
本案例结合平台开发过程中遇到的问题,简要总结了缓存中常见的问题并提供一些解决方案。
案例介绍
缓存穿透:
在项目开发中,我们常会遇到以下三种情形:图一、查询缓存
图二、删除缓存
图三、更新缓存
在项目中使用缓存通常都是先检查缓存中是否存在,如果存在直接返回缓存内容,如果不存在就查询数据库,然后再缓存查询结果返回。这个时候如果我们查询的某一个数据在缓存中一直不存在,就会造成每一次请求都查询DB,这样缓存就失去了意义,在流量很大时就会对DB造成一定的压力。这就是缓存穿透。如果有人利用不存在的key频繁攻击我们的应用,很可能直接对DB造成影响,这是在网站设计时需要考虑的问题。
有一个比较巧妙的做法是,将这个不存在的key预先设定一个值及过期时间,比如”key”-“null”等。当返回null值的时候,我们就认为这是一个不存在的key,然后决定是否继续等待访问取值,或者放弃操作。如果继续等待访问,过一个时间轮询点后再次请求这个key,如果取到的值不是”null”,则认为这时key有值了,从而避免了透传到数据库,把大量的类似请求挡在了缓存之中。
在我们搭建的平台中,有些地方需要特别注意防范缓存穿透问题,比如用户登录操作、找回密码操作等。我们可以分两步处理,在用户发起请求的时候,首先对请求内容的格式做校验,将非法内容的请求直接阻挡在前端或者服务层面;其次,在缓存中对频繁查询的字段做校验。比如最常用到的手机号,首先过滤非法手机号,然后再查询数据库,如果手机号不存在,则将该手机号放入缓存中,下次请求可直接从缓存中读取结果,减少数据库的压力。如果后续将手机号添加到了平台,同步更新对应的key即可。
缓存并发
有时候如果平台并发访问高,一个缓存失效了,可能出现多个进程同时查询DB,造成DB压力过大,还有缓存频繁更新的问题。处理方案是对缓存查询加锁,如果KEY不存在则加锁,查询DB放入缓存,然后解锁;其他进程如果发现有锁就等待,等解锁后查询数据或者进入DB查询。这种情况和刚才说的预先设定值问题有些类似,不过利用锁的方式,会造成部分请求等待。
缓存失效
引起这个问题的主要原因也是高并发,平时我们可能将缓存设置一个过期时间(比如1分钟或5分钟),当并发很高时可能在某一个时间点同时生成很多缓存key,并且过期时间都一样,这个时候就可能引发在过期时间点到达时,缓存同时失效,请求全部转发到DB,DB就会有很大的压力,造成缓存雪崩。关于此问题,方案之一就是将缓存失效时间均匀的分散开,防止同一时间点大量的缓存key失效。比如我们可以在原有的失效时间基础上增加一个随机值,比如1-5分钟随机,这样每一个缓存的过期时间的重复率就会降低,就很难引发集体失效的事件。
热点key
缓存中的某些key(比如某个促销商品或热点事件),对应value存储在集群中的一台机器,使得所有流量涌向同一机器,这样就形成系统的一个瓶颈。该问题的挑战在于它无法通过增加机器容量来解决。方案一:客户端热点key缓存:将热点key对应的value缓存在本地客户端,并设置一个失效时间。每次读取时,首先检查本地缓存中是否存在key,如果存在则直接返回,不存在再去访问分布式缓存集群。
方案二:将热点key分散为多个子key,存储到缓存集群上的不同机器上。这些子key对应的value都和热点key一样,当通过热点key去查询数据库时,通过某种hash算法随机选择一个子key,然后再去访问缓存机器。
DB和缓存一致性问题
如上图二、图三是对更新数据的两种不同操作,一种删除缓存(数据只写入数据库,不写入缓存)、一种更新缓存(数据不仅写入数据库,还要写入缓存),应该选用哪种更新方案呢,这里需要讨论下DB和缓存不一致的问题。由于操作缓存与操作数据库不是原子的,中间某一过程非常有可能执行失败。假设先写数据库,再淘汰缓存:第一步写数据库操作成功,第二步淘汰缓存失败,则会出现DB中是新数据,Cache中是旧数据,两方数据不一致。
图四、db中是新数据,cache中是旧数据
假设先淘汰缓存,再写数据库:第一步淘汰缓存成功,第二步写数据库失败,则只会引发一次Cache miss,但是不存脏数据及缓存不一致的现象。
图五、cache中无数据,db中是旧数据
所以我们应该选用:先淘汰缓存,再写数据库。
多级缓存也会存在一致性问题,如果我们需要强一致性的话,缓存与数据库同步是会存在时间差的,所以在具体开发的过程中,要根据场景来具体分析,二级缓存需要解决是缓存穿透与程序的健壮性,保证当集中式缓存出现问题的时候,我们的平台能继续运行。
注:缓存并发是针对同一个缓存来讲,缓存失效是针对很多缓存而言。
总结
一个好的项目不仅需要实现客户提出的功能需求,还需在性能、稳定性等方面花大量功夫进行测试,才能保证项目在现场环境较少的出现问题,也减少以后维护项目的成本。本案例总结了在使用缓存技术时经常遇到的几个问题,并提供了相应的解决方案,可供其它用到类似技术的同事学习借鉴。相关文章推荐
- 缓存中常见的4种问题分析以及解决方案
- 【缓存】缓存中常见的4种问题分析以及解决方案
- Storm实战常见问题及解决方案
- Android Studio常见项目问题及解决方案
- 常见浏览器兼容问题与解决方案
- VS 2008和.NET 3.5 Beta2常见问题的解决方案
- 报表常见问题(二)ireport引入到项目无法解析字体及pdf无法显示中文的解决方案
- 实战Memcached缓存系统(8)Memcached异步实时读写问题的解决方案SAC
- Android WebView常见问题及解决方案汇总
- Hadoop YARN常见问题以及解决方案
- ionic开发常见问题及解决方案(四)
- 【易错】一级缓存、二级缓存介绍与常见问题(四)
- Android AVD两个常见问题解决方案
- 浏览器缓存导致FLASH资源更新问题的解决方案
- net-snmp开发代理常见问题解决方案
- 常见浏览器兼容性问题与解决方案(下)
- Java 创建URL的常见问题及解决方案
- Android WebView常见问题及解决方案汇总
- 常见的浏览器兼容性问题与解决方案——CSS篇
- Storm实战常见问题及解决方案