【SpringBoot 2學習筆記】《十二》SpringBoot2緩存之旅Redis探祕

12.1 前提

Redis安裝:https://blog.csdn.net/gavinbj/article/details/104098730

代碼基於SpringCache的代碼:https://blog.csdn.net/gavinbj/article/details/104310547

DB訪問代碼自動生成參考:https://blog.csdn.net/gavinbj/article/details/104024890

12.2 Redis簡介

Redis是一個高性能的key-value緩存存儲數據庫。完全開源免費的,遵守BSD協議。Redis產品有以下三個特點:

  • Redis支持數據的持久化,可以將內存中的數據保存在磁盤中,重啓的時候可以再次加載進行使用。
  • Redis不僅僅支持簡單的key-value類型的數據,同時還提供list,set,zset,hash等數據結構的存儲。
  • Redis支持數據的備份,即master-slave模式的數據備份。
  • 性能極高 – Redis能讀的速度是110000次/s,寫的速度是81000次/s 。
  • 豐富的數據類型 – Redis支持二進制案例的 Strings, Lists, Hashes, Sets 及 Ordered Sets 數據類型操作。
  • 原子行:Redis的所有操作都是原子性的,意思就是要麼成功執行要麼失敗完全不執行。單個操作是原子性的。多個操作也支持事務,即原子性,通過MULTI和EXEC指令包起來。
  • 其他特性:Redis還支持 publish/subscribe, 通知, key 過期等等特性。

12.3 配置文件(POM和Properties)

pom.xml中添加關於Redis的依賴。

	<dependency>
	    <groupId>org.springframework.boot</groupId>
	    <artifactId>spring-boot-starter-data-redis</artifactId>
	</dependency>

配置文件添加Redis配置項目:application.properties

# Redis 配置項目
# Redis數據庫索引(默認爲0)
spring.redis.database=0
# Redis服務器連接密碼(默認爲空)
spring.redis.password=HaoJT1980
# Redis服務器地址
spring.redis.host=119.3.155.121
# Redis服務器連接端口
spring.redis.port=6379
# 鏈接超時時間 單位 ms(毫秒)
spring.redis.timeout=3000

#連接池中最大空閒鏈接,默認值是8
spring.redis.jedis.pool.max-idle=20
#連接池中最小空閒鏈接,默認是0
spring.redis.jedis.pool.min-idle=10
## 如果賦值爲-1,則表示不限制
spring.redis.jedis.pool.max-active=200
#等待連接的最長時間
spring.redis.jedis.pool.max-wait=1000

12.4 應用代碼

Controller層和前面一樣不在贅述,這裏只給出改變的業務邏輯層代碼。

package com.gavinbj.confmng.service.impl;

import java.util.List;
import java.util.concurrent.TimeUnit;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import com.gavinbj.confmng.persistence.entity.UserInfo;
import com.gavinbj.confmng.persistence.entity.UserInfoExample;
import com.gavinbj.confmng.persistence.mapper.UserInfoMapper;
import com.gavinbj.confmng.service.UserInfoService;

/**
 * 用戶信息
 * 
 * @author gavinbj
 *
 */
@Service
public class UserInfoServiceRedisImpl implements UserInfoService {

	@Autowired 
	private UserInfoMapper userInfoMapper;
	
	@Autowired
    private RedisTemplate<Object, Object> redisTemplate;
	
	/**
	 * 根據用戶ID進行主鍵檢索
	 */
	@Override
	public UserInfo getUserByPK(String userId) {
		UserInfo user = (UserInfo)redisTemplate.opsForValue().get(userId);
		if(user == null) {
			user = this.userInfoMapper.selectByPrimaryKey(userId);
		}
		if (user != null) {
			redisTemplate.opsForValue().set(user.getUserId(), user, 100L, TimeUnit.SECONDS);			
		}
		return user;
	}
	
	/**
	 * 保存用戶信息
	 * 
	 */
	@Override
	public UserInfo saveUserInfo(UserInfo user) {
		UserInfo userExist = this.userInfoMapper.selectByPrimaryKey(user.getUserId());
		if(userExist == null) {
			// 該用戶ID不存在可以插入用戶
			this.userInfoMapper.insertSelective(user);
			redisTemplate.opsForValue().set(user.getUserId(), user, 100L, TimeUnit.SECONDS);
		}else {
			throw new SysException(EnumCodeMsg.USER_EXIST, "用戶信息");
		}
		return user;
	}
	
	
	/**
	 * 刪除用戶信息
	 * 
	 */
	@Override
	public void delUserInfo(String userId) {
	
		this.userInfoMapper.deleteByPrimaryKey(userId);
        // 同步刪除Redis中的該用戶信息
		redisTemplate.delete(userId);
	}

}

12.5 驗證緩存

1、驗證保存數據到Redis

驗證工具:Postman (UserInfoServiceImpl.saveUserInfo)

POST
http://localhost:9003/gavin/api/users
JSON Body:
{
	"userId" : "lijing",
	"userName" : "李靜",
	"introduce" : "美女主播",
	"mobilephone" : "13948474647",
	"email": "[email protected]"
}

執行結果:

{
    "status": 0,
    "code": 1003,
    "msg": "處理成功!",
    "data": {
        "userId": "lijing",
        "userName": "李靜",
        "introduce": "美女主播",
        "mobilephone": "13948474647",
        "email": "[email protected]",
        "birthday": null,
        "gender": null
    }
}

控制檯輸出:

2020-02-15 10:10:29.403  INFO 18812 --- [nio-9003-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/gavin]  : Initializing Spring DispatcherServlet 'dispatcherServlet'
2020-02-15 10:10:30.130 DEBUG 18812 --- [nio-9003-exec-1] c.g.c.p.m.U.selectByPrimaryKey           : ==>  Preparing: select user_id, user_name, introduce, mobilephone, email, birthday, gender from user_info where user_id = ? 
2020-02-15 10:10:30.138 DEBUG 18812 --- [nio-9003-exec-1] c.g.c.p.m.U.selectByPrimaryKey           : ==> Parameters: lijing(String)
2020-02-15 10:10:30.295 DEBUG 18812 --- [nio-9003-exec-1] c.g.c.p.m.U.selectByPrimaryKey           : <==      Total: 0
2020-02-15 10:10:30.319 DEBUG 18812 --- [nio-9003-exec-1] c.g.c.p.m.U.insertSelective              : ==>  Preparing: insert into user_info ( user_id, user_name, introduce, mobilephone, email ) values ( ?, ?, ?, ?, ? ) 
2020-02-15 10:10:30.352 DEBUG 18812 --- [nio-9003-exec-1] c.g.c.p.m.U.insertSelective              : ==> Parameters: lijing(String), 李靜(String), 美女主播(String), 13948474647(String), [email protected](String)
2020-02-15 10:10:30.457 DEBUG 18812 --- [nio-9003-exec-1] c.g.c.p.m.U.insertSelective              : <==    Updates: 1

此時在數據庫中已經保存了該數據,接下來我們用檢索數據的方式,驗證數據是直接從緩存取得相應數據。因爲我們開啓了SQL執行日誌的打印,所以,如果檢索時,有沒有打印輸出檢索SQL使我們檢驗是否走緩存的依據。

GET
http://localhost:9003/gavin/api/users/lijing

執行結果:

{
    "status": 0,
    "code": 1003,
    "msg": "處理成功!",
    "data": {
        "userId": "lijing",
        "userName": "李靜",
        "introduce": "美女主播",
        "mobilephone": "13948474647",
        "email": "[email protected]",
        "birthday": "",
        "gender": ""
    }
}

此時看控制檯,沒有輸出新的檢索SQL,可以驗證本次數據檢索直接從Redis數據區中取得,在上次請求中除了將用戶信息保存到數據庫外,Redis數據庫中也緩存了相應的用戶數據。

2、驗證清除緩存

基於第一步中,我們在向數據庫保存用戶信息時,同時將該數據保存到了Redis數據庫中。現在,我們調用刪除用戶信息方法(該方法中通過使用redisTemplate.delete來刪除指定關鍵字的緩存內容),我們看看是否同時將緩存的用戶信息刪除掉。驗證步驟如下:

  • 調用用戶刪除方法

  • 調用用戶信息查詢

如果緩存刪除後,在調用用戶信息查詢,應該去數據庫檢索,控制檯能夠輸出相應的檢索語句。

DELETE
http://localhost:9003/gavin/api/users/lijing

執行結果

{
    "status": 0,
    "code": 1003,
    "msg": "處理成功!",
    "data": "OK"
}

控制檯信息:

2020-02-15 10:15:03.273 DEBUG 18812 --- [nio-9003-exec-6] c.g.c.p.m.U.deleteByPrimaryKey           : ==>  Preparing: delete from user_info where user_id = ? 
2020-02-15 10:15:03.276 DEBUG 18812 --- [nio-9003-exec-6] c.g.c.p.m.U.deleteByPrimaryKey           : ==> Parameters: lijing(String)
2020-02-15 10:15:03.449 DEBUG 18812 --- [nio-9003-exec-6] c.g.c.p.m.U.deleteByPrimaryKey           : <==    Updates: 1

從上面可以看出,已經從數據庫刪除了數據並打印了刪除語句。

接下來執行檢索用的接口

GET
http://localhost:9003/gavin/api/users/lijing

執行結果

{
    "status": 1,
    "code": 5003,
    "msg": "用戶信息檢索結果爲空",
    "data": ""
}

我們看控制檯輸出:

2020-02-15 10:15:31.510 DEBUG 18812 --- [nio-9003-exec-7] c.g.c.p.m.U.selectByPrimaryKey           : ==>  Preparing: select user_id, user_name, introduce, mobilephone, email, birthday, gender from user_info where user_id = ? 
2020-02-15 10:15:31.512 DEBUG 18812 --- [nio-9003-exec-7] c.g.c.p.m.U.selectByPrimaryKey           : ==> Parameters: lijing(String)
2020-02-15 10:15:31.559 DEBUG 18812 --- [nio-9003-exec-7] c.g.c.p.m.U.selectByPrimaryKey           : <==      Total: 0

可見執行檢索時,沒有從緩存中區的數據,接下里直接檢索數據庫,數據庫檢索結果也沒有。所以返回用戶信息爲空。剛纔刪除Redis緩存也是正確的。

3、驗證檢索功能同時可以存儲緩存和查詢緩存

通過如上兩步,現在緩存中沒有緩存的用戶信息,我們此時執行兩次檢索方法,檢索一個數據庫中存在的用戶。第一次,因爲緩存中沒有該用戶,所以會輸出檢索用SQL。第二次在執行該用戶的檢索,此時應該直接使用緩存中的檢索結果,不去數據庫重新檢索,控制檯中不輸出SQL。

GET
http://localhost:9003/gavin/api/users/zhangsanfeng

連續執行兩次結果:

{
    "status": 0,
    "code": 1003,
    "msg": "處理成功!",
    "data": {
        "userId": "zhangsanfeng",
        "userName": "張三丰",
        "introduce": "一代宗師",
        "mobilephone": "13948474647",
        "email": "[email protected]",
        "birthday": "",
        "gender": ""
    }
}

控制檯輸出:

2020-02-15 10:18:49.607 DEBUG 18812 --- [nio-9003-exec-8] c.g.c.p.m.U.selectByPrimaryKey           : ==>  Preparing: select user_id, user_name, introduce, mobilephone, email, birthday, gender from user_info where user_id = ? 
2020-02-15 10:18:49.608 DEBUG 18812 --- [nio-9003-exec-8] c.g.c.p.m.U.selectByPrimaryKey           : ==> Parameters: zhangsanfeng(String)
2020-02-15 10:18:49.655 DEBUG 18812 --- [nio-9003-exec-8] c.g.c.p.m.U.selectByPrimaryKey           : <==      Total: 1

從上可以看出第一次檢索zhangsanfeng時,直接檢索了數據庫。第二次檢索使用了緩存,沒有打印出SQL日誌。

以上是基本的緩存使用的例子,旨在讓大家瞭解入門,實際項目中使用緩存的場景比較複雜,這裏不做分析,各位根據實際項目進行適當的研究學習。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章