Mybatis缓存和Redis缓存服务器的案例分析

      MyBatis默认是支持一级缓存和二级缓存,他自身的实现是通过PerpetualCache,这哥缓存对象实现了,当然也可以是MemarCache。目前中用的比较多的就是Redis,是一个nosql数据库,也称为缓存数据库。


1. 什么是一级缓存? 为什么使用一级缓存?

      每当我们使用MyBatis开启一次和数据库的会话,MyBatis会创建出一个SqlSession对象表示一次数据库会话

      在对数据库的一次会话中,我们有可能会反复地执行完全相同的查询语句,如果不采取一些措施的话,每一次查询都会查询一次数据库,而我们在极短的时间内做了完全相同的查询,那么它们的结果极有可能完全相同,由于查询一次数据库的代价很大,这有可能造成很大的资源浪费。

      为了解决这一问题,减少资源的浪费,MyBatis会在表示会话的SqlSession对象中建立一个简单的缓存,将每次查询到的结果结果缓存起来,当下次查询的时候,如果判断先前有个完全一样的查询,会直接从缓存中直接将结果取出,返回给用户,不需要再进行一次数据库查询了。

     如下图所示,MyBatis会在一次会话的表示----一个SqlSession对象中创建一个本地缓存(local cache),对于每一次查询,都会尝试根据查询的条件去本地缓存中查找是否在缓存中,如果在缓存中,就直接从缓存中取出,然后返回给用户;否则,从数据库读取数据,将查询结果存入缓存并返回给用户。



这个图很好的说明了MyBatis一级缓存的使用周期,以及过程。


Mybatis一级缓存的工作过程:

1.对于某个查询,根据statementId,params,rowBounds来构建一个key值,根据这个key值去缓存Cache中取出对应的key值存储的缓存结果;

2. 判断从Cache中根据特定的key值取的数据数据是否为空,即是否命中;

3. 如果命中,则直接将缓存结果返回;

4. 如果没命中:

        4.1  去数据库中查询数据,得到查询结果;

        4.2  将key和查询到的结果分别作为key,value对存储到Cache中;

        4.3. 将查询结果返回;

5. 结束。


接下来是和Redis整合的例子:

Redis可以说是第三方缓存数据库,很强大,这样就会涉及到二级缓存,二级缓存的图如下:



继承Cache接口:

package com.lgy.cache;

import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

import org.apache.ibatis.cache.Cache;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.lgy.util.SerializeUtil;
import com.lgy.util.redis.JedisPoolUtils;

import redis.clients.jedis.Jedis;


public class MybatisRedisCache implements Cache {

	private static Logger logger = LoggerFactory.getLogger(MybatisRedisCache.class);
	private Jedis redisClient = JedisPoolUtils.getPool().getResource();
	/** The ReadWriteLock. */
	private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock();

	private String id;

	public MybatisRedisCache(final String id) {
		if (id == null) {
			throw new IllegalArgumentException("Cache instances require an ID");
		}
		logger.debug(">>>>>>>>>>>>>>>>>>>>>>>>MybatisRedisCache:id=" + id);
		this.id = id;
	}
	
	@Override
	public String getId() {
		return this.id;
	}
	
	@Override
	public int getSize() {
		return Integer.valueOf(redisClient.dbSize().toString());
	}

	@Override
	public void putObject(Object key, Object value) {
		logger.debug(">>>>>>>>>>>>>>>>>>>>>>>>putObject:" + key + "=" + value);
		redisClient.set(SerializeUtil.serialize(key.toString()), SerializeUtil.serialize(value));
	}
	
	@Override
	public Object getObject(Object key) {
		Object value = null;
		byte[] bs = redisClient.get(SerializeUtil.serialize(key.toString()));
		if(bs != null) {
			value = SerializeUtil.unserialize(bs);
		}
		logger.debug(">>>>>>>>>>>>>>>>>>>>>>>>getObject:" + key + "=" + value);
		return value;
	}
	
	@Override
	public Object removeObject(Object key) {
		logger.debug(">>>>>>>>>>>>>>>>>>>>>>>>removeObject:key=" + key);
		return redisClient.expire(SerializeUtil.serialize(key.toString()), 0);
	}

	@Override
	public void clear() {
		logger.debug(">>>>>>>>>>>>>>>>>>>>>>>>clear");
		redisClient.flushDB();
	}

	@Override
	public ReadWriteLock getReadWriteLock() {
		logger.debug(">>>>>>>>>>>>>>>>>>>>>>>>getReadWriteLock");
		return readWriteLock;
	}

}

 注意几个特别的方法:

1.getObject

2.putObject

3.clear


1.第一次调用select语句时候

               a.会执行getObject()方法,从缓存中获取,若获取到了直接返回。

               b.活执行getObject()方法,从缓存中获取,为null则会执行select sql语句,然后调用putObject方法将key-value存在缓存服务器中。

2.调用update语句,会清空db,调用clear方法


Mybatis实现缓存的机制使用了大量的装饰者模式,有兴趣同学可以去研究下!

你可能感兴趣的:(mybatis)