redis實現二級緩存


緩存的作用就是降低數據庫的使用率,來減輕數據庫的負擔。我們平常的操作一般都是查>改,所以數據庫的有些查操作是重復的,如果一直使用數據庫就會有負擔。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,可以看到現在是空的

2.啟動程序,首先會運行MybatisRedisCache中的構造函數,在日志中看到id=com.yif.redis.model.User,即你的實例

3.運行查詢步驟,MybatisRedisCache的運行順序是:
第一次查詢:
getObject()–>putObject()–>數據庫查詢

再看看redis中的keys,可以看出已經將存入了一個新的key

第二次相同的查詢:
getObject()

這樣就可以看出第一次查詢是從數據庫中查出然后將結果存入緩存中,第二次相同的查詢是從緩存中就查出了,不再通過數據庫查詢。

注意:

在mapper文件中,主要兩個參數:flushCache和useCache:

在select中,flushCache默認是false,不會清除本地緩存和二級緩存;useCache默認為true,表示進行二級緩存
在update、insert和delete中,flushCache默認為true,會清空本地緩存和二級緩存;而useCache在此是不存在的

所以在update語句中加上flushCache后更新數據庫會清除redis的二級緩存,這樣在進行下一次查詢時,從redis中獲取的便是自動更新后的數據(會將數據庫的數據put到redis中),這里還有一個情況,也是自己遇到的。網上有很多資料寫着MybatisRedisCache中的clear()方法是無用的,所以很多都沒有寫,這里我也沒寫,不過這樣的話更新后就不會自動更新,所以自己又寫了clear()方法,在數據庫更新之后便會執行這個clear()方法。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM