采用redis作为独立缓存服务器(一)

读者注意:接上篇,请先阅读上篇。

一  环境

       基础环境:jdk1.7+spring3.2.9+mybatis3.2.2+redis3.2.8+mysql5.6

      引入额外的依赖

<dependency>
    	<groupId>org.springframework.data</groupId>
    	<artifactId>spring-data-redis</artifactId>
    	<version>1.3.1.RELEASE</version>
	</dependency>
	<dependency>
	    <groupId>redis.clients</groupId>
	    <artifactId>jedis</artifactId>
	    <version>2.4.1</version>
	</dependency>
maven会自动添加上如下依赖包:

spring-data-redis-1.3.1.RELEASE.jar

jedis-2.4.1.jar

commons-pool2-2.0.jar

附加工具及验证工具:

centos6.5  64位   +     SecureCRT6.7  +  RedisDesktopManager0.8.8

二    服务器架构



三  关键代码

=======================redis参数配置=====================================

#*****************jedis连接参数设置*********************
#redis服务器ip
redis.ip=169.254.130.122
#redis服务器端口号
redis.port=6379
#redis访问密码
redis.passWord=test123
#与服务器建立连接的超时时间
redis.timeout=3000
#************************jedis池参数设置*******************
#jedis的最大活跃连接数
jedis.pool.maxActive=50
#jedis最大空闲连接数
jedis.pool.maxIdle=10
#jedis池没有连接对象返回时,等待可用连接的最大时间,单位毫秒,默认值为-1,表示永不超时。
#如果超过等待时间,则直接抛出JedisConnectionException
jedis.pool.maxWait=1500
#从池中获取连接的时候,是否进行有效检查
jedis.pool.testOnBorrow=true
#归还连接的时候,是否进行有效检查
jedis.pool.testOnReturn=true


======================spring配置=========================

<?xml version="1.0" encoding="UTF-8"?>
<beans
	xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:p="http://www.springframework.org/schema/p"
	xmlns:aop="http://www.springframework.org/schema/aop"
	xmlns:tx="http://www.springframework.org/schema/tx"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:cache="http://www.springframework.org/schema/cache"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
	 http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
	 http://www.springframework.org/schema/aop 
	 http://www.springframework.org/schema/aop/spring-aop-3.2.xsd
	 http://www.springframework.org/schema/tx
	 http://www.springframework.org/schema/tx/spring-tx-3.2.xsd
	 http://www.springframework.org/schema/context
     http://www.springframework.org/schema/context/spring-context-3.2.xsd
	 http://www.springframework.org/schema/cache
     http://www.springframework.org/schema/cache/spring-cache-3.2.xsd">
	 
	<import resource="classpath:configs/spring/applicationContext-*.xml"/>
	
	<bean id="config" class="org.springframework.beans.factory.config.PreferencesPlaceholderConfigurer">
		<property name="locations">
			<list>
				<value>classpath:configs/druidConfig.properties</value>
				<value>classpath:configs/redis.properties</value>
			</list>
		</property>
	</bean>
	<bean id="druidDataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
		<property name="driverClassName" value="${driverClassName}" />
		<property name="url" value="${url}" />
		<property name="username" value="${username}" />
		<property name="password" value="${password}" />
		<property name="filters" value="${filters}" />
		<property name="initialSize" value="${initialSize}" />
		<property name="maxActive" value="${maxActive}" />
		<property name="minIdle" value="${minIdle}" />
		<property name="maxWait" value="${maxWait}" />
		<property name="validationQuery" value="${validationQuery}" />
		<property name="testWhileIdle" value="${testWhileIdle}" />
		<property name="testOnBorrow" value="${testOnBorrow}" />
		<property name="testOnReturn" value="${testOnReturn}" />
		<property name="maxPoolPreparedStatementPerConnectionSize" value="${maxPoolPreparedStatementPerConnectionSize}" />
		<property name="removeAbandoned" value="${removeAbandoned}" />
		<property name="removeAbandonedTimeout" value="${removeAbandonedTimeout}" />
		<property name="timeBetweenEvictionRunsMillis" value="${timeBetweenEvictionRunsMillis}" />
		<property name="minEvictableIdleTimeMillis" value="${minEvictableIdleTimeMillis}" />
	</bean>
	<bean id="sessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
		<property name="dataSource" ref="druidDataSource"></property>
		<property name="configLocation">
			<value>classpath:configs/mybatis-config.xml</value>
		</property>
		<property name="mapperLocations">
			<list>
				<value>classpath:com/wx/entitys/*.xml</value>
			</list>
		</property>
	</bean>
	<bean id="sqlSessionTemplate" class="org.mybatis.spring.SqlSessionTemplate">
		<constructor-arg name="sqlSessionFactory" ref="sessionFactory"></constructor-arg>
	</bean>
	<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
		<property name="dataSource" ref="druidDataSource"></property>
	</bean>
	<tx:advice id="txAdvise" transaction-manager="txManager">
		<tx:attributes>
			<tx:method name="find*" read-only="true"/>
			<tx:method name="search*" read-only="true"/>
			<tx:method name="query*" read-only="true"/>
			<tx:method name="get*" read-only="true"/>
			<tx:method name="add*" propagation="REQUIRED"/>
			<tx:method name="save*" propagation="REQUIRED"/>
			<tx:method name="do*" propagation="REQUIRED"/>
			<tx:method name="update*" propagation="REQUIRED"/>
			<tx:method name="del*" propagation="REQUIRED"/>
		</tx:attributes>
	</tx:advice>
	<aop:config>
		<aop:pointcut expression="execution(* com.wx.service.*.*(..))" id="myCut"/>
		<aop:advisor advice-ref="txAdvise" pointcut-ref="myCut"/>
	</aop:config>
	 
    <!-- 配置redis 单机版 start-->
    <!-- redis数据源 -->
    <bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig">  
        <!-- 最大空闲数 -->
        <property name="maxIdle" value="${jedis.pool.maxIdle}" />  
        <!-- 最大活跃数 -->
        <property name="maxTotal" value="${jedis.pool.maxActive}" />  
        <!-- 最大等待时间 -->
        <property name="maxWaitMillis" value="${jedis.pool.maxWait}" />  
        <!-- 返回连接时,检测连接是否成功 -->
        <property name="testOnBorrow" value="${jedis.pool.testOnBorrow}" />  
    </bean>
    
    <!-- Spring-redis连接池管理工厂 -->
    <bean id="jedisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
        <!-- IP地址 -->
        <property name="hostName" value="${redis.ip}" />
        <!-- 端口号 -->
        <property name="port" value="${redis.port}" />
        <!-- 密码 -->
        <property name="password" value="${redis.passWord}"/>
        <!-- 超时时间 -->
        <property name="timeout" value="${redis.timeout}" />
        <property name="poolConfig" ref="poolConfig" />
    </bean> 
    
    <!-- 使用中间类解决RedisCache.jedisConnectionFactory的静态注入,从而使MyBatis实现第三方缓存 -->
    <bean id="redisCacheTransfer" class="com.wx.utils.RedisCacheTransfer">
        <property name="jedisConnectionFactory" ref="jedisConnectionFactory"/>
    </bean> 
    <!-- 单机版Redis集成 End -->
   
</beans>

Mybatis为了方便我们扩展缓存定义了一个Cache接口,看看ehcache-mybatis的源码就明白了。我们要使用自己的cache同样的实现Cache接口即可。

package com.wx.utils;

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

import org.apache.ibatis.cache.Cache;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.serializer.JdkSerializationRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializer;

public class RedisCache implements Cache {

    private static JedisConnectionFactory jedisConnectionFactory;
                                          
    private final String id;
    
    private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
    
    public RedisCache(final String id){
        if (id == null) {
            throw new IllegalArgumentException("cache instances require an ID");
        }
        this.id = id;
    }
    
    
    /**
     * 缓存的清除策略,只要有增删改操作,那么就会自动调用它 ,如果不需要,请注释掉
     */
    public void clear() {
    	RedisConnection connection = null;
        try {
        	connection=jedisConnectionFactory.getConnection();
            connection.flushDb();
            connection.flushAll();
            System.out.println("缓存被清空了....");
        } catch (Exception e) {
            e.printStackTrace();
        }finally{
            if (connection != null) {
                connection.close();
            }
        }
        
    }

    @Override
    public String getId() {
        return this.id;
    }

    @Override
    public Object getObject(Object key) {
        System.out.println("--------------------------------get_key==>"+key+"<==");
        Object result = null;
        //JedisConnection connection = null;
        RedisConnection connection = null;
        try {
            connection = jedisConnectionFactory.getConnection();
            RedisSerializer<Object> serializer = new JdkSerializationRedisSerializer();
            result = serializer.deserialize(connection.get(serializer.serialize(key)));
        } catch (Exception e) {
            e.printStackTrace();
        }finally{
            if (connection != null) {
                connection.close();
            }
        }
        
        return result;
    }

    @Override
    public ReadWriteLock getReadWriteLock() {
        return this.readWriteLock;
    }

    @Override
    public int getSize() {
        int result = 0;
        //JedisConnection connection = null;
        RedisConnection connection = null;
        try {
            connection = jedisConnectionFactory.getConnection();
            result = Integer.valueOf(connection.dbSize().toString());
        } catch (Exception e) {
            e.printStackTrace();
        }finally{
            if (connection != null) {
                connection.close();
            }
        }
        return result;
    }

    @Override
    public void putObject(Object key, Object value) {
        System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>put_key===>"+key+"<===");
          
        //JedisConnection connection = null;
        RedisConnection connection = null;
        try {
            connection = jedisConnectionFactory.getConnection();
            RedisSerializer<Object> serializer = new JdkSerializationRedisSerializer();
            System.out.println("**"+serializer.serialize(key));
            connection.set(serializer.serialize(key), serializer.serialize(value));
        } catch (Exception e) {
            e.printStackTrace();
        }finally{
            if (connection != null) {
                connection.close();
            }
        }
        
    }

    @Override
    public Object removeObject(Object key) {
        //JedisConnection connection = null;
    	RedisConnection connection=null;
        Object result = null;
        try {
        	System.out.println("===>"+key+"一个缓存被删除了");
            connection = jedisConnectionFactory.getConnection();
            RedisSerializer<Object> serializer = new JdkSerializationRedisSerializer();
            result = connection.expireAt(serializer.serialize(key), 0);
        } catch (Exception e) {
            e.printStackTrace();
        }finally{
            if (connection != null) {
                connection.close();
            }
        }
        return result;
    }

    public static void setJedisConnectionFactory(JedisConnectionFactory jedisConnectionFactory) {
        RedisCache.jedisConnectionFactory = jedisConnectionFactory;
    }
}
========================

package com.wx.utils;

import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;

//该类用于JedisConnectionFactory对象的注入
public class RedisCacheTransfer {
	 public void setJedisConnectionFactory(JedisConnectionFactory jedisConnectionFactory) {
	       RedisCache.setJedisConnectionFactory(jedisConnectionFactory);
	 }
}


===============================

在看ehcache-mybatis的源码 它真正使用cache的方式是通过集成org.apache.ibatis.cache.decorators.LoggingCache 这个类实现的,所以我们也继承

package com.wx.utils;

import org.apache.ibatis.cache.decorators.LoggingCache;

public class LoggingRedisCache extends LoggingCache{
	public LoggingRedisCache(String id) {  
        super(new RedisCache(id));  
	} 
}

在mapper.xml中添加如下cache标签

<!-- 支持缓存配置 -->
	<cache type="com.wx.utils.LoggingRedisCache" />

在mybatis核心配置文件中启用缓存功能

<!-- 配置mybatis的缓存,延迟加载等相关属性 -->
    <settings>
        <!-- 是否开启全局缓存 -->
        <setting name="cacheEnabled" value="true"/>
        <!-- 查询时,关闭关联对象即时加载以提高性能 -->
        <setting name="lazyLoadingEnabled" value="false"/>
        <!-- 设置关联对象加载的形态,此处为按需加载字段(加载字段由SQL指 定),不会加载关联表的所有字段,以提高性能 -->
        <setting name="aggressiveLazyLoading" value="true"/>
        <!-- 配置默认的执行器。SIMPLE 执行器没有什么特别之处。REUSE 执行器重用预处理语句。BATCH 执行器重用语句和批量更新 -->  
    	<setting name="defaultExecutorType" value="REUSE" />
    	<!-- 对于未知的SQL查询,允许返回不同的结果集以达到通用的效果 -->
        <setting name="multipleResultSetsEnabled" value="true"/>
    </settings>

四   验证

应用程序运行前,缓存服务器里面没有任何缓存,如图


进行登录操作,并进入综合查询页码


这中间会经历4个sql语句

select * from users where userName=? and passWord=?

SELECT s.stuId,s.stuName,s.gender,s.age,s.address,d.departName  FROM student s inner join department d   on s.deptidd=d.deptid   where 1=1 
   ORDER BY AGE DESC
   LIMIT ?,?

SELECT count(*)   FROM student s inner join department d   on s.deptidd=d.deptid   where 1=1

select departName from department

然后我们可以看到缓存服务器上就会有对应的4个键值对

直接从linux服务器上看


从运行结果上看,先从缓存取,没取到从数据库取,然后存入缓存


从redis图形管理界面也可以看出


往后翻,发现一个sql一个key


再次运行同样的sql,发现只从缓存取数据了,而没必要从数据库取数据了


证明独立缓存集成配置成功!!!

这里有个问题,如果进行增删改操作,会默认删除所有的缓存,而不是按照mapper的命名空间刷新缓存,如果有读者解决了这个问题请留言告诉我,感激不尽!!!

该缓存解决方案并不能实现对缓存的精确控制!

比如如下需求:对商品信息进行缓存,由于商品信息查询访问量大,
但是要求用户每次都能查询最新的商品信息,此时就无法实现当一个商品变化时
只刷新该商品的缓存信息而不刷新其它商品的信息,因为mybaits的二级缓存区域以mapper为单位划分,
当一个商品信息变化会将所有商品信息的缓存数据全部清空。
解决此类问题需要在业务层根据需求对数据有针对性缓存。

请读者继续阅读我的后面的博文,会提供相应解决方案!!!





















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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

御前两把刀刀

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值