问题描述

应用在使用过程中会需要重启等,但是如果ehcache随着应用一起重启,那么刚重启的时候就会出现大量的miss,需要一定的访问量来重建缓存,如果缓存能够持久化,重启之后可以复用将会有助于缓解重启的缓存miss

解决办法

ehcache支持在关闭时将缓存持久化到指定的硬盘目录

	<cache name="persistCache"  eternal="true"
		diskPersistent="true" >
	</cache>
	<diskStore path="c:/data" />

通过配置diskPersistent=true(cache级)以及diskStore(配置文件级)在应用shutdown的时候会生成对应的序列化文件。

	@Test
	public void persist() throws InterruptedException{
		CacheManager cache=CacheManager.create("cache.xml");
		Ehcache persistCache=cache.addCacheIfAbsent("persistCache");
		persistCache.put(new Element(1,new Student("a")));
		persistCache.put(new Element(2,new Student("b")));
		cache.shutdown();
	}
	@Test
	public void resume() throws InterruptedException{
		CacheManager cache=CacheManager.create("cache.xml");
		Ehcache persistCache=cache.getEhcache("persistCache");
		System.out.println(((Student)persistCache.get(1).getObjectValue()).getName());
		System.out.println(((Student)persistCache.get(2).getObjectValue()).getName());
		cache.shutdown();
	}

运行两个测试案例,在第二个的输出中会得到第一个存入的值a、b,需要注意的是其中存入的对象需要实现serializable接口。
在查看响应的存储目录时,会看到两个文件"cache名.data"及"cache名.index"。

序列化及反序列化

在调用shutdown方法的时候,ehcache会dispose底层的cache从而实现序列化到硬盘(貌似只能使用java默认的序列化,所以对象要实现serializable)。
在data文件中存放的是存入的Element对象,在index文件中存放的是key的值以及对应的diskMarker对象,这个对象主要存放了element在data文件中的偏移量及大小。当cache启动发现存在持久化文件时,它并不会主动去加载,当调用get,它会在index中寻找是否有命中,如果有,则会取出对应的diskMarker,再去data文件根据偏移量来寻找对应的数据进行反序列化,这也减小初始化的过程的压力,不用将全部对象反序列化。
其中要注意的是序列化的过程只有发生在调用shutdown的时候。如果无法显示调用shutdown方法可以通过设置系统参数System.setProperty("net.sf.ehcache.enableShutdownHook","true");在jvm关闭是调用钩子,从而调用shutdown,当然如果强制关闭进程是无效的。