Spring + MySQL + Mybatis + Redis【二级缓存】
一、Redis环境
Redis 官网 :http://redis.io/
windows下载:https://github.com/dmajkic/redis/downloads
1、文件解压缩
2、启动Redis服务器
3、启动Redis客户端
4、测试Redis缓存
redis-cli.exe -h 127.0.0.1 -p 6379
set keytest valuestest 根据key、value加入缓存
get keytest 根据key获取值
flushall 清空所有缓存
5、设置Redis密码
6、conf配置文件
#是否以后台守护进程运行,默认为no, 取值yes, no
daemonize no #pid文件存放路径
pidfile /var/run/redis.pid #配置redis端口,默认6379
port 6379 #绑定ip。默认绑定所有本机ip,一般用在服务器多ip下,可以只监听内网服务器ip,保证服务安全
bind 127.0.0.1 #sock文件
unixsocket /tmp/redis.sock #客户端超时时间,单位秒
timeout 300 #log级别,支持四个级别,debug,notice,verbose,warning
loglevel verbose #log文件路径
logfile #log输出到标准设备,logs不写文件,输出到空设备,/deb/null
logfile stdout #保存快照的频率,在多长时间内执行一定数量的写操作时,保存快照的频率,可以设置多个条件。如果都注释掉,则不做内存数据持久化。如果只是把redis只用作cache,不开启持久化功能
save <seconds> <changes>
save 900 1 #是否使用压缩
rdbcompression #快照数据库名称
dbfilename #数据库存放路径
dir #redis主从 做法 在从上填上主的IP和端口号 主上不用做任何设置
slaveof <masterip> <masterport> #主库服务器口令,如果主服务器未打开requirepass,则不需要此项
masterauth <master-password> #在master服务器挂掉或者同步失败时,从服务器是否继续提供服务
slave-serve-stale-data yes #设置redis服务密码,如果开启,则客户端连接时需要 -a 指定密码,否则操作会提示无权限
requirepass foobared #命令改名,相当于linux alias,可以用改功能屏蔽一些危险命令
rename-command #最大连接数;0 表示不限制
maxclients 128 #最大使用内存(分配的内存),推荐生产环境下做相应调整,我们用的是只用来做高速缓存,限制2G。默认情况下,redis会占用可用的所有内存
maxmemory <bytes> #过期策略,提供六种策略
maxmemory-policy volatile-lru
volatile-lru //删除过期和lru 的key(默认值)
allkeys-lru //删除lru算法的key
volatile-random //随机删除即将过期key
allkeys->random //随机删除
volatile-ttl //删除即将过期的
noeviction //永不过期,返回错误 #是否开启appendonlylog,开启的话每次写操作会记一条log。相当于mysql的binlog;不同的是,每次redis启动都会读此文件构建完整数据。即使删除rdb文件,数据也是安全的
appendonly #日志文件的名称,默认appendonly.aof
appendfilename appendonly.aof #异步写append file 的策略。类似mysql事物log写方式。三种
appendfsync
appendfsync always //同步,每次写都要flush到磁盘,安全,速度慢。
appendfsync everysec //每秒写(默认值,推荐值)同mysql
appendfsync no //交给操作系统去做flush的动作 #虚拟内存开关
vm-enabled no #swap文件,不同redis swap文件不能共享。而且生产环境下,不建议放在tmp目录
vm-swap-file /tmp/redis.swap #vm大小限制。0:不限制,建议60-80% 可用内存大小
vm-max-memory 0 #根据缓存内容大小调整,默认32字节
vm-page-size 32 #page数。每 8 page,会占用1字节内存。vm-page-size * vm-pages 等于 swap 文件大小
vm-pages 134217728 #vm 最大io线程数。注意: 0 标志禁止使用vm
vm-max-threads 4
二、实现Redis缓存
1、整体思路
- 参考Ehcache实现MyBatis二级缓存代码(Maven引用对应jar查阅)
- 使用Spring管理Redis连接池
- 模仿EhcacheCache,实现RedisCache
2、pom.xml中加入Maven依赖
<!-- spring-redis实现 -->
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-redis</artifactId>
<version>1.6.2.RELEASE</version>
</dependency>
<!-- redis客户端jar -->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.8.0</version>
</dependency>
3、引入applicationContext.xml中引入redis配置
<?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"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- redis数据源 -->
<bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig">
<property name="maxIdle" value="${redis.maxIdle}" />
<property name="maxTotal" value="${redis.maxActive}" />
<property name="maxWaitMillis" value="${redis.maxWait}" />
<property name="testOnBorrow" value="${redis.testOnBorrow}" />
</bean>
<!-- Spring-redis连接池管理工厂 -->
<bean id="jedisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory"
p:host-name="${redis.host}" p:port="${redis.port}" p:password="${redis.pass}" p:pool-config-ref="poolConfig"/>
<!-- 使用中间类解决RedisCache.jedisConnectionFactory的静态注入,从而使MyBatis实现第三方缓存 -->
<bean id="redisCacheTransfer" class="com.hsmdata.springTest.modules.cache.RedisCacheTransfer">
<property name="jedisConnectionFactory" ref="jedisConnectionFactory"/>
</bean>
</beans>
4、redis.properties配置文件
#============================#
#==== Redis settings ====#
#============================#
#redis 服务器 IP
redis.host=127.0.0.1 #redis 服务器端口
redis.port=6379 #redis 密码
redis.pass= #redis 支持16个数据库(相当于不同用户)可以使不同的应用程序数据彼此分开同时又存储在相同的实例上
redis.dbIndex=3 #redis 缓存数据过期时间单位秒(3600*12 = 43 200)
redis.expiration=43200 #控制一个 pool 最多有多少个状态为 idle 的jedis实例
redis.maxIdle=200 #控制一个 pool 可分配多少个jedis实例
redis.maxActive=1000 #当borrow一个jedis实例时,最大的等待时间,如果超过等待时间,则直接抛出JedisConnectionException;
redis.maxWait=500 #在borrow一个jedis实例时,是否提前进行alidate操作;如果为true,则得到的jedis实例均是可用的;
redis.testOnBorrow=true
5、创建缓存实现类RedisCache
package com.hsmdata.springTest.modules.cache; import org.apache.ibatis.cache.Cache;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.connection.jedis.JedisConnection;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.serializer.JdkSerializationRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializer;
import redis.clients.jedis.exceptions.JedisConnectionException; import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock; /**
* @author
* 2018-04-10 20:50
* $DESCRIPTION}
*/ public class RedisCache implements Cache
{
private static final Logger logger = LoggerFactory.getLogger(RedisCache.class); private static JedisConnectionFactory jedisConnectionFactory; private final String id; /**
* The {@code ReadWriteLock}.
*/
private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock(); public RedisCache(final String id) {
if (id == null) {
throw new IllegalArgumentException("Cache instances require an ID");
}
logger.debug("MybatisRedisCache:id=" + id);
this.id = id;
} @Override
public void clear()
{
JedisConnection connection = null;
try
{
connection = jedisConnectionFactory.getConnection();
connection.flushDb();
connection.flushAll();
}
catch (JedisConnectionException e)
{
e.printStackTrace();
}
finally
{
if (connection != null) {
connection.close();
}
}
} @Override
public String getId()
{
return this.id;
} @Override
public Object getObject(Object key)
{
Object result = null;
JedisConnection connection = null;
try
{
connection = jedisConnectionFactory.getConnection();
RedisSerializer<Object> serializer = new JdkSerializationRedisSerializer();
result = serializer.deserialize(connection.get(serializer.serialize(key)));
}
catch (JedisConnectionException 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;
try
{
connection = jedisConnectionFactory.getConnection();
result = Integer.valueOf(connection.dbSize().toString());
}
catch (JedisConnectionException e)
{
e.printStackTrace();
}
finally
{
if (connection != null) {
connection.close();
}
}
return result;
} @Override
public void putObject(Object key, Object value)
{
JedisConnection connection = null;
try
{
connection = jedisConnectionFactory.getConnection();
RedisSerializer<Object> serializer = new JdkSerializationRedisSerializer();
connection.set(serializer.serialize(key), serializer.serialize(value));
}
catch (JedisConnectionException e)
{
e.printStackTrace();
}
finally
{
if (connection != null) {
connection.close();
}
}
} @Override
public Object removeObject(Object key)
{
JedisConnection connection = null;
Object result = null;
try
{
connection = jedisConnectionFactory.getConnection();
RedisSerializer<Object> serializer = new JdkSerializationRedisSerializer();
result =connection.expire(serializer.serialize(key), 0);
}
catch (JedisConnectionException e)
{
e.printStackTrace();
}
finally
{
if (connection != null) {
connection.close();
}
}
return result;
} public static void setJedisConnectionFactory(JedisConnectionFactory jedisConnectionFactory) {
RedisCache.jedisConnectionFactory = jedisConnectionFactory;
} }
6、创建中间类RedisCacheTransfer,完成RedisCache.jedisConnectionFactory的静态注入
package com.hsmdata.springTest.modules.cache; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory; /**
* @author
* 2018-04-10 20:52
* $DESCRIPTION}
*/ public class RedisCacheTransfer {
@Autowired
public void setJedisConnectionFactory(JedisConnectionFactory jedisConnectionFactory) {
RedisCache.setJedisConnectionFactory(jedisConnectionFactory);
}
}
7、mapper中加入MyBatis二级缓存
<mapper namespace="com.hsmdata.springTest.modules.mapper.UserMapper" >
<!--开启本mapper的二级缓存,隔10秒自动刷新缓存 flushInterval="10000" -->
<cache type="com.hsmdata.springTest.modules.cache.RedisCache" />
8、Mybatis全局开启二级缓存
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<settings>
<!-- 开启二级缓存,默认是false -->
<setting name="cacheEnabled" value="true"/> <!--resultMap中的association和collection标签具有延迟加载的功能。-->
<!--延迟加载的意思是说,在关联查询时,利用延迟加载,先加载主信息。使用关联信息时再去加载关联信息。--> <!-- lazyLoadingEnabled:延迟加载启动,默认是false
全局性设置懒加载。如果设为‘false’,则所有相关联的都会被初始化加载。-->
<setting name="lazyLoadingEnabled" value="false"/> <!-- aggressiveLazyLoading:积极的懒加载,false的话按需加载,默认是true
当设置为‘true’的时候,懒加载的对象可能被任何懒属性全部加载。否则,每个属性都按需加载。-->
<setting name="aggressiveLazyLoading" value="true"/> <setting name="multipleResultSetsEnabled" value="true"/>
<setting name="useColumnLabel" value="true"/>
<setting name="useGeneratedKeys" value="true"/>
<setting name="defaultExecutorType" value="SIMPLE"/>
<setting name="defaultStatementTimeout" value="25000"/>
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
</configuration>
9、测试
package com.springTest.mybatis.cache; import com.hsmdata.springTest.modules.entity.User;
import com.hsmdata.springTest.modules.mapper.UserMapper;
import com.hsmdata.springTest.modules.service.UserService;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; /**
* @author
* 2018-04-10 15:11
* $DESCRIPTION}
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration({"classpath:/config/applicationContext.xml", "classpath:/config/spring-servlet.xml"})
public class CacheTest {
@Autowired
private UserService userService;
@Autowired
private SqlSessionFactory sqlSessionFactory; /**
* 同一个sqlSession
*/
@Test
public void testFirstCache(){
SqlSession sqlSession=sqlSessionFactory.openSession();
UserMapper userMapper= sqlSession.getMapper(UserMapper.class);
User user=userMapper.selectByPrimaryKey(56);
System.out.println(user); /* 对sqlsession执行commit操作,也就意味着用户执行了update、delete等操作,那么数据库中的数据势必会发生变化,如果用户请求数据仍然使用之前内存中的数据,那么将读到脏数据。
所以在执行sqlsession操作后,会清除保存数据的HashMap,用户在发起查询请求时就会重新读取数据并放入一级缓存中了。*/
// sqlSession.commit(); user=userMapper.selectByPrimaryKey(56);
System.out.println(user);
} /**
* 不同的sqlSession
*/
@Test
public void testSecondaryCache(){
SqlSession sqlSession=sqlSessionFactory.openSession();
UserMapper userMapper= sqlSession.getMapper(UserMapper.class);
User user=userMapper.selectByPrimaryKey(56);
System.out.println(user); // 即使开启了二级缓存,不同的sqlsession之间的缓存数据也不是想互访就能互访的,必须等到sqlsession关闭了以后,才会把其一级缓存中的数据写入二级缓存。
// 关闭session
// sqlSession.close(); // 通过sqlSessionFactory创建一个新的session
sqlSession=sqlSessionFactory.openSession();
// 获取mapper对象
userMapper=sqlSession.getMapper(UserMapper.class); user=userMapper.selectByPrimaryKey(56);
System.out.println(user);
} /**
* 不同的sqlSession
*/
@Test
public void testSecondaryCache2() {
/* User user=new User("cache","123456","cache","","male",20);
userService.insert(user);*/ User user = userService.get(56);
System.out.println(user); User user1 = userService.get(56);
System.out.println(user1);
} @Test
public void testFirstCache2() {
/* User user=new User("cache","123456","cache","","male",20);
userService.insert(user);*/ User user = userService.getTwo(56);
System.out.println(user);
} @Test
public void testRedisCache(){
User user=userService.get(55);
System.out.println(user); User user2=userService.get(56);
System.out.println(user2); User user3=userService.get(55);
System.out.println(user3); } }
Spring + MySQL + Mybatis + Redis【二级缓存】的更多相关文章
- Spring Boot + Mybatis + Redis二级缓存开发指南
Spring Boot + Mybatis + Redis二级缓存开发指南 背景 Spring-Boot因其提供了各种开箱即用的插件,使得它成为了当今最为主流的Java Web开发框架之一.Mybat ...
- Spring + MySQL + Mybatis + Redis【二级缓存】执行流程分析
一级缓存基于 PerpetualCache 的 HashMap 本地缓存,其存储作用域为 Session,当 Session flush 或 close 之后,该Session中的所有 Cache 就 ...
- Redis集成到Spring做mybatis做二级缓存
一.原理: 要缓存的 Java 对象必须实现 Serializable 接口,因为 Spring 会将对象先序列化再存入 Redis,比如本文中的 com.defonds.bdp.city.bean. ...
- springboot mybatis redis 二级缓存
前言 什么是mybatis二级缓存? 二级缓存是多个sqlsession共享的,其作用域是mapper的同一个namespace. 即,在不同的sqlsession中,相同的namespace下,相同 ...
- spring+springmvc+mybatis+redis实现缓存
先搭建好redis环境 需要的jar如下: jdbc.driverClassName=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql://localhost:330 ...
- 使用Redis做MyBatis的二级缓存
使用Redis做MyBatis的二级缓存 通常为了减轻数据库的压力,我们会引入缓存.在Dao查询数据库之前,先去缓存中找是否有要找的数据,如果有则用缓存中的数据即可,就不用查询数据库了. 如果没有才去 ...
- Redis实现Mybatis的二级缓存
一.Mybatis的缓存 通大多数ORM层框架一样,Mybatis自然也提供了对一级缓存和二级缓存的支持.一下是一级缓存和二级缓存的作用于和定义. 1.一级缓存是SqlSession级别的缓存.在操作 ...
- Mybatis使用Redis二级缓存
在Mybatis中允许开发者自定义自己的缓存,本文将使用Redis作为Mybatis的二级缓存.在Mybatis中定义二级缓存,需要如下配置: 1. MyBatis支持二级缓存的总开关:全局配置变量参 ...
- mybatis整合redis二级缓存
mybatis默认开启了二级缓存功能,在mybatis主配置文件中,将cacheEnabled设置成false,则会关闭二级缓存功能 <settings> <!--二级缓存默认开启, ...
随机推荐
- JS interview loop code
//九九乘法表 document.write("<table width='600' border=0'>"); for(var i=1; i<=9; i++){ ...
- ABAP git客户端
Jerry习惯把自己写的小程序放到自己的github上:https://github.com/i042416 对于写的ABAP程序,需要先把SAPGUI里的代码手动拷贝到本地,然后用git客户端pus ...
- Jerry的ABAP原创技术文章合集
我之前发过三篇和ABAP相关的文章: 1. Jerry的ABAP, Java和JavaScript乱炖 这篇文章包含我多年来在SAP成都研究院使用ABAP, Java和JavaScript工作过程中的 ...
- Uva 10765 鸽子和炸弹
题目链接:https://vjudge.net/contest/166461#problem/B 题意: 给一个无向图,求每一个点删除后,剩下的连通块的数目: 分析: 只有割顶被删掉后,连通分量才会改 ...
- 【转】JS模块化工具requirejs教程(二):基本知识
前一篇:JS模块化工具requirejs教程(一):初识requirejs 我们以非常简单的方式引入了requirejs,这一篇将讲述一下requirejs中的一些基本知识,包括API使用方式等. 基 ...
- XCode插件因为升级不能用了怎么办?几个步骤教你搞定
之前XCode安装了自动注释的插件 VVDomenter.升级之后不能使用了怎么办?跟着我做吧. 1.打开xcode插件所在的目录:~/library/Application Support/Deve ...
- activeMQ消息队列的使用
ActiveMQ解决问题: 1.解决服务之间的耦合 2.增加系统并发处理量. 它使用的是标准生产者和消费者模型.有两种数据结构:Queue/Topic 1.Queue队列,生产者生产一个消息,只能由 ...
- Android学习笔记_22_服务Service应用之—与Activity进行相互通信的本地服务
一.启动服务的两种方法方法: 第一种: startService()和stopService()启动关闭服务.适用于服务和Activity之间没有调用交互的情况.如果相互之间需要方法调用或者传递参数 ...
- Centos安装VMware
转载:http://www.mamicode.com/info-detail-2171464.html
- Data Guard 知识 (来自网络)
更改DG工作模式前提参数得设定合理. Physical standby直接从主库接受archived log,然后直接做基于block的物理恢复(更新或调整变化的block),所以physical s ...