适用于高并发的本地缓存方案

使用本地缓存需要注意两个问题:

1 内存管理,及时解除无用对象的引用。防止大量无用对象进入old区,引发full gc。

2 数据同步,如果应用是一个集群,需要保持各台机器的数据一致性。

问题1的解决可以采用LRU算法,预先定好缓存大小。达到最大值后,清除最近最少使用的对象。

问题2比较复杂,需要有一个集中的地方控制缓存一致,比如可以采用消息中间件,写时进行异步复制。这种方式成本较大。

其实对于业务系统,用户通过浏览器访问的数据,不需要很强的一致性。只要在3秒内,各台机器能够保持最终一致,用户是感觉不大不同机器数据的不同步。

因此通过控制缓存时间为3秒或其它很短的时间,就可以保证一定程度的数据一致,避免数据同步的开销和复杂度。

缓存时间短又会引发另外一个问题,就是缓存的命中率。在高并发访问下,缓存时间短可能会导致,大量的访问刚好都没有命中,缓存穿透给系统带来瞬间的高峰压力。

综合考虑以上几个矛盾点,可以对缓存数据进行封装,使用有效失效次数的缓存对象,保证在高并发情况下,大量的访问都能命中缓存,同时又能保证缓存及时失效和更新。代码如下

首先是LRUHashMap。需要注意的是LRUHashMap不是线程安全的,非线程安全访问是否会带来问题,取决于业务场景,对共享变量进行相互覆盖的影响(参考http://www.iteye.com/topic/656670)。

	class LRUHashMap extends LinkedHashMap<String, Object> {
		private int MAX_ENTRIES;

		public LRUHashMap(int size) {
			MAX_ENTRIES = size;
		}

		protected boolean removeEldestEntry(Map.Entry<String, Object> eldest) {
			return size() > MAX_ENTRIES;
		}
	}

缓存对象

public class CacheObject implements Serializable {
	long createTime;
	long lifeTime;
	Object value;
	AtomicInteger invalidTimes;

	public CacheObject(long lifeTime, Object value) {
		this.lifeTime = lifeTime;
		this.value = value;
		invalidTimes = new AtomicInteger(0);
		this.createTime = System.currentTimeMillis();
	}

	public long getCreateTime() {
		return createTime;
	}

	public void setCreateTime(long createTime) {
		this.createTime = createTime;
	}

	public long getLifeTime() {
		return lifeTime;
	}

	public void setLifeTime(long lifeTime) {
		this.lifeTime = lifeTime;
	}

	public Object getValue() {
		// 如果已经失效很久,说明很久没有被访问,那么直接返回null,不对失效次数进行判断。
		if ((System.currentTimeMillis() - createTime) > 10 * lifeTime) {
			return null;
		}
		// 保证在高并发下,缓存失效也可以保证较高的命中率
		if (System.currentTimeMillis() - createTime > lifeTime) {
			if (invalidTimes.incrementAndGet() > 3) {
				return value;
			} else {
				return null;
			}
		}
		return value;
	}

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

	public AtomicInteger getInvalidTimes() {
		return invalidTimes;
	}

	public void setInvalidTimes(AtomicInteger invalidTimes) {
		this.invalidTimes = invalidTimes;
	}
}

本地缓存

public class LocalCache {
	LRUHashMap cacheArea = new LRUHashMap(20);

	public Object get(String key) {
		CacheObject cacheObject = (CacheObject) cacheArea.get(key);
		return cacheObject == null ? null : cacheObject.getValue();
	}

	public void put(String key, Object value, long lifeTime) {
		CacheObject cacheObject = new CacheObject(lifeTime, value);
		cacheArea.put(key, cacheObject);
	}
}

参考http://www.cnblogs.com/redcreen/archive/2011/02/15/1955248.html

猜你喜欢

转载自hill007299.iteye.com/blog/1488888
今日推荐