文档章节

本地缓存的实现以及遇到的问题

爱吃大肉包
 爱吃大肉包
发布于 2017/02/27 16:45
字数 1090
阅读 114
收藏 1
点赞 0
评论 0

先分享下我基于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, 一起来构建我们的知识体系

 

 

© 著作权归作者所有

共有 人打赏支持
爱吃大肉包
粉丝 60
博文 29
码字总数 27564
作品 0
广州
程序员
微信小程序播放缓存的音频文件

很多时候我们都想把数据预先缓存到本地,节省带宽。但是最近在处理微信小程序播放缓存到本地的音频文件的时候,遇到一些小问题,然后对于安卓和IOS需要采用不同的播放策略。 首先,如果哪怕用...

yubang ⋅ 2017/12/30 ⋅ 0

Tachyon:Spark生态系统中的分布式内存文件系统

Tachyon是Spark生态系统内快速崛起的一个新项目。 本质上, Tachyon是个分布式的内存文件系统, 它在减轻Spark内存压力的同时,也赋予了Spark内存快速大量数据读写的能力。Tachyon把内存存储...

cloud-coder ⋅ 2015/07/07 ⋅ 0

微信小程序常见错误及基本排除方法

也接触了不少人解决问题的过程里,有一些比较难以排查的可能性列举在这,方便大家看一下: 也欢迎大家把自己常用的排除方法列举在这,一起互相学习探讨! 1:ES6;使用es6可能导致安卓端真机...

qianmenchuxue ⋅ 2016/12/20 ⋅ 0

Linux实现的ARP缓存老化时间原理解析

一.问题众所周知,ARP是一个链路层的地址解析协议,它以IP地址为键值,查询保有该IP地址主机的MAC地址。协议的详情就不详述了,你可以看RFC,也可以看教科书。这里写这么一篇文章,主要是为了...

晨曦之光 ⋅ 2012/04/10 ⋅ 0

PhoneGap 与 Application Cache应用缓存

一个想法 在研究 PhoneGap 的时候一直在想一个问题,怎么提高移动网络下的网络性能?特别是静态文件,多次加载完全是浪费。 HTTP 协议中控制缓存也比较纠结,总会有个 304 请求,而且各种协议...

Java编程思想 ⋅ 2014/07/24 ⋅ 0

xUtils框架介绍(二)

昨天对xUtils整体上做了一个简单的介绍,今天咱们就代码码起,真刀实枪的也看看,看看如何快速便捷的把xUtils给集成到大家的项目中去。xUtils中有四大组件可以供我们使用,分别是ViewUtils、...

Jack_1900 ⋅ 2013/11/01 ⋅ 8

亿级请求下多级缓存那些事

什么是多级缓存 所谓多级缓存,即在整个系统架构的不同系统层级进行数据缓存,以提升访问效率,这也是应用最广的方案之一。我们应用的整体架构如图1所示: 图1 多级缓存方案 整体流程如上图所...

技术小能手 ⋅ 01/03 ⋅ 0

Android快速开发框架:ThinkAndroid

ThinkAndroid是包含Android mvc和简易sqliteorm以及ioc模块,它封装了Android httpclitent中的http模块,具有快速构建文件缓存功能,无需考虑什么格式的文件,都可以非常轻松的实现缓存,它实...

丨小丶牧灬 ⋅ 2015/07/30 ⋅ 1

我眼中的dubbo

我眼中的DUBBO (微信地址:http://mp.weixin.qq.com/s?_biz=MzIwMTA2MjMxNA==&mid=211696554&idx=1&sn=6f66d1c31fd11f5deee2d4ddd840fcf2&scene=0#rd) 一. 前言 dubbo作为一款国内开源的优......

清风傲剑 ⋅ 2015/09/05 ⋅ 6

java应用中的本地缓存

java中的本地缓存,工作后陆续用到,一直想写,一直无从下手,最近又涉及到这方面的问题了,梳理了一下。自己构造单例、guava、ehcache基本上涵盖了目前的大多数行为了。 为什么要有本地缓存?...

疯狂的兔子 ⋅ 2014/08/05 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

Redis 单线程 为何却需要事务处理并发问题

Redis是单线程处理,也就是命令会顺序执行。那么为什么会存在并发问题呢? 个人理解是,虽然redis是单线程,但是可以同时有多个客户端访问,每个客户端会有 一个线程。客户端访问之间存在竞争...

码代码的小司机 ⋅ 56分钟前 ⋅ 0

到底会改名吗?微软GVFS 改名之争

微软去年透露了 Git Virtual File System(GVFS)项目,GVFS 是 Git 版本控制系统的一个开源插件,允许 Git 处理 TB 规模的代码库,比如 270 GB 的 Windows 代码库。该项目公布之初就引发了争...

linux-tao ⋅ 今天 ⋅ 0

笔试题之Java基础部分【简】【二】

1.静态变量和实例变量的区别 在语法定义上的区别:静态变量前要加static关键字,而实例变量前则不加。在程序运行时的区别:实例变量属于某个对象的属性,必须创建了实例对象,其中的实例变...

anlve ⋅ 今天 ⋅ 0

Lombok简单介绍及使用

官网 通过简单注解来精简代码达到消除冗长代码的目的 优点 提高编程效率 使代码更简洁 消除冗长代码 避免修改字段名字时忘记修改方法名 4.idea中安装lombnok pom.xml引入 <dependency> <grou...

to_ln ⋅ 今天 ⋅ 0

【转】JS浮点数运算Bug的解决办法

37.5*5.5=206.08 (JS算出来是这样的一个结果,我四舍五入取两位小数) 我先怀疑是四舍五入的问题,就直接用JS算了一个结果为:206.08499999999998 怎么会这样,两个只有一位小数的数字相乘,怎...

NickSoki ⋅ 今天 ⋅ 0

table eg

user_id user_name full_name 1 zhangsan 张三 2 lisi 李四 `` ™ [========] 2018-06-18 09:42:06 星期一½ gdsgagagagdsgasgagadsgdasgagsa...

qwfys ⋅ 今天 ⋅ 0

一个有趣的Java问题

先来看看源码: public class TestDemo { public static void main(String[] args) { Integer a = 10; Integer b = 20; swap(a, b); System.out......

linxyz ⋅ 今天 ⋅ 0

十五周二次课

十五周二次课 17.1mysql主从介绍 17.2准备工作 17.3配置主 17.4配置从 17.5测试主从同步 17.1mysql主从介绍 MySQL主从介绍 MySQL主从又叫做Replication、AB复制。简单讲就是A和B两台机器做主...

河图再现 ⋅ 今天 ⋅ 0

docker安装snmp rrdtool环境

以Ubuntu16:04作为基础版本 docker pull ubuntu:16.04 启动一个容器 docker run -d -i -t --name flow_mete ubuntu:16.04 bash 进入容器 docker exec -it flow_mete bash cd ~ 安装基本软件 ......

messud4312 ⋅ 今天 ⋅ 0

OSChina 周一乱弹 —— 快别开心了,你还没有女友呢。

Osc乱弹歌单(2018)请戳(这里) 【今日歌曲】 @莱布妮子 :分享吴彤的单曲《好春光》 《好春光》- 吴彤 手机党少年们想听歌,请使劲儿戳(这里) @clouddyy :小萝莉街上乱跑,误把我认错成...

小小编辑 ⋅ 今天 ⋅ 9

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部