开发问题解决方案
时间和时间戳的应用场景
Netty的ByteBuf基础知识
JVM生产环境调优工具
i18n注意事项
导致缓存击穿的代码示例
CPU高的问题排查方法
服务器性能监控
复费率读写接口标准
本文档使用 MrDoc 发布
-
+
首页
导致缓存击穿的代码示例
缓存穿透指的是大量的请求越过缓存,直接命中数据库,导致数据库压力骤增,可能达到I/O瓶颈,出现数据库宕机。 下面举个例子,假如历史数据存储前,需要为每一条数据附加一个projectId: - 下等码 这种方法有很明显的问题,比如有100个网关,下面接了10000块表,list里有1万条数据,要查1万次缓存。 ```java for (int i = 0; i < list.size(); i++) { HistoryData data = list.get(i); String sn = data.getGatewaySn(); // findBySnWithoutRelation方法中已经实现了redis缓存,当命中redis时,试用redis数据,未命中时,查询数据库,并将数据置入redis Device gateway = this.deviceService.findBySnWithoutRelation(sn, null); if (gateway != null && gateway.getProjectId() != null) { data.setProjectId(gateway.getProjectId()); } } ``` - 中等码 经过优化,增加了一个gatewayMap,用来缓存网关信息,这样看起来100个网关最多查100次缓存。 ——但实际上并不是这样。 ```java Map<String, Device> gatewayMap = new HashMap<>(); for (int i = 0; i < list.size(); i++) { HistoryData data = list.get(i); String sn = data.getGatewaySn(); Device gateway = null; if (!gatewayMap.containsKey(sn)) { gateway = this.deviceService.findBySnWithoutRelation(sn, null); // 这个地方存在缓存击穿问题 // 比如网关sn是001-100,每个网关上报了100条数据, // 如果sn=036这个网关在数据库不存在,那么查出来的gateway为null // 这时候 !gatewayMap.containsKey("036") 始终为true, // 就会执行100次 this.deviceService.findBySnWithoutRelation(sn, null) // 而redis缓存建立的时候,并不会为不存在的gateway建立缓存,于是这个查询会直接命中数据库 if (gateway != null) { gatewayMap.put(sn, gateway); } } else { gateway = gatewayMap.get(sn); } if (gateway != null) { data.setProjectId(gateway.getProjectId()); } } ``` - 上等码 此处用到的是2级缓存,请求 → 内存临时缓存 → redis持久化缓存 → 数据库。 因为内存缓存是最便宜的,我们应该尽量在这一层将不必要的请求都拦截,所以在此处应当为不存在的gateway建立缓存,避免不存在的数据查询请求越过redis命中数据库。 ```java Map<String, Device> gatewayMap = new HashMap<>(); // 遍历所有历史数据的时候,为其添加项目id for (int i = 0; i < list.size(); i++) { HistoryData data = list.get(i); String sn = data.getGatewaySn(); Device gateway = null; Device subDevice = null; if (!gatewayMap.containsKey(sn)) { gateway = this.deviceService.findBySnWithoutRelation(sn, null); // 很简单,这里把 if(gateway != null) 去掉就可以 // 后续报文处理时,可以从gatewayMap中查询到gateway,只不过为null,这样就不会命中redis或者数据库了 gatewayMap.put(sn, gateway); } else { gateway = gatewayMap.get(sn); } if (gateway != null) { data.setProjectId(gateway.getProjectId()); } } ``` - 总结 缓存穿透是指查询一个根本不存在的数据,缓存层和存储层都不会命中,通常出于容错的考虑,如果从存储层查不到数据则不写入缓存层。 缓存穿透将导致不存在的数据每次请求都要到存储层去查询,失去了缓存保护后端存储的意义。造成缓存穿透的基本原因有两个: - 自身业务代码或者数据出现问题。 - 一些恶意攻击、 爬虫等造成大量空命中。 缓存穿透问题解决方案通常是**缓存空对象**或**布隆过滤器**。 缓存空对象就是上面的案例,这个案例中,因为存在内存缓存,所以我们就不需要在redis中建立空缓存,但是换一个角度来看,如果我们这个处理数据的逻辑里面,并不能一次获取一批数据,而是一次传入一条数据,那么建立内存缓存就不可能了,这时就要考虑在redis中建立空缓存,来避免缓存击穿。
admin
2024年2月24日 12:51
转发文档
收藏文档
上一篇
下一篇
手机扫码
复制链接
手机扫一扫转发分享
复制链接
Markdown文件
分享
链接
类型
密码
更新密码