本地缓存的实现以及遇到的问题
来源:互联网 发布:怎么打造淘宝爆款 编辑:程序博客网 时间:2024/05/20 02:51
先分享下我基于MAP实现的一个本地缓存
package org.hjb.component;import java.lang.ref.SoftReference;import java.util.Map;import java.util.concurrent.ConcurrentHashMap;import java.util.concurrent.TimeUnit;import org.apache.logging.log4j.LogManager;import org.apache.logging.log4j.Logger;/** * 本地缓存 * * 何锦彬 2017.02.24 */public class LocalMemory { // 数据 static class CacheData { // 过期时间 private Long invalidTime; private Object data; public Long getInvalidTime { return invalidTime; } public void setInvalidTime(Long invalidTime) { this.invalidTime = invalidTime; } public Object getData { return data; } public void setData(Object data) { this.data = data; } } private static Logger logger = LogManager.getLogger(LocalMemory.class); // 存储本地缓存数据.用软引用避免OutOfMemoryError static Map<String, SoftReference<CacheData>> localData = new ConcurrentHashMap<String, SoftReference<CacheData>>; public static final int MAX_SIZE = 10000; public static final int WARN_VALUE = 8000; /** * @param key * 缓存KEY * @param value * 缓存数据 * @param timeOut * 超时时间,单位秒 */ public static void put(String key, Object value, Long timeOut) { if (localData.size >= WARN_VALUE) { logger.warn("注意:本地缓存已经达到临界值,size:" + localData.size); } if (localData.size > MAX_SIZE) { logger.error("超出最大值:" + localData.size); return; } CacheData cacheData = new CacheData; long now = System.currentTimeMillis; long invalidTime = now + (timeOut * 1000); cacheData.setData(value); cacheData.setInvalidTime(invalidTime); SoftReference<CacheData> refCacheData = new SoftReference<CacheData>(cacheData); localData.put(key, refCacheData); } public static final Object get(String key) { SoftReference<CacheData> referenceData = localData.get(key); if (referenceData == null) { logger.debug("未找到数据,key => {}", key); } CacheData cacheData = localData.get(key).get; if (cacheData == null) { logger.debug("未找到数据,key => {}", key); } Long invalidTime = cacheData.getInvalidTime; if (invalidTime == null) { return null; } long now = System.currentTimeMillis; if (now > invalidTime) { // 清除缓存 localData.remove(key); return null; } return cacheData.getData; } public static void put(String key, Object value, long time, TimeUnit unit) { put(key, value, unit.toSeconds(time)); } public static void main(String[] args) throws InterruptedException { String key = "test"; Object value = "hello world"; LocalMemory.put("test", value, 1l); System.out.println(LocalMemory.get(key)); Thread.sleep(2000); System.out.println(LocalMemory.get(key)); }}
本地缓存
优势:
1,易用,只是比map多了个过期时间,有超时的概念
2,用软引用,可防止对JVM的堆对象造成out memory
3, 相对集中缓存不需要进行网络开销,消除RPC
缺点:
1,用的是堆内存。会对JVM的垃圾回收造成影响
2,大小控制只能是通过KEY值的存储数量控制,无法通过控制内存占用大小
3,缺少监控方面的设计
4,没有缓存的移除,定期清除失效缓存
5,缓存穿透的问题,当缓存失效时间时,大量访问到了缓存的传统,压到数据库去了
对于3,4问题可以用google的guava
对于1,ehcache可以用JAVA的直接内存.
对于直接内存这部分不好实现,JAVA只提供了个ByteBuffer.allocateDirect(capacity)的方法去应用直接内存,也就意味着要存入直接内存必须先把整个对象序列号成byte再放入直接内存。
但这样每次都需要序列号与反序列化的开销,而且得全量加载的堆内存引起垃圾回收。ehcache有直接用native方法实现
踩过的坑:
缓存失效
当缓存出现失效, 瞬间大量访问压到了DB,造成DB的压力
解决:
1,不用失效时间来触发缓存的更新
1, 后台定时刷新最新内容到本地缓存,不依靠失效时间来触发。
2, 结合广播通知模式(如 redis)+本地缓存更新进行更新缓存,而不是通过失效来触发(目前系统主要就是这个模式,待加上案例分享)
当然,两种进行结合效果更好,
WEB服务器不停监控redis的访问,同时定时轮询,覆盖缓存中的内容
2,通过控制进入DB操作的线程数进行控制
如, 通过重入锁的,tryLock的condition,condition,阻塞超时方法,通知等进行控制(待加上案例分享)
缓存穿透
当访问不存在的KEY时,一直传入到数据库层面去,压到DB,造成DB的压
解决:
1, 添加计数器,如当一个KEY的次数达到了10次后, 在缓存总加入该KEY,进行null的返回
2, 是否符合KEY的规则 + Bloom Filter, 用redis的bitmap存数组,对已存在的值进行hash存入(如果是ID,直接存,不需要hash,准确率100%)。 如果访问的有bit位置为0的,必定不存在
返回同一对象地址
本地缓存读取后的修改,会相互影响的问题
解决:
如果需要修改,返回对象需要进行深度clone
欢迎关注我的公众号,重现线上各种BUG, 一起来构建我们的知识体系
- 本地缓存的实现以及遇到的问题
- Retrofit+OKHttp实现缓存以及遇到的问题
- 关于采用HashMap作为本地缓存遇到的问题
- 在Ubuntu下实现本地套接字(socket)通信以及遇到的问题!
- Retrofit2.0+okhttp3缓存机制以及遇到的问题
- Retrofit2.0+okhttp3缓存机制以及遇到的问题
- 本地缓存以及UIDocumentInteractionController的使用
- XListView的使用以及本地缓存
- Servlet实现文件下载以及遇到的问题
- hive 实现 udf row_number 以及遇到的问题
- Android 沉浸式状态栏实现,以及遇到的问题
- iOS QQ实现第三方登录以及遇到的问题
- iOS QQ实现第三方登录以及遇到的问题
- IBM服务器以及本地PC机安装suse linux,oracle测试,遇到的问题有:
- java sigar 获取本地信息代码实例,以及运行遇到的问题
- git 上传本地项目到github以及遇到的一些问题
- 上传本地仓库到github上时遇到的一些问题以及解决方法
- 有关于RTX本地缓存的问题
- ES6__异步开发优化
- 让delphi程序不受WINDOWS日期格式的影响
- 去除取消WPS的广告推送、WPS热点以及推荐软件等骚扰功能
- Spring源码解析
- Python与AI之一_入门
- 本地缓存的实现以及遇到的问题
- MySQL几个超时参数(timeout)解释
- wireshark解析rtp协议,流媒体中的AMR/H263/H264包的方法
- angularjs http请求设置头信息
- 计算机基础
- VS 加入静态库(.lib)
- svg可视化制作工具
- 让机器人更安全——(3.如何定义人机协作行为)
- 每天一个adb命令:monkey命令详解