一、缓存穿透

原理:用户的大量请求未命中到缓存,如客户端发送了一个数据库没有的请求给缓存,缓存发现没有该key对应的数据(数据库没有缓存肯定不会有)即命令发送到数据库请求获取,数据库也没有key对应的数据即返回空,但是这样就不会存到缓存中。导致每次该请求都会到达数据库进行无用操作,缓存就形同虚设(穿过缓存到数据库请求,缓存命中率问题)。

发生原因:业务代码问题(如没调缓存或缓存设置有问题)、恶意攻击(如故意用不存在的key请求接口)

优化:

  1. 缓存空对象:即不管请求的数据返回了什么,哪怕是空,也存储到缓存(设置个默认值,也缓存起来)。这样就会导致更多的键放到了缓存,包括不存在的乱七八糟的key(bigkey),通过设置过期时间降低风险。也有可能硬盘在某个瞬间有问题导致请求访问出错,缓存这时候保存了个null,导致缓存和存储层数据不一致。
  2. 使用布隆过滤器拦截(很小内存实现对数据的过滤):服务开始时先将所以key以一种算法使数据以最简的方式加入到布隆过滤器并预热,在每次请求发生时都会先查看该请求对应的key是否存在,不存在直接返回null而不进行其他请求(将所有可能存在的数据哈希到一个足够大的bitmap中,一个一定不存在的数据会被这个bitmap拦截掉)。对于相对固定的数据比较实用。

二、缓存更新策略

缓存更新策略(缓存中数据过期时机,直接影响到缓存数据与数据库数据的一致性)有如下三种:

  1. LRU、LFU、FIFO算法移除:设置当缓存达到最大内存之后才进行删除,该策略一致性最差但维护成本低。其中FIFO算法:先进先出,即先缓存,先被淘汰。LFU算法,把最不经常使用的缓存淘汰。LRU算法,把近期最少使用的缓存淘汰。
  2. 超时移除:给缓存中每个key设置超时时间,超过时间即删除,该策略一致性较差但维护成本低
  3. 主动更新:在数据库数据发生变化时主动让缓存进行更新,例如订阅模式,数据库发生修改主动提醒缓存层修改数据。该策略一致性高但维护成本也高

建议:一般生产环境一致性要求较高,所以使用超时移除和主动更新结合。

三、缓存粒度控制

定义:添加缓存层之后会将数据库获取到的数据缓存到redis中,而存储到redis中的数据量就是缓存粒度问题。如数据库中查出很多数据,但是只有部分数据会使用,是否将数据库中的全部属性添加到缓存,直接影响存储量。从通用角度考虑(如后期业务扩展)肯定全部存储较好,但是从空间占用角度考虑,还是存储有用数据比较好。需要结合实际应用场景考虑。

四、无底洞问题

定义:当集群机器数量添加到一定数量之后会发现再加机器,集群的性能没提升反而下降,即更多机器并不意味着更高的性能,可能会因为需要更多时间进行节点通信、客户端使用批处理命令需要的时间更长(请求次数增加)等因素照成性能下降。

优化:

  1. 尽量少用慢查询的命令及查询所有(all)等的命令
  2. 尽量减少网络通信次数,即客户端发到服务端的请求次数

五、缓存雪崩问题及优化

定义:正常情况下redis服务承载着大量的请求,到达存储层的请求比较少。当redis服务异常/宕机,或者大批原有缓存失效而未有新缓存到达(如把缓存都设置了相同的过期时间导致同一时刻大量缓存过期),这时候如果有大量请求过来都直接打到存储层,压向后端组件(mysql),对存储层造成巨大压力,极易造成其宕机等,因为其一般无法承受太大压力。

优化:

  1. 保证缓存高可用,保证一台宕机缓存仍能正常使用
  2. 可以通过给数据库的读写操作加锁或者队列方式保证同一时间不会有大量并发读写数据库的请求线程造成雪崩现象(限流)。但是这种优化在高并发下,会导致线程阻塞而用户等待超时。另外在分布式环境下要注意分布式锁的问题,高并发下不建议使用。
  3. 给每个缓存的固定失效时间加个随机数,这样就会降低每个缓存的过期时间重复率,即将缓存失效时间分散开,缓存大批失效的概率就降低了。
  4. 提前演练:压力测试等
  5. 使用缓存标记及缓存数据,缓存标记用于记录数据是否过期,过期则让后台线程更新对应缓存,而缓存的数据会比缓存标记时间长,即缓存标记在认为某个key过期时,会让后台进程去更新该key对应缓存,但这个时候缓存数据其实还没过期,请求过来时会返回久的缓存数据给请求,当后台线程更新完之后的请求才会返回新的缓存数据。

代码示例:

(1)加锁操作代码:

public object GetProductListNew() {int cacheTime = 30;String cacheKey = "product_list";String lockKey = cacheKey;String cacheValue = CacheHelper.get(cacheKey);  //到缓存取数据if (cacheValue != null) {return cacheValue;} else {synchronized(lockKey) {cacheValue = CacheHelper.get(cacheKey);   //再到缓存获取数据,有可能其他线程更新了if (cacheValue != null) {return cacheValue;} else {cacheValue = GetProductListFromDB();   //到sql查找数据CacheHelper.Add(cacheKey, cacheValue, cacheTime);}}return cacheValue;}
}

(2)给每一个缓存数据增加相应的缓存标记,记录缓存是否失效,如果缓存标记失效,则更新数据缓存:

public object GetProductListNew() {int cacheTime = 30;String cacheKey = "product_list";//缓存标记String cacheSign = cacheKey + "_sign";String sign = CacheHelper.Get(cacheSign);  //查看缓存标记是否失效String cacheValue = CacheHelper.Get(cacheKey);   //获取缓存数据if (sign != null) {return cacheValue; //未过期,直接返回} else {CacheHelper.Add(cacheSign, "1", cacheTime);ThreadPool.QueueUserWorkItem((arg) -> {//这里一般是 sql查询数据cacheValue = GetProductListFromDB(); //日期设缓存时间的2倍,用于脏读CacheHelper.Add(cacheKey, cacheValue, cacheTime * 2);                 });return cacheValue;}
} 

六、缓存预热

定义:在系统上线或使用人数较少时先将数据加载到缓存中,这样就不会造成开始时用户请求全部都会到数据库查询的问题。

操作:

  1. 当数据量较小时项目启动自动加载
  2. 定义一个缓存操作,进行手动操作
  3. 夜间定时将数据缓存

七、缓存降级

定义:由于系统访问量剧增导致服务出现问题(响应慢等),这时候应尽量舍弃非核心(可以丢弃)服务而保证核心服务可用,即对非核心服务进行降级操作。可以让程序实施自动降级,也可以人工紧急降级。另外服务一般都会进行分级,对于不同的异常情况对不同的服务进行降级。

八、热点key重建

定义:(基本同雪崩)对于热点key(访问量较大的key)失效时,有对应的请求则需要到缓存层重新获取,因为热点key访问的请求较多,同一时间由于高并发可能会有多个线程同时到缓存查数据发现没有,即到存储层进行查找并进行对key的缓存进行重建,就会出现同时有多个线程到数据库进行查找该key,对缓存层造成压力。另外数据库操作时间久的key也可能会出现这种问题。

解决:

  1. 互斥锁:  在一个线程发现缓存需要重建时,加锁,完成重建后释放锁。在这个过程中有其他线程发现缓存没数据,要进行重建时发现线程被锁住了,则进入等待状态,直到锁解开再获取数据。这种方式优点是思路简单,保证一致性。缺点为代码复杂,存在死锁风险,且会存在大量线程等待问题。
  2. 热点key永不过期:在缓存层面,即redis上不为key设置过期时间(expire),但是在功能层面上为每个value设置逻辑过期时间,即在缓存的时候顺便为对应value添加一个逻辑过期时间属性,在每次get到value时会顺便拿出逻辑过期时间,判断到逻辑过期时间过期,会用单独的线程为其作缓存的重建。但是会存在数据不一致问题,因为每次拿到缓存就使用,在发现逻辑时间过期会掉重建,但是输出的依然是旧值,此时如果重建时间久,接下来的请求依然会使用久的值知道重建完成,容易曹诚数据不一致。重建过程一样添加互斥锁,相对上面好就好在重建过程依然可以在缓存获取到数据而不用进行等待。该方式优点是杜绝热点key重建 。 缺点是不保证一致性,逻辑过期时间属性增加了维护成本和内存成本。

互斥锁:

String get(String key){String value=redis.get(key);if(value==null){String mutexKey=”mutex:key:”+key;if(redis.set(mutexKey,”1”,”ex 180”,”nx”)){value=db.get(key);redis.set(key,value);redis.delete(mutexKey);}else{//其他线程休息50ms后重试Thread.sleep(50);get(key);}}return value;
}

永不过期:

String get(String key){V v=redis.get(key);String value = v.getValue();long logicTimeout = v.getLogicTimeout();if(logicTimeout>=System.currentTimeMillis()){String mutexKey=”mutex:key:”+key;if(redis.set(mutexKey,”1”,”ex 180”,”nx”)){//异步更新后台异常执行threadPool.execute(new Runnable(){public void run(){String dbValue=db.get(key);redis.set(key,(dbValue,newLogicTimeout));redis.delete(keyMutex);}});}}return value;
}

加入缓存存在的问题及优化相关推荐

  1. 什么是 CDN 缓存命中率以及如何计算和优化它?

    新钛云服已累计为您分享694篇技术干货 本文主要关注 Amazon CloudFront CDN 缓存以及如何使用它们来实现更好的缓存命中率. 在了解缓存中的命中率和未命中率之前,最好先了解缓存是什么 ...

  2. 【前端部署第四篇】使用 Docker 构建缓存及多阶段构建优化单页应用

    大家好,我是山月,这是我最近新开的专栏:前端部署系列.包括 Docker.CICD 等内容,大纲图示如下: 示例代码开源,置于 Github 中,演示如何对真实项目进行部署上线. simple-dep ...

  3. 移动端视频缓存保障与CDN调度优化

    本文由网易云信资深音视频客户端工程师张根宁在LiveVideoStackCon 2019上海音视频技术大会的演讲整理而成,张根宁分享了团队在线视频播放器优化的主要方向,即缓冲和卡顿问题.对于卡顿,可以 ...

  4. 高性能编程:三级缓存(LLC)访问优化

    作者:ciuwaalu,腾讯 TEG 后台开发工程师 AMD 服务器,多线程应用绑核,选取不同的 CPU 核,性能差距可达50%. 最近有幸因项目拿到一台 AMD EPYC 系列测试服务器,发现了一些 ...

  5. linux下缓存命中测试,Linux 性能优化实战(倪朋飞)---查看缓存命中情况

    cachestat 提供了整个操作系统缓存的读写命中情况. cachetop 提供了每个进程的缓存命中情况.但是,cachetop 并不把直接 I/O 算进来. 安装 cachestat.cachet ...

  6. 缓存问题引发的一系列优化

    具体场景是这样的,生产环境的缓存采用oscache,配置了永久缓存,最大缓存数是30000.缓存集群采用Jgroup组播,数据变更(主要是update)成功后由该同步节点通知到其他所有节点,有20多个 ...

  7. django 中QuerySet特性,支持切片,索引,可迭代(缓存机制)iterator性能优化

    Book表的数据显示 id title price publish_id 2 Linux 30 1 3 项塔兰 45 2 4 追风筝的人 39.9 3 5 富爸爸 23 10 创建queryset 视 ...

  8. 2022华为杯C题汽车制造涂装-总装缓存调序区调度优化问题建模解决

    一.背景介绍 汽车制造厂主要由焊装车间.涂装车间.总装车间构成,每个车间有不同的生产偏好,如:焊装车间由于车身夹具的限制偏向最小车型及配置切换生产,涂装车间由于喷漆(固定每5辆车清洗喷头.颜色切换也需 ...

  9. 【Hibernate框架开发之九】Hibernate 性能优化笔记!(遍历、一级/二级/查询/缓存/乐观悲观锁等优化算法)...

    1.   循环分页或者循环进行部分读取处理数据的时候,使用 session.clear() ; 2.    对应1+N(N+1)问题使用如下解决方式: 1): 使用createCriteria进行查询 ...

最新文章

  1. 在JLabel上显示图片,并且图片自适应JLabel的大小
  2. 海思3531D上编译FFmpeg源码操作步骤
  3. 怎么用计算机计算出选手最后得分,计算选手最后得分(限制分的值,且最后按n或N键结束,其他键继续)...
  4. 录制失败因为媒体服务失败_具惠善减肥失败,竟然是因为它
  5. java xfire下载_XFIRE教程 PDF 下载
  6. 借助树的概率dp(期望)+数学-好题-hdu-4035-Maze
  7. mysql中的增删改查
  8. Windows Embedded CE 6.0开发初体验(三)设置Boot-loader
  9. html 解析 qt,windows下用QTwebkit解析html
  10. macbook air可以运行c语言吗,新 MacBook Air 现场上手:它让 MacBook 变得有点尴尬了...
  11. opencv-api boxPoints
  12. CF1041F Ray in the tube构造_思维
  13. 欧姆龙CP/CJ系列PLC包含哪些通讯方式呢?
  14. Spring源码解析系列汇总
  15. insgram 网页分享_instagram网页版登陆使用图文教程
  16. 关于 IE 浏览器打开时速度过慢的问题
  17. Selenium Gird下文件上传问题的解决(WebUI自动化测试)
  18. eBay自养号测评需要准备哪些资料?
  19. 计算机组成与系统结构期末复习 第二章 2.2
  20. 人工智能--技术发展史

热门文章

  1. 「深度学习之优化算法」(六)人工蜂群算法
  2. hades 冥府伴偶
  3. 【分布计算环境笔记】10 SOA、网格计算、云计算与P2P技术
  4. 安装elasticsearch及中文IK和近义词配置
  5. LeetCode 42. Trapping Rain Water(收集雨水Ⅰ)
  6. 小程序生成海报插件painter(原生小程序版)
  7. Linux环境下安装Python解释器--生活有感
  8. css中设置鼠标形状的函数,css怎样设置鼠标的形状
  9. java 死锁 活锁_Java之死锁/活锁
  10. 闰年python_“闰土”的真实结局:晚年被鲁迅家辞退,因太穷没钱看病而死