SpringBoot(七) - Redis 快取
阿新 • • 發佈:2022-12-01
1、五大基本資料型別和操作
1.1 字串-string
命令 | 說明 |
---|---|
set key value | 如果key還沒有,那就可以新增,如果key已經存在了,那會覆蓋原有key的值 |
get key | 如果key還沒有,獲取為(nil),代表key沒有被使用,如果key存在,可以獲取對應key的值 |
exists key | 判斷某個key是否存在,返回Integer值1 代表存在,如果 exists car2 則返回0,不存在 |
move key db | 將當前資料庫存在的鍵值移動到其它資料庫,其中db是資料庫的序號 |
expire key 秒鐘 | 為已經存在的key設定過期時間,注意過期之後,從記憶體中去掉了,是get不到的 |
ttl key | 檢視還有多少秒過期,-1表示永不過期,-2表示已過期 |
type key | 命令用於返回 key 所儲存的值的型別 |
del key | 根據key值刪除 |
append key value | 根據key將其值進行字串拼接 |
strlen key | 根據key獲取其值的字串長度,位元組數 |
incr key | 對key對應數值進行加一操作,對應的字串值必須是數值 |
decr key | 對key對應數值進行減一操作 |
incrby key 數值 | 對key對應數值按照指定的值進行遞增 |
decrby key 數值 | 對key對應數值按照指定的值進行遞減 |
getrange key 起始位置 結束位置 | 獲取指定區間內的值,類似between。。。and的關係,起始位置為0,結束位置為-1 就是返回所有 |
setrange key 起始位置 具體值 | 設定指定區間內的值,具體值會從起始位置開始覆蓋 |
setex key 過期秒值 真實值 | 設定帶過期時間的key,動態設定。 |
setnx key value | 只有在 key 不存在時,才會設定 key 的值,如果已經存在了,不覆蓋,設定不了; |
setnx key value | 如果返回0 代表沒有設定成功,key對應值已經存在,如果返回1代表設定成功;這個就是redis的分散式鎖命令,很重要; |
mset key1 val1 key2 val2 .... | 同時設定一個或多個 key-value 對 |
mget key1 key2 key3 .... | 獲取所有(一個或多個)給定 key 的值。 |
msetnx key1 val1 key2 val2 ..... | 同時設定一個或多個 key-value 對,當且僅當所有給定 key 都不存在 |
1.2 列表-list
list操作起來類似於棧;
命令 | 說明 |
---|---|
lpush key val1 val2 val3 .... | 從左側開始存放元素,先進後出 |
lrange key 起始位置 結束位置 | 從左側開始,指定範圍獲取元素,-1代表所有 |
rpush key val1 val2 val3 .... | 從右側開始存放元素,先進先出 |
lpop key | 從左側一次取出一個元素 |
rpop key | 從右側一次取出一個元素 |
lindex key index | 按照索引下標獲得元素(從左到右,左下標從0開始,如果是-1代表最後一個,-2代表倒數第二個) |
llen key | 獲取集合元素個數 |
lrem key 個數 具體的值 | 從左往右刪除指定個數等於具體值的元素,返回的值為實際刪除的數量,個數0,表示刪除全部給定的值 |
ltrim key 開始index 結束index | 擷取指定範圍的值後再賦值給key |
rpoplpush 源列表 目的列表 | 移除列表的最後一個元素,並將該元素新增到另一個列表並返回 |
lset key index value | 將key集合中的指定下標位置值改為value |
linsert key before/after 值1 值2 | 在list某個已有 值1 的前後再新增具體 值2 |
小結:
- 它是一個字串連結串列,left、right都可以插入新增;
- 如果鍵不存在,建立新的連結串列;
- 如果鍵已存在,新增內容;
- 如果值全移除,對應的鍵也就消失了;
- 連結串列的操作無論是頭和尾效率都極高,但假如是對中間元素進行操作,效率就很慘淡了;
1.3 集合-set
命令 | 說明 |
---|---|
sadd key val1 val2 ... | 集合set中新增元素,如果有重複元素會自動去除 |
smembers key | 檢視集合中的元素 |
sismember key val | 判斷val是否在set集合中,如果在返回1 ,不在返回0 |
scard key | 獲取集合裡面的元素個數 |
srem key value | 刪除集合中元素 |
srandmember key 某個整數 | 隨機出幾個數,如果超過最大數量就全部取出 |
srandmember key 某個整數 | 如果寫的值是負數,比如-3 ,表示需要取出3個,但是可能會有重複值。 |
spop key | 隨機出棧 |
smove key1 key2 | 將key1裡的某個值賦給key2 |
sdiff key1 key2 | 在第一個set裡面而不在後面任何一個set裡面的項 |
sinter key1 key2 | 在兩個set中都有的值的交集返回 |
sunion key1 key2 | 在兩個set中所有的值的集合返回,會自動排除重複 |
1.4 鍵值對-hash
K V模式不變,但V是一個鍵值對;
命令 | 說明 |
---|---|
hset 父key 子key 子value | 將父key,增加子鍵值對,類似屬性 |
hget 父key 子key | 獲取父key,某個子key的值,獲取屬性值 |
hmset 父key 子key1 子val1 子key2 子val2 .... | 批量新增屬性 |
hmget 父key 子key1 子key... | 批量獲取屬性 |
hgetall 父key | 批量獲取屬性及值 |
hdel 父key 子key | 刪除子key屬性及值 |
hlen 父key | 返回父key中的子key個數,相當於java實體的屬性個數 |
hexists 父key 子key | 判斷父key中是否包含某個子key,結果為1,代表存在 |
hkeys 父key | 獲取父key中所有的子key |
hvals 父key | 獲取父key中的所有的子val |
hincrby 父key 子key 值 | 給指定的子key值增加固定的值 |
hincrbyfloat 父key 子key 值 | 給有指定key的值增加小數 |
hsetnx 父key 子key 子val | 如果子key存在則失敗,如果不存在則賦值 |
1.5 有序集合-zset
在set基礎上,加一個score值。之前set是k1 v1 v2 v3,現在zset是k1 score1 v1 score2 v2;
命令 | 說明 |
---|---|
zadd key score1 val1 score2 val2 score3 val3 ... | 有序集合新增帶score值的元素 |
zscore key val | 獲取集合中某個值對應score值 |
zrange key 0 -1 [withscores] | zrange zset1 0 -1 ,結果為所有的值,不帶分數;如:zrange zset1 0 -1 ,結果為所有的值,不帶分數 |
zrange zset1 0 -1 withscores | 結果為所有的值和分數 |
zrangebyscore key 開始score 結束score | 獲取score值在開始score-結束score之間的元素 |
zrangebyscore zset1 10 40 | 獲取score值在10-40之間的元素,包含10和40 |
zrangebyscore zset1 10 (40 | 不包含40值;( 的含義是不包含 |
zrangebyscore zset1 (10 (40 | 不包含10,40值 |
zrangebyscore zset1 10 50 limit 2 2 | limit 結果的起始下標,獲取的個數;limit 含義是限制獲取的條數,相當於mysql的分頁; |
zrem key 某score下對應的value值 | 刪除元素 |
zcard key | 獲取key對應的值的個數;注意score 和 value是一個整體 |
zcount key score區間 | 獲取分值區間內元素個數 |
zrank key values值 | 獲得下標值 |
zscore key 對應value值 | 獲得value對應分數 |
zrevrank key value值 | 逆序獲得對應逆序的下標值 |
zrevrange key 起始下標,結束下標 | 將之前順序進行倒序 |
zrevrangebyscore key 結束score 開始score | 根據score值輸出元素 |
zincrby key 增加分值 value值 | 給對應的值增加score值 |
2、Redis整合
2.1 spring-boot-starter-data-redis 依賴
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
2.2 redis配置
#埠號
server:
port: 8096
# redis配置
spring:
redis:
host: 127.0.0.1 #如果是redis遠端伺服器,此處redis伺服器ip地址
port: 6379 #預設埠
# database: 0 #指定redis資料庫,預設是0
# password: # 密碼有就寫,沒有就省略
2.3 SpringBoot框架自動配置的redisTemplate
2.3.1 清空資料庫
//自動裝配 SpringBoot框架自動配置的redisTemplate
@Autowired
private RedisTemplate<Object,Object> redisTemplate;
//基於SpringBoot框架自動配置的redisTemplate,操作redis快取
//獲取連線
RedisConnection connection = redisTemplate.getConnectionFactory().getConnection();
//清空資料庫中的所有資料
log.info("清空資料庫中的所有資料");
connection.flushDb();
2.3.2 新增資料
//程式中,新增資料據到redis
log.info("------ 基於SpringBoot框架自動配置的redisTemplate 新增資料 ------");
redisTemplate.opsForValue().set("kh96_class_name","KGC_KH96");
redisTemplate.opsForValue().set("student_num",19);
2.3.3 獲取資料
//程式中,從redis獲取資料
log.info("------ 基於SpringBoot框架自動配置的redisTemplate 獲取資料 ------");
log.info("****** 根據 班級的key:{},獲取班級名稱:{} ******","kh96_class_name",redisTemplate.opsForValue().get("kh96_class_name"));
log.info("****** 根據 班級的key:{},獲取班級人數:{} ******","student_num",redisTemplate.opsForValue().get("student_num"));
2.3.4 修改值 (出現錯誤)
//程式中,基於SpringBoot框架自動配置的redisTemplate,操作redis快取,存在問題
//場景:對班級人數進行增減操作,比如將班級人數,增加10
log.info("------ 基於SpringBoot框架自動配置的redisTemplate 操作資料 ------");
redisTemplate.opsForValue().increment("student_num",10);
//直接報錯,會報500異常: redis.clients.jedis.exceptions.JedisDataException: ERR value is not an integer or out of range
//原因,通過系統預設的 redisTemplate,存放key和value值時,會自動使用Object類的序列化和反序列化,導致redis中真實存放的資料不是原始值,而是序列化後的值
資料結果:
2.4 自定義redisTemplate
2.4.1 fastjson 依賴
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.62</version>
</dependency>
2.4.2 自定義redisTemplate 配置類
//Redis自定義配置類,實現一個自定義序列化方式的 redisTemplate,提緩緩掉預設自動配置的 redisTemplate,實現String型別任意型別的value
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
// 自定義redisTemplate的模板物件
RedisTemplate<String, Object> template = new RedisTemplate<>();
// 設定連線工廠
template.setConnectionFactory(redisConnectionFactory);
//由於要通過程式操作遠端的redis資料庫,必須支援序列化,才可以讓程式中的資料,在網路中傳輸
//定義String型別的序列化方式
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
// 定義fastjson序列化方式,可以序列化任何物件
FastJsonRedisSerializer<Object> fastJsonRedisSerializer = new FastJsonRedisSerializer<>(Object.class);
// 需改為新的序列化方式
template.setKeySerializer(stringRedisSerializer);
template.setValueSerializer(fastJsonRedisSerializer);
template.setHashKeySerializer(stringRedisSerializer);
template.setHashValueSerializer(fastJsonRedisSerializer);
// 初始化為新的模板
template.afterPropertiesSet();
return template;
}
}
2.4.3 使用自定義redisTemplate 重新操作資料
//自動裝配自定義 redisTemplate
@Autowired
private RedisTemplate<String,Object> redisTemplate;
//其他程式碼不變
操作結果:
2.5 自定義redisUtils工具類
2.5.1 自定義redisUtils工具類
---> RedisUtil 工具類
2.5.2 使用自定義redisTemplate和redisUtils工具類
@GetMapping("/testRedisUtils")
public String testSpringBootRedisUtils(){
//基於自定義的redisTemplate 和 RedisUtils 工具類,操作redis快取
//程式中,新增資料據到redis
log.info("------ 基於自定義的redisTemplate 和 RedisUtils 工具類 新增資料 ------");
redisUtils.set("kh96_class_name_utils","KGC_KH96");
redisUtils.set("student_num_utils",19);
//程式中,從redis獲取資料
log.info("------ 基於自定義的redisTemplate 和 RedisUtils 工具類 獲取資料 ------");
log.info("****** 根據 班級的key:{},獲取班級名稱:{} ******","kh96_class_name_utils",redisUtils.get("kh96_class_name_utils"));
log.info("****** 根據 班級的key:{},獲取班級人數:{} ******","student_num_utils",redisUtils.get("student_num_utils"));
//程式中,基於SpringBoot框架自動配置的redisTemplate,操作redis快取
//場景:對班級人數進行增減操作,比如姜班級人數,增加10
log.info("------ 基於自定義的redisTemplate 和 RedisUtils 工具類 操作資料 ------");
redisUtils.incr("student_num_utils",10);
return "工具類 RedisUtils 操作 redis 成功!";
}
2.5.3 程式中如何存放物件到 redis
核心思想:一般都是姜物件轉換為json字串,存入redis,獲取物件資料,就先獲取json字串,再轉換為對應物件即可;
@GetMapping("/testRedisUtils")
public String testSpringBootRedisUtils(){
//程式中如何存放物件到 redis
//核心思想:一般都是姜物件轉換為json字串,存入redis,獲取物件資料,就先獲取json字串,再轉換為對應物件即可
//模擬使用者登入成功後,將使用者資訊存入redis中,方便後續從redis中獲取使用者資訊
User loginUser = User.builder().userId(1001).userName("KH96").userTel("135012030404").build();
//直接將物件存入redis即可
log.info("------ 基於自定義的redisTemplate 和 RedisUtils 工具類 儲存物件 ------");
//自動把實體,通過fastjson的序列化方式,轉發為JSON字串儲存
redisUtils.set(loginUser.getUserId().toString(),loginUser);
//模擬獲取登入使用者資訊,直接從redis獲取存入的JSON字串,轉換為目標使用者物件
User realUser = JSON.parseObject(redisUtils.get(loginUser.getUserId().toString()).toString(),User.class);
log.info("------ 基於自定義的redisTemplate 和 RedisUtils 工具類獲取物件:{} ",realUser);
return "工具類 RedisUtils 操作 redis 成功!";
}
資料結果:
3、練習
3.1 題目要求
實現商品評論點贊功能,要限制次數
1)功能:商品評論點贊,只能有一個介面,第一次請求是增加點贊,第二次請求就是取消點贊
2)限制:使用redis增加操作限制,點贊不能太頻繁,比如:限制5s內最多點選4次,如果沒有超出限制,可以正常操作,如果超出限制,返回提示:操作過於頻繁,請稍後重試!
統一使用map做返回結果,內容必須包含:code:狀態碼,自定義,msg: success/fail,data:返回的資料內容
比如:獲取驗證碼返回:
{
"code": 200,
"msg": "success",
"data": "手機號:xxxx,驗證碼:xxxx"
}
3.2 程式碼
/**
* @author : huayu
* @date : 19/10/2022
* @param : []
* @return : java.util.Map<java.lang.String,java.lang.Object>
* @description : 點贊操作
* 第一次請求是增加點贊,第二次請求就是取消點贊
* 限制5s內最多點選4次,如果沒有超出限制,可以正常操作,如果超出限制,返回提示:操作過於頻繁,請稍後重試!
*/
@GetMapping("/praise")
public Map<String,Object> praise(){
Map<String,Object> reMsg = new HashMap<>();
reMsg.put("code","200");
reMsg.put("msg","success");
//判斷是不是第一次點選
if(redisUtils.get("praiseFlag") == null){
//第一點選, 設定 praiseFlag
redisUtils.set("praiseFlag",0);
}
//判斷 是否有五秒限制
if(redisUtils.get("time") == null){
//第一次點選,或已經超過5秒
//新增 記錄點選 次數
redisUtils.set("time",1,5);
}else {
//5秒內的操作
//判斷 5秒中點選的次數 等於 4
if((Integer)redisUtils.get("time") == 4){
reMsg.put("code","500");
reMsg.put("msg","fail");
reMsg.put("data","操作過於頻繁,請稍後重試!");
//返回資訊
return reMsg;
}else {
//5秒內的 點選次數沒有大於4次
redisUtils.incr("time",1);
}
}
//判斷上一次是點贊還是取消點贊
if((Integer)redisUtils.get("praiseFlag") == 1){
//取消點贊操作
redisUtils.set("praiseFlag",0);
reMsg.put("data", "取消點贊成功!");
}else {
//點贊操作
redisUtils.set("praiseFlag",1);
reMsg.put("data", "點贊成功!");
}
//返回資訊
return reMsg;
}
3.3 測試結果
3.3.1 第一次點選(五秒內)
3.3.2 第二次點選(五秒內)
3.3.3 第三次點選(五秒內)
3.3.4 第四次點選(五秒內)
3.3.5 第五次點選(五秒內)
4、瀏覽記錄 練習
4.1 請求方法
@Autowired
RedisTemplate<String,Object> redisTemplate;
// 瀏覽記錄,最新的瀏覽記錄放在上面,最多展示10個,使用zset做,搜尋關鍵字的score值使用日期毫秒,倒敘
@GetMapping("/addtHistory")
public RequestResult<Set<Object>> addtHistory(String uId,String pId){
//獲取zset操作物件
ZSetOperations<String, Object> opsForZSet = redisTemplate.opsForZSet();
//放入記錄
opsForZSet.add(uId,pId,System.currentTimeMillis());
Set<Object> historySet = null;
//獲取記錄數量
int historiesCount = opsForZSet.size(uId).intValue();
//判斷否有10個記錄
if(historiesCount >= 10) {
historySet = opsForZSet.reverseRange(uId,0, 9);
return ResultBuildUtil.success(historySet);
}else {
historySet = opsForZSet.rangeByScore(uId, 0, historiesCount);
return ResultBuildUtil.success(historySet);
}
}