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

 

 

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

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

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

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. 如果应用服务器分布式部署,相互之间缓存无法共享

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

转载于:https://my.oschina.net/u/2012189/blog/752423

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值