java动态缓存成长小纪(一)——创建一个简单的缓存

在实际项目中,我们经常会需要使用到缓存。一般来说,缓存介质是内存;而常见的DB将数据存储在硬盘中的;缓存读取是电脉冲,而硬盘读取是机械地读取转动的硬盘,速度差异是上百倍的。所以往往可以通过缓存,对经常用到的数据进行访问,以提高速度。

创建缓存实际上就是针对两个对象,1. Cache对象,即一个缓存对象;2. CacheManager对象,即一个管理不同缓存的对象,其核心实际上就是一个Map,用来保存与获取不同缓存。

最简单的缓存 实现如下:

/**
 *  项目名称:
 *  文件说明:创建一个缓存管理器 <span style="font-family: Arial, Helvetica, sans-serif;">刘晨曦</span>
 *  主要特点:
 *  版本号:1.0
 *  创建时间:2013-12-3
 **/
package NBOffer;

import java.util.HashMap;

public class CacheManager {

	static HashMap<String,Cache> cacheMap=new HashMap<String,Cache>();

	public static Cache getCache(String id)
	{
		return cacheMap.get(id);
	}

	public static void putCache(Cache cache)
	{
		cacheMap.put(cache.id, cache);
	}

	public static void main(String[] args) {
		Cache cache1=new Cache("1","A1");
		Cache cache2=new Cache("2","A2");
		CacheManager.putCache(cache1);
		CacheManager.putCache(cache2);
		CacheManager.getCache("1").showInfo();
	}
}

class Cache
{
	String id;//相当于主键
	Object val;
	public Cache(String id)
	{
		new Cache(id,null);
	}

	public Cache(String id,Object val)
	{
		this.id=id;
		this.val=val;
	}

	public void setValue(Object val)
	{
		this.val=val;
	}

	public void showInfo()
	{
		System.out.println("Cache的ID是:   "+id+"   Cache的值是:   "+val);
	}
}

问题1: 哪一步能够体现出缓存相对于直接读取库的优点?

实际上是getCache()方法,是从HashMap中读取,也就是从JVM中读取,处于内存中。

问题2: 缓存若未能命中,岂不是返回空的Cache了?

是的,所以我们需要对getCache进行改进,未能命中说明缓存里面没有,需要从数据库里面取数。

/**
 *  项目名称:
 *  文件说明:创建一个缓存管理器 <span style="font-family: Arial, Helvetica, sans-serif;">刘晨曦</span>
 *  主要特点:
 *  版本号:1.0
 *  创建时间:2013-12-3
 **/
package NBOffer;

import java.util.HashMap;

public class CacheManager {

	static HashMap<String,Cache> cacheMap=new HashMap<String,Cache>();

	public static Cache getCache(String id)
	{
		if(cacheMap.get(id)==null)
		{
			Object val=getFromDB(id);
			cacheMap.put(id, new Cache(id,val));
		}
		return cacheMap.get(id);
	}

	public static void putCache(Cache cache)
	{
		cacheMap.put(cache.id, cache);
	}

	public static Object getFromDB(String id)
	{
	System.out.println("缓慢地从内存中读取id="+id+"对应的数据。。。");
	return new String("value"+id);
	}

	public static void main(String[] args) {
		Cache cache1=new Cache("1","value1");
		Cache cache2=new Cache("2","value2");
		CacheManager.putCache(cache1);
		CacheManager.putCache(cache2);
		CacheManager.getCache("3").showInfo();
	}
}

class Cache
{
	String id;//相当于主键
	Object val;
	public Cache(String id)
	{
		new Cache(id,null);
	}

	public Cache(String id,Object val)
	{
		this.id=id;
		this.val=val;
	}

	public void setValue(Object val)
	{
		this.val=val;
	}

	public void showInfo()
	{
		System.out.println("Cache的ID是:   "+id+"   Cache的值是:   "+val);
	}
}

问题3:如果我想定时一段时间刷新缓存,比如,每隔15min刷新一次所有缓存,那我应该怎么办?

为了方便观察,我们设置为每间隔1s刷新一次。

/**
 *  项目名称:
 *  文件说明:创建一个缓存管理器 刘晨曦
 *  主要特点:
 *  版本号:1.0
 *  创建时间:2013-12-3
 **/
package NBOffer;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Set;

public class CacheManager {

	static HashMap<String,Cache> cacheMap=new HashMap<String,Cache>();

	public static Cache getCache(String id)
	{
		if(cacheMap.get(id)==null)
		{
			Object val=getFromDB(id);
			cacheMap.put(id, new Cache(id,val));
		}
		return cacheMap.get(id);
	}

	public static void putCache(Cache cache)
	{
		cacheMap.put(cache.id, cache);
	}

	public static Object getFromDB(String id)
	{
	System.out.println("缓慢地从内存中读取id="+id+"对应的数据。。。");
	return new String("value"+id);
	}

	public static void refreshCaches()
	{
		System.out.println("刷新缓存。。。");
		Set<String> keySet=cacheMap.keySet();
		Iterator it=keySet.iterator();
		while(it.hasNext())
		{
			String id=(String) it.next();
			Object val=getFromDB(id);
			cacheMap.put(id, new Cache(id,val));
		}
	}

	public static void main(String[] args) {
		Cache cache1=new Cache("1","value1");
		Cache cache2=new Cache("2","value2");
		CacheManager.putCache(cache1);
		CacheManager.putCache(cache2);
		CacheManager.getCache("3").showInfo();

		Thread refreshTD=new Thread()
		{
			public void run()
			{
				while(true)
				{
					refreshCaches();
					try {
						Thread.sleep(1000);//每一秒刷新一次
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			}
		};
		refreshTD.start();
	}
}

class Cache
{
	String id;//相当于主键
	Object val;

	public Cache(String id)
	{
		new Cache(id,null);
	}

	public Cache(String id,Object val)
	{
		this.id=id;
		this.val=val;
	}

	public void setValue(Object val)
	{
		this.val=val;
	}

	public void showInfo()
	{
		System.out.println("Cache的ID是:   "+id+"   Cache的值是:   "+val);
	}
}

问题4:上面你说的缓存是存在问题的,因为每次你都是统一刷新,有的数据刚刚在14min59s的时候上传的,你就得让它在1s后刷新吗?另外,有的数据变化的很快,不能15min刷新的,而是需要没1min刷新一次,你这样做真的好吗?还有,有的数据在这15min中之内都不会用到,还有必要刷新吗?

上述三个问题的确需要改进,那么我可以针对不同的缓存,并且在自由需要(发送请求+缓存不存在/失效)的时候才进行刷新,再进行优化。代码如下:

/**
 *  项目名称:
 *  文件说明:创建一个缓存管理器 lcx
 *  主要特点:
 *  版本号:1.0
 *  创建时间:2013-12-3
 **/
package NBOffer;

import java.util.Date;
import java.util.HashMap;

public class CacheManager {

	static HashMap<String,Cache> cacheMap=new HashMap<String,Cache>();

	public static Cache getCache(String id)
	{
		//对于不存在或者失效的缓存统一处理
		if(cacheMap.get(id)==null||!cacheMap.get(id).isValid())
		{
			Object val=getFromDB(id);
			Cache cache=new Cache(id,val);
			cache.latest=new Date();
			cacheMap.put(id, cache);
		}
		return cacheMap.get(id);
	}

	public static void putCache(Cache cache)
	{
		cache.latest=new Date();
		cacheMap.put(cache.id, cache);
	}

	public static Object getFromDB(String id)
	{
		System.out.println("缓慢地从内存中读取id="+id+"对应的数据。。。");
		return new String("value"+id);
	}

	public static void main(String[] args) {
		Cache cache1=new Cache("1","value1");
		Cache cache2=new Cache("2","value2");
		cache2.invalidTime=5000;//设置失效视角是5s
		CacheManager.putCache(cache1);
		CacheManager.putCache(cache2);
		try {
			Thread.sleep(6000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		CacheManager.getCache("1");
		CacheManager.getCache("2");
	}
}
class Cache
{
	String id;//相当于主键
	Object val;
	Date latest;//最近刷新时间
	long invalidTime=10000;//默认失效时间10s

	public Cache(String id)
	{
		new Cache(id,null);
	}

	public Cache(String id,Object val)
	{
		this.id=id;
		this.val=val;
	}

	public void setValue(Object val)
	{
		this.val=val;
	}

	/**
	 * 判断现在缓存是否有效
	 * @return
	 */
	public boolean isValid()
	{
		return (new Date()).getTime()-latest.getTime()<invalidTime;
	}

	public void showInfo()
	{
		System.out.println("Cache的ID是:   "+id+"   Cache的值是:   "+val);
	}
}

问题5:既然都做到这个份上了,那么关于缓存的几个名词都得说说了。

缓存命中率:缓存命中率=从缓存中读数/(缓存读数+磁盘读数)。

缓存失效:缓存超过了失效时间。过期了(也可能有其他原因)。

缓存穿透:对于并不存在的数据,每次都会想DB发送请求,对DB的危害非常大。

缓存雪崩:对于某个失效的缓存在短时间内大量访问,可能造成DB宕机。

缓存算法:LRU、LFU、LIFO

下一节,将针对不同的缓存算法进行研究。

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-10-10 13:50:09

java动态缓存成长小纪(一)——创建一个简单的缓存的相关文章

java创建一个简单的小框架frame

import java.awt.*; import javax.swing.*; public class SimpleFrameTest { public static void main(String[] args) { EventQueue.invokeLater(new Runnable(){ // 开一个线程 public void run() { SimpleFrame frame = new SimpleFrame(); frame.setTitle("记事本"); //

Java实现一个简单的缓存方法

缓存是在web开发中经常用到的,将程序经常使用到或调用到的对象存在内存中,或者是耗时较长但又不具有实时性的查询数据放入内存中,在一定程度上可以提高性能和效率.下面我实现了一个简单的缓存,步骤如下. 创建缓存对象EntityCache.java public class EntityCache {   /**    * 保存的数据    */   private Object datas;   /**    * 设置数据失效时间,为0表示永不失效    */   private long time

【Unity Shaders】Reflecting Your World —— 在Unity3D中创建一个简单的动态Cubemap系统

本系列主要参考<Unity Shaders and Effects Cookbook>一书(感谢原书作者),同时会加上一点个人理解或拓展. 这里是本书所有的插图.这里是本书所需的代码和资源(当然你也可以从官网下载). ========================================== 分割线 ========================================== 写在前面 我们已经学了很多关于反射的内容,但是我们现在的反射并不能实时反射,即当反射物体移动时它们不

用 Eclipse 创建一个简单的web项目

Eclipse neon 汉化版 ; 1;右击新建 -->  选择 动态Web项目 2:  填写 项目名 项目位置 ; 选择 Dynamic web module version 和 tomcat version ; 点击完成 即可创建 项目; 2.1:项目名称; 2.2:项目位置; 2.3: Dynamic Web Module Version  和 Tomacat  Version  之间有版本上的匹配关系: 匹配关系如下图 3: 创建成功后的项目结构: 4: 在创建好项目结构之后 先查看一

IntelliJ IDEA 15 部署Tomcat及创建一个简单的Web工程

一.部署Tomcat 二.创建一个简单的Web工程 2.1创建一个新工程 创建一个新工程 设置JDK及选择Web Application (创建的是Web工程) 点击Next,选择工作空间,起个工程名 2.2项目部署 在工具栏点击 Project: 无需任何设置,选择默认编译目录(或自定义编译目录) Modules: 将Tomcat加入 Libraries:无需任何设置.这里描述了此项目的依赖. Facets: 无需任何设置.这里描述了此项目所适配的服务框架 Artifacts: 无需任何配置.

创建一个简单的 MDM 服务器(2)

四.实现 server URL 接下来以推送最简单的锁屏命令为例,演示如何实现从服务器推送完整的 MDM 消息给注册设备. 首先实现一个简单的Jsp 页面.页面中是一个 html 表单,在<select>标签中我们会列出所有注册设备的 UUID(当然列出设备名称会更好),当你选择一个UUID,点击submit 按钮,服务器会向这个设备推送锁屏命令. servlet lock 来负责处理这个表单: Stringudid=request.getParameter("select1&quo

创建一个简单的 MDM 服务器(1)

前提:已获得 APNS 证书 ,已完成 MDM 配置描述文件的制作.请参考< MDM 证书申请流程 >一文和<配置MDM Provisioning Profile>. 环境:OSX 10.9.2,JDK 1.6,Eclipse JavaEE Helois,Tomcat 7.0 一.前言 <THE IOS MDMPROTOCOL>(即Inside Apple MDM)一文中描述了一个简单 MDM Server Python 实现(server.py).笔者也曾参照此文配置

《Yii2 By Example》第2章:创建一个简单的新闻阅读器

第2章 创建一个简单的新闻阅读器 本章内容包含:创建第一个控制器,用于展示新闻条目列表和详情:学习控制器和视图之间的交互:自定义视图的布局. 本章结构如下: 创建控制器和动作 创建用于展示新闻列表的视图 控制器是如何将数据传送到视图的 例子--创建一个控制器,展示静态新闻条目列表和详情 将常用视图内容分割成多个可复用视图 例子--在视图中进行部分渲染 创建静态页面 在视图和布局之前共享数据 例子--根据URL参数更换布局背景 使用动态模块布局 例子--添加展示广告信息的动态盒 使用多个布局 例子

创建一个简单的银行程序包.........未完善,待续

2练习1:创建一个简单的银行程序包   练习目标-Java 语言中面向对象的封装性及构造器的使用. 任务 在这个练习里,创建一个简单版本的(账户类)Account类.将这个源文件放入banking程序包中.在创建单个帐户的默认程序包中,已编写了一个测试程序TestBanking.这个测试程序初始化帐户余额,并可执行几种简单的事物处理.最后,该测试程序显示该帐户的最终余额. 1. 创建banking 包 2. 在banking 包下创建Account类.该类必须实现上述UML框图中的模型. 声明一