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