redis实现二级缓存

摘要:
Mybatis也会做缓存,也会有一级缓存和二级缓存:一级缓存:是SqlSession级别的缓存,使用HashMap数据结构来用于存储缓存数据的二级缓存:是mapper级别的缓存,其作用域是mapper的同一个namespace,不同的SqlSession执行两次相同namespace下的sql语句,并且传递的参数相同,返回的结果也相同时,第一次执行sql语句是会将数据从数据库中取出并存入缓存中,第二次查询时便会从缓存中直接获取数据二级缓存的实现大大的降低了数据库的负担,这里就来实现以下使用redis实现Mybatis的二级缓存。

缓存的作用就是降低数据库的使用率,来减轻数据库的负担。我们平常的操作一般都是查>改,所以数据库的有些查操作是重复的,如果一直使用数据库就会有负担。Mybatis也会做缓存,也会有一级缓存和二级缓存:

  • 一级缓存:是SqlSession级别的缓存,使用HashMap数据结构来用于存储缓存数据的
  • 二级缓存:是mapper级别的缓存,其作用域是mapper的同一个namespace,不同的SqlSession执行两次相同namespace下的sql语句,并且传递的参数相同,返回的结果也相同时,第一次执行sql语句是会将数据从数据库中取出并存入缓存中,第二次查询时便会从缓存中直接获取数据
    二级缓存的实现大大的降低了数据库的负担,这里就来实现以下使用redis实现Mybatis的二级缓存。

我这里使用springboot快速搭建的项目>>>直通<<<,基本的环境如下:
jdk 1.7+
springboot maven mybatis项目
redis
mysql

二级缓存的实现

概述

redis二级缓存的实现,主要是重写了Cache.java的方法,自定义缓存,先来看看Cache的方法有哪些:

1
2
3
4
5
6
7
String getId();
void putObject(Object var1, Object var2);
Object getObject(Object var1);
Object removeObject(Object var1);
void clear();
int getSize();
ReadWriteLock getReadWriteLock();

这里有put、get、clear等方法,将在我们自己的缓存方法中重写这些方法

自定义缓存方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
public class implements Cache {
private static final Logger logger = LoggerFactory.getLogger(MybatisRedisCache.class);
private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
private RedisTemplate redisTemplate;
private String id;
private static final long EXPIRE_TIME_IN_MINUTES = 30;
public (String id){
if (id==null){
throw new IllegalArgumentException("Cache instances require an ID");
}
logger.info("=====================================Redis cache id = "+id);
this.id = id;
}
public String getId() {
return id;
}
public void putObject(Object key, Object value) {
logger.debug("==============================redis put= "+key);
RedisTemplate redisTemplate = getRedisTemplate();
ValueOperations opsForValue = redisTemplate.opsForValue();
opsForValue.set(key, value, EXPIRE_TIME_IN_MINUTES, TimeUnit.MINUTES);
}
public Object getObject(Object key) {
logger.debug("================================redis get================================");
RedisTemplate redisTemplate = getRedisTemplate();
ValueOperations opsForValue = redisTemplate.opsForValue();
return opsForValue.get(key);
}
public Object removeObject(Object key) {
logger.debug("==========================================redis remove==========================");
RedisTemplate redisTemplate = getRedisTemplate();
redisTemplate.delete(key);
return null;
}
public void clear() {
logger.debug("=====================================clear redis================================");
RedisTemplate redisTemplate = getRedisTemplate();
redisTemplate.execute(new RedisCallback() {
@Override
public Object doInRedis(RedisConnection connection) throws DataAccessException {
大专栏 redis实现二级缓存
connection.flushDb();
return "OK";
}
});
}
@Override
public int getSize() {
return 0;
}
@Override
public ReadWriteLock getReadWriteLock() {
return readWriteLock;
}
public RedisTemplate getRedisTemplate() {
if (redisTemplate == null) {
redisTemplate = ApplicationContextHolder.getBean("redisTemplate");
}
return redisTemplate;
}
public void setRedisTemplate(RedisTemplate redisTemplate) {
this.redisTemplate = redisTemplate;
}
}

这里需要用到redisTemplate,一开始我们初始化的是空的redisTemplate,这样的话redisTemplate.opsForValue()就是一个空指针异常,所以我们这里要getBean来获取,所以这里有一个ApplicationContextHolder工具类。

ApplicationContextHolder.java:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
@Component
public class ApplicationContextHolder implements ApplicationContextAware {
private static ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext ctx) throws BeansException {
applicationContext = ctx;
}
/**
* Get application context from everywhere
*
* @return
*/
public static ApplicationContext getApplicationContext() {
return applicationContext;
}
/**
* Get bean by class
*
* @param clazz
* @param <T>
* @return
*/
public static <T> T getBean(Class<T> clazz) {
return applicationContext.getBean(clazz);
}
/**
* Get bean by class name
*
* @param name
* @param <T>
* @return
*/
@SuppressWarnings("unchecked")
public static <T> T getBean(String name) {
return (T) applicationContext.getBean(name);
}
}
二级缓存的使用

在mapper中添加自定义cache:

1
<cache type="com.yif.utils.MybatisRedisCache" eviction="LRU"/>

其中的eviction有4个不同的参数:

LRU :最近最少使用的:移除最长时间不被使用的对象
FIFO:先进先出:按进入缓存的顺序来移除他们
SOFT:软引用:移除基于垃圾回收器状态和软引用规则的对象
WEAK:弱引用:更积极的移除基于垃圾收集器状态和弱引用规则的对象

测试

测试前先在数据库中插入几条数据,用户测试二级缓存,其实二级缓存就是在原先完整的数据库操作程序中添加了一个自定义cache并在数据库mapper中引入使用。
1.启动redis,查看redis中的keys,可以看到现在是空的
redis实现二级缓存第1张
2.启动程序,首先会运行MybatisRedisCache中的构造函数,在日志中看到id=com.yif.redis.model.User,即你的实例

3.运行查询步骤,MybatisRedisCache的运行顺序是:
第一次查询:
getObject()–>putObject()–>数据库查询
redis实现二级缓存第2张
再看看redis中的keys,可以看出已经将存入了一个新的key
redis实现二级缓存第3张
第二次相同的查询:
getObject()
redis实现二级缓存第4张
这样就可以看出第一次查询是从数据库中查出然后将结果存入缓存中,第二次相同的查询是从缓存中就查出了,不再通过数据库查询。

注意:

在mapper文件中,主要两个参数:flushCache和useCache:

在select中,flushCache默认是false,不会清除本地缓存和二级缓存;useCache默认为true,表示进行二级缓存
在update、insert和delete中,flushCache默认为true,会清空本地缓存和二级缓存;而useCache在此是不存在的

所以在update语句中加上flushCache后更新数据库会清除redis的二级缓存,这样在进行下一次查询时,从redis中获取的便是自动更新后的数据(会将数据库的数据put到redis中),这里还有一个情况,也是自己遇到的。网上有很多资料写着MybatisRedisCache中的clear()方法是无用的,所以很多都没有写,这里我也没写,不过这样的话更新后就不会自动更新,所以自己又写了clear()方法,在数据库更新之后便会执行这个clear()方法。

免责声明:文章转载自《redis实现二级缓存》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇运行bat时隐藏cmd窗口js引用类型赋值不改变原对象值下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

解决redis显示中文为乱码问题

如果你是用的windows cmd,还是乱码,要设置窗口的编码 输入:chcp 65001 就是换成UTF-8代码页,在命令行标题栏上点击右键,选择"属性"->"字体",将字体修改为True Type字体"Lucida Console",然后点击确定将属性应用到当前窗口 C:Redis>redis-cli.exe --raw -h 10.*.3...

删除 Dblink 报错 ORA02024: database link not found 的解决方法

  朋友说删除DBLINK 报错:ORA-02024: database link not found。   在Metalink上搜到了2篇与这个错误有关的文章:        Cannot drop a database link after changing the global_name ORA-02024 [ID 382994.1]       ...

MyBatis魔法堂:Insert操作详解(返回主键、批量插入)

  一、前言                                      数据库操作怎能少了INSERT操作呢?下面记录MyBatis关于INSERT操作的笔记,以便日后查阅。 二、 insert元素 属性详解                               其属性如下:    parameterType ,入参的全限定类名或类型...

云上RDS架构

概述 越来越多的企业选择上云,最基础的云服务就是IaaS(Infrastructure as a Service)服务,直观理解就是虚拟主机,用户不用再自建机房,自己购买服务器,而是直接向云厂商购买虚拟主机服务ECS(Elastic Compute Service),按时按量付费。对于数据库而言,将数据库能力集成进来,就是DaaS(Database as...

老技术记录-C#+SqlServer使用SqlDependency监听数据库表变化

开发环境: .net / C# (.net core理论上也可以) 数据库:MS SQL Server 2005 以上 (我用的sqlserver2012) 功能:SqlDependency提供了一种机制,当被监听的数据库中的数据发生变化时,SqlDependency会自动触发OnChange事件来通知应用程序,从而达到让应用程序实时更新数据(或缓存)的目...

Oracle Data Guard

DG是 Oracle Data Guard的简称。也就是Oracle11g的 数据卫士。 由于在工作中 Oracle和 SQL SERVER2008 同时都需要维护管理。给我的感觉这里的 DG 其实和 Sql Server 2008的镜像实现的功能是一样的(当然更强大一些)。DG 中的物理备用库 尤其和 Sql Server 2008 的镜像实现的功能是一...