您的位置:首页 > 数据库 > Redis

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

2018-02-03 00:54 891 查看
读者注意:接上篇,请先阅读上篇。

一  环境

       基础环境: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为单位划分,

当一个商品信息变化会将所有商品信息的缓存数据全部清空。

解决此类问题需要在业务层根据需求对数据有针对性缓存。

请读者继续阅读我的后面的博文,会提供相应解决方案!!!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: