本地缓存的实现以及遇到的问题
2017-02-28 09:24
302 查看
科技优家 2017-02-27 16:48
先分享下我基于MAP实现的一个本地缓存
本地缓存
优势:
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方法实现
踩过的坑:
解决:
1,不用失效时间来触发缓存的更新
1, 后台定时刷新最新内容到本地缓存,不依靠失效时间来触发。
2, 结合广播通知模式(如 redis)+本地缓存更新进行更新缓存,而不是通过失效来触发(目前系统主要就是这个模式,待加上案例分享)
当然,两种进行结合效果更好,
WEB服务器不停监控redis的访问,同时定时轮询,覆盖缓存中的内容
2,通过控制进入DB操作的线程数进行控制
如, 通过重入锁的,tryLock的condition,condition,阻塞超时方法,通知等进行控制(待加上案例分享)
解决:
1, 添加计数器,如当一个KEY的次数达到了10次后, 在缓存总加入该KEY,进行null的返回
2, 是否符合KEY的规则 + Bloom Filter, 用redis的bitmap存数组,对已存在的值进行hash存入(如果是ID,直接存,不需要hash,准确率100%)。 如果访问的有bit位置为0的,必定不存在
解决:
如果需要修改,返回对象需要进行深度clone
欢迎关注我的公众号,重现线上各种BUG, 一起来构建我们的知识体系
先分享下我基于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, 一起来构建我们的知识体系
相关文章推荐
- 本地缓存的实现以及遇到的问题
- 1, 本地缓存的实现以及遇到的问题
- Retrofit+OKHttp实现缓存以及遇到的问题
- 在Ubuntu下实现本地套接字(socket)通信以及遇到的问题!
- Servlet实现文件下载以及遇到的问题
- 关于采用HashMap作为本地缓存遇到的问题
- Retrofit2.0+okhttp3缓存机制以及遇到的问题
- C# asp.net 搭建微信公众平台(可实现关注消息与消息自动回复)的代码以及我所遇到的问题
- RecyclerView实现瀑布流遇到的各种问题(item移动,加载更多图片闪烁,以及定制各种类型Header和Footer)
- https,https的本地测试环境搭建,asp.net结合https的代码实现,http网站转换成https网站之后遇到的问题
- 使用Bootstrap Tabs选项卡Ajax加载数据的实现以及遇到的问题;
- 使用Spring Data Redis实现缓存遇到的一些问题
- Java 实现Excel表数据的读取和写入 以及过程中可能遇到的问题
- iOS QQ实现第三方登录以及遇到的问题
- 【转】https,https的本地测试环境搭建,asp.net结合https的代码实现,http网站转换成https网站之后遇到的问题
- ashx实现ajax功能遇到的浏览器缓存问题
- https,https的本地测试环境搭建,asp.net结合https的代码实现,http网站转换成https网站之后遇到的问题
- (C语言版)栈和队列(一)——实现链式栈和链式队列的基本操作以及遇到的问题
- https,https的本地测试环境搭建,asp.net结合https的代码实现,http网站转换成https网站之后遇到的问题
- iOS QQ实现第三方登录以及遇到的问题