Java对象缓存(1) —— 内存缓存

 

 

开发过程当中会有很是频繁地查询某一类对象,尤为是经过主键查询整个对象的状况。好比user,对于前端UI来讲,极可能展示任何业务列表的时候,都有相关的用户信息,须要显示用户的昵称、头像之类的,这是就要把列表中涉及的用户一个个查出来。前端

传统的方式是使用数据库联合查询,但若是用户表很大,和业务表关联查询的代价是很高的,并且若是用户表与业务表不在同一个数据库实例上,就无法联合了。另外一种思路就是先查业务表——若是有分页机制的话,一般结果也就是几十条,再针对结果集的每一个用户主键,一一查询对应的用户信息。后者的好处是查询压力是可控的,不至于让数据库爆掉;缺点在于对数据库的查询请求仍是过于频繁。java

在这种状况下,若是用户信息不常常变更,就能够将其缓存起来,每次从缓存中获取数据,从而减轻数据库压力。Java中最简单的内存缓存实现就是用HashMap,以数据库主键为key,整个对象序列化之后的字符串做为value。但HashMap不是线程安全的,并发状况下,可能出现意想不到的错误,因此应该是用concurrent·包中的ConcurrentHashMap类实现。代码示例以下:sql

package com.nuanxinli.cache;

import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

import org.nutz.dao.Dao;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;

import com.nuanxinli.bo.instance.User;

@IocBean
public class UserCache {
	private ConcurrentMap<String, User> userMap = new ConcurrentHashMap<String, User>();
	@Inject("refer:system_dao")
	private Dao dao;

	public User get(String username) {
		User user = userMap.get(username);
		if (user == null) {
			user = dao.fetch(User.class, username);
			userMap.put(username, user);
		}
		return user;
	}

	public void updateOneUser(User user) {
		userMap.put(user.getUsername(),user);		
	}

}

从代码逻辑能够看出,调用get方法时,首先从map里查找,若是map中没有,再从数据库里取,而且把结果加入到缓存map中,下次在使用便可直接从map中取到。另外,提供了一个updateOneUser方法,以便在用户信息发生变化时更新缓存。下面是使用UserCache的例子数据库

package com.nuanxinli.logic.livecast;

import java.util.List;

import org.nutz.dao.Chain;
import org.nutz.dao.Cnd;
import org.nutz.dao.Dao;
import org.nutz.dao.QueryResult;
import org.nutz.dao.pager.Pager;
import org.nutz.dao.sql.Criteria;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;

import com.nuanxinli.application.ImHttpException;
import com.nuanxinli.bo.instance.livecast.LiveComment;
import com.nuanxinli.cache.UserCache;
import com.nuanxinli.util.StrUtil;

@IocBean
public class LiveCommentLogic {

	@Inject("refer:gold_dao")
	private Dao goldDao;
	@Inject
	private UserCache userCache;

	public QueryResult getList(Integer liveId, String type, Integer pageNum, Integer pageSize) {
        //查询业务对象列表
		Criteria cri = Cnd.cri();
		if (StrUtil.isNotNullOrBlank(type)) {
			cri.where().and("type", "=", type);
		}
		cri.where().and("is_deleted", "=", 0).and("live_id", "=", liveId);
		Pager pager = goldDao.createPager(pageNum, pageSize);
		List<LiveComment> list = goldDao.query(LiveComment.class, cri, pager);
		pager.setRecordCount(goldDao.count(LiveComment.class, cri));
        //遍历列表,把其中的用户信息补全
		for (LiveComment liveComment : list) {
			liveComment.setCreateUser(userCache.get(liveComment.getCreator()));
		}
		return new QueryResult(list, pager);
	}

}

这里的代码使用了Nutz的ioc、dao和Json序列化框架。缓存

这个缓存方法固然是简单粗暴的,不少问题都没有考虑到,好比:安全

  1. ConcurrentMap的内存使用效率不高,一旦缓存用户量大,会撑爆内存。好在只有被查询到的用户才会被缓存,而大部分应用活跃用户数都远小于总用户数,对内存的压力不算太大。
  2. Java应用服务器一旦重启,缓存所有丢失
  3. 若是应用服务器分布式部署,相互之间缓存没法共享

后两个问题,就须要引入独立的集中缓存方案了,后面继续总结。服务器

相关文章
相关标签/搜索