SpringBoot整合Redis實現(xiàn)高并發(fā)數(shù)據(jù)緩存的示例講解 天天頭條
時間:2023-06-24 16:03:49
目錄
什么是緩存為什么要用緩存Redis為什么這么快實現(xiàn)一個用戶信息的緩存方式一:利用RedisTemplate實現(xiàn) 導(dǎo)入依賴添加配置添加redis工具類及配置類service層controller層測試方式二:采用SpringBoot注解開啟緩存修改service層實現(xiàn)類代碼修改RedisConfig配置類什么是緩存
緩存是?個高速數(shù)據(jù)交換的存儲器,使用它可以快速的訪問和操作數(shù)據(jù)。
舉個通俗的例子。
小明經(jīng)營著一家飯店,在剛開張的時候由于名氣不足,客源少,生意并不是很忙,平時沒事的時候就閑著,有客人來了再進廚房安排做菜。隨著飯店的日益發(fā)展,此時的飯店已經(jīng)不同往日,有著大量的穩(wěn)定客源,并且在某些節(jié)假日的時候甚至爆滿。按照以前的做法,那肯定是行不通了,在用餐高峰期的時候因為備餐慢導(dǎo)致了客戶的長時間等待,使得飯店的屢遭投訴。
為解決這一問題,小明想到了一個辦法,可以在空閑的時候,提前將熱門的菜做完后放入保溫柜,等用餐高峰期時再拿出來加熱后就可以直接上菜,就規(guī)避了短時間內(nèi)大量客源而導(dǎo)致的備餐慢的問題,通過這一方法,即使在高峰期,也能很好的應(yīng)對。
(資料圖片)
這就是緩存的本質(zhì),將熱點資源(高頻讀、低頻寫)提前放入離用戶最近、訪問速度更快的地方,以提高訪問速度。
為什么要用緩存
使用緩存后,效率會大大的提升,減少了不必要的資源消耗,提升了用戶體驗。
redis的特點:
redis支持?jǐn)?shù)據(jù)的持久化,可以將內(nèi)存中的數(shù)據(jù)保存在磁盤中,重啟的時候可以再次加在進行使用。redis不僅僅支持簡單的key-value類型的數(shù)據(jù),同時還提供list,set,zset,hash等數(shù)據(jù)結(jié)構(gòu)的儲存redis支持?jǐn)?shù)據(jù)的備份,即master-slave模式的數(shù)據(jù)備份redis的優(yōu)勢:
性能極高——redis能讀得的速度是110000次/s,寫的速度是81000次/s。豐富的數(shù)據(jù)類型——redis支持二進制案例的Strings, Lists, Hashes, Sets 及 Ordered Sets 數(shù)據(jù)類型操作。原子——redis的所有操作都是原子性的,意思就是要么成功執(zhí)行要么失敗完全不執(zhí)行。單個操作是原子性的。多個操作也支持事務(wù),即原子性,通過multi和exec指令包起來。豐富的特性redis還支持publish/subscribe,通知,key過期等等特性Redis為什么這么快
(1)完全基于內(nèi)存,數(shù)據(jù)存在內(nèi)存中,絕大部分請求是純粹的內(nèi)存操作,非??焖?,跟傳統(tǒng)的磁盤文件數(shù)據(jù)存儲相比,避免了通過磁盤IO讀取到內(nèi)存這部分的開銷。
(2)數(shù)據(jù)結(jié)構(gòu)簡單,對數(shù)據(jù)操作也簡單。Redis中的數(shù)據(jù)結(jié)構(gòu)是專門進行設(shè)計的,每種數(shù)據(jù)結(jié)構(gòu)都有一種或多種數(shù)據(jù)結(jié)構(gòu)來支持。Redis正是依賴這些靈活的數(shù)據(jù)結(jié)構(gòu),來提升讀取和寫入的性能。
(3)采用單線程,省去了很多上下文切換的時間以及CPU消耗,不存在競爭條件,不用去考慮各種鎖的問題,不存在加鎖釋放鎖操作,也不會出現(xiàn)死鎖而導(dǎo)致的性能消耗。
(4)使用基于IO多路復(fù)用機制的線程模型,可以處理并發(fā)的鏈接。
實現(xiàn)一個用戶信息的緩存
數(shù)據(jù)庫表結(jié)構(gòu):
CREATE TABLE `blade_user` ( `id` bigint(20) NOT NULL COMMENT "主鍵", `tenant_id` varchar(12) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT "000000" COMMENT "租戶ID", `code` varchar(12) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT "用戶編號", `user_type` int(11) DEFAULT NULL COMMENT "用戶平臺", `account` varchar(45) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT "賬號", `password` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT "密碼", `name` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT "昵稱", `real_name` varchar(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT "真名", `avatar` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT "頭像", `email` varchar(45) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT "郵箱", `phone` varchar(45) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT "手機", `birthday` datetime DEFAULT NULL COMMENT "生日", `sex` int(11) DEFAULT NULL COMMENT "性別", `role_id` varchar(1000) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT "角色id", `dept_id` varchar(1000) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT "部門id", `post_id` varchar(1000) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT "崗位id", `create_user` bigint(20) DEFAULT NULL COMMENT "創(chuàng)建人", `create_dept` bigint(20) DEFAULT NULL COMMENT "創(chuàng)建部門", `create_time` datetime DEFAULT NULL COMMENT "創(chuàng)建時間", `update_user` bigint(20) DEFAULT NULL COMMENT "修改人", `update_time` datetime DEFAULT NULL COMMENT "修改時間", `status` int(11) DEFAULT NULL COMMENT "狀態(tài)", `is_deleted` int(11) DEFAULT "0" COMMENT "是否已刪除", PRIMARY KEY (`id`) USING BTREE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC COMMENT="用戶表";
方式一:利用RedisTemplate實現(xiàn) 導(dǎo)入依賴
完整pom.xml文件:
4.0.0 org.springframework.boot spring-boot-starter-parent 2.7.8 com.redis.demo springboot-redis 0.0.1-SNAPSHOT springboot-redis Demo project for Spring Boot 1.8 org.springframework.boot spring-boot-starter-web org.projectlombok lombok true org.springframework.boot spring-boot-starter-test test com.baomidou mybatis-plus-boot-starter 3.4.2 org.springframework.boot spring-boot-starter-data-redis mysql mysql-connector-java 8.0.15 cn.hutool hutool-all 5.8.5 com.alibaba fastjson 1.2.41 org.springframework.boot spring-boot-starter-cache org.springframework.boot spring-boot-maven-plugin org.projectlombok lombok
添加配置
application.yml文件:
server:
port: 8081
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://3.129.36.183:3306/test?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=UTF-8
username: root
password: root
#redis
redis:
host: 3.129.36.183
#Redis服務(wù)器連接端口
port: 6379
#Redis服務(wù)器連接密碼
password: 123456
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl #開啟sql日志
# 將帶有下劃線的表字段映射為駝峰格式的實體類屬性
map-underscore-to-camel-case: true
#配置類型別名所對應(yīng)的包
type-aliases-package: com.redis.demo.entity
#配置SQL輸出語句com.winsun.dataclean.mapper
mapper-locations: com/redis/demo/dao/*.xml
添加redis工具類及配置類
RedisUtils:
package com.redis.demo.utils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
/**
* Redis工具類
*
* @author
*/
@Component
public class RedisUtils {
@Autowired
private RedisTemplate redisTemplate;
// =============================common============================
/**
* 指定緩存失效時間
*
* @param key 鍵
* @param time 時間(秒)
* @return
*/
public boolean expire(String key, long time) {
try {
if (time > 0) {
redisTemplate.expire(key, time, TimeUnit.SECONDS);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 根據(jù)key 獲取過期時間
*
* @param key 鍵 不能為null
* @return 時間(秒) 返回0代表為永久有效
*/
public long getExpire(String key) {
return redisTemplate.getExpire(key, TimeUnit.SECONDS);
}
/**
* 判斷key是否存在
*
* @param key 鍵
* @return true 存在 false不存在
*/
public boolean hasKey(String key) {
try {
return redisTemplate.hasKey(key);
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 刪除緩存
*
* @param key 可以傳一個值 或多個
*/
@SuppressWarnings("unchecked")
public void del(String... key) {
if (key != null && key.length > 0) {
if (key.length == 1) {
redisTemplate.delete(key[0]);
} else {
redisTemplate.delete(CollectionUtils.arrayToList(key));
}
}
}
// ============================String=============================
/**
* 普通緩存獲取
*
* @param key 鍵
* @return 值
*/
public Object get(String key) {
return key == null ? null : redisTemplate.opsForValue().get(key);
}
/**
* 普通緩存放入
*
* @param key 鍵
* @param value 值
* @return true成功 false失敗
*/
public boolean set(String key, Object value) {
try {
redisTemplate.opsForValue().set(key, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 普通緩存放入并設(shè)置時間
*
* @param key 鍵
* @param value 值
* @param time 時間(秒) time要大于0 如果time小于等于0 將設(shè)置無限期
* @return true成功 false 失敗
*/
public boolean set(String key, Object value, long time) {
try {
if (time > 0) {
redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
} else {
set(key, value);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 遞增
*
* @param key 鍵
* @param delta 要增加幾(大于0)
* @return
*/
public long incr(String key, long delta) {
if (delta < 0) {
throw new RuntimeException("遞增因子必須大于0");
}
return redisTemplate.opsForValue().increment(key, delta);
}
/**
* 遞減
*
* @param key 鍵
* @param delta 要減少幾(小于0)
* @return
*/
public long decr(String key, long delta) {
if (delta < 0) {
throw new RuntimeException("遞減因子必須大于0");
}
return redisTemplate.opsForValue().increment(key, -delta);
}
// ================================Map=================================
/**
* HashGet
*
* @param key 鍵 不能為null
* @param item 項 不能為null
* @return 值
*/
public Object hget(String key, String item) {
return redisTemplate.opsForHash().get(key, item);
}
/**
* 獲取hashKey對應(yīng)的所有鍵值
*
* @param key 鍵
* @return 對應(yīng)的多個鍵值
*/
public MapRedisConfig:
package com.redis.demo.config;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.redis.demo.utils.MapUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializer;
import javax.annotation.PostConstruct;
import java.util.Map;
/**
* @Author: laz
* @CreateTime: 2023-02-20 11:55
* @Version: 1.0
*
* 序列化
*/
@Configuration
public class RedisConfig {
@Autowired
private RedisTemplate redisTemplate;
@PostConstruct
public void init() {
initRedisTemplate();
}
private void initRedisTemplate() {
RedisSerializer stringSerializer = redisTemplate.getStringSerializer();
redisTemplate.setKeySerializer(stringSerializer);
redisTemplate.setHashKeySerializer(stringSerializer);
redisTemplate.setValueSerializer(stringSerializer);
redisTemplate.setHashValueSerializer(stringSerializer);
}
}
開發(fā)mapper接口
package com.redis.demo.dao; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.redis.demo.entity.BladeUser; /** ** 用戶表 Mapper 接口 *
* * @author laz * @since 2023-03-09 */ public interface BladeUserMapper extends BaseMapper{ }
service層
IBladeUserService:
package com.redis.demo.service; import com.baomidou.mybatisplus.extension.service.IService; import com.redis.demo.entity.BladeUser; import com.redis.demo.result.DealResult; /** ** 用戶表 服務(wù)類 *
* * @author laz * @since 2023-03-09 */ public interface IBladeUserService extends IService{ DealResult getById(Long id); }
BladeUserServiceImpl:
package com.redis.demo.service.impl; import cn.hutool.core.bean.BeanUtil; import cn.hutool.json.JSONUtil; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.redis.demo.constant.RedisConstants; import com.redis.demo.dao.BladeUserMapper; import com.redis.demo.entity.BladeUser; import com.redis.demo.result.DealResult; import com.redis.demo.service.IBladeUserService; import com.redis.demo.status.CacheNameStatus; import com.redis.demo.utils.RedisUtils; import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cache.annotation.Cacheable; import org.springframework.stereotype.Service; import org.springframework.util.ObjectUtils; /** ** 用戶表 服務(wù)實現(xiàn)類 *
* * @author laz * @since 2023-03-09 */ @Service public class BladeUserServiceImpl extends ServiceImplimplements IBladeUserService { @Autowired private RedisUtils redisUtils; @Override public DealResult getById(Long id) { String userKey = RedisConstants.CACHE_USER_KEY+id; Object user = redisUtils.get(userKey); if (!ObjectUtils.isEmpty(user)){ return DealResult.data(JSONUtil.toBean(JSONUtil.toJsonStr(user),BladeUser.class)); } BladeUser bladeUser = baseMapper.selectById(id); redisUtils.set(userKey, JSON.toJSONString(bladeUser)); return DealResult.data(bladeUser); } }
controller層
package com.redis.demo.controller; import com.redis.demo.result.DealResult; import com.redis.demo.service.IBladeUserService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RestController; /** ** 用戶表 前端控制器 *
* * @author laz * @since 2023-03-09 */ @RestController @RequestMapping("/bladeUser") public class BladeUserController { @Autowired private IBladeUserService bladeUserService; @RequestMapping("getById/{id}") public DealResult getById(@PathVariable("id")Long id){ return bladeUserService.getById(id); } }
測試
啟動項目,使用postman訪問該接口,連續(xù)請求兩次,觀察響應(yīng)時長:
第一次:
第二次:
可以看到,第一次3.34s,第二次43ms,效率明顯提高!
方式二:采用SpringBoot注解開啟緩存
以方式一為準(zhǔn)
在啟動類添加@EnableCaching注解
修改service層實現(xiàn)類代碼
package com.redis.demo.service.impl; import cn.hutool.core.bean.BeanUtil; import cn.hutool.json.JSONUtil; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.redis.demo.constant.RedisConstants; import com.redis.demo.dao.BladeUserMapper; import com.redis.demo.entity.BladeUser; import com.redis.demo.result.DealResult; import com.redis.demo.service.IBladeUserService; import com.redis.demo.status.CacheNameStatus; import com.redis.demo.utils.RedisUtils; import lombok.AllArgsConstructor; import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cache.annotation.Cacheable; import org.springframework.stereotype.Service; import org.springframework.util.ObjectUtils; /** ** 用戶表 服務(wù)實現(xiàn)類 *
* * @author laz * @since 2023-03-09 */ @Service public class BladeUserServiceImpl extends ServiceImplimplements IBladeUserService { @Autowired private RedisUtils redisUtils; // @Override // public DealResult getById(Long id) { // // String userKey = RedisConstants.CACHE_USER_KEY+id; // Object user = redisUtils.get(userKey); // if (!ObjectUtils.isEmpty(user)){ // // return DealResult.data(JSONUtil.toBean(JSONUtil.toJsonStr(user),BladeUser.class)); // } // // BladeUser bladeUser = baseMapper.selectById(id); // redisUtils.set(userKey, JSON.toJSONString(bladeUser)); // return DealResult.data(bladeUser); // } @Cacheable(cacheNames = CacheNameStatus.BLADE_USER,keyGenerator = CacheNameStatus.KEY_GENERATOR) @Override public DealResult getById(Long id) { BladeUser bladeUser = baseMapper.selectById(id); return DealResult.data(bladeUser); } }
修改RedisConfig配置類
在配置類中添加自定義KeyGenerator
/**
* 自定義KeyGenerator
* @return
*/
@Bean
public KeyGenerator simpleKeyGenerator() {
return (o, method, objects) -> {
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append(o.getClass().getSimpleName());
stringBuilder.append(".");
stringBuilder.append(method.getName());
stringBuilder.append("[");
for (Object obj : objects) {
if(obj.toString().indexOf("Vo@")!= -1)
{
Map map = MapUtil.getAttrFromModel(obj);
stringBuilder.append("[");
for(String item:map.keySet())
{
stringBuilder.append(",");
stringBuilder.append(map.get(item));
}
stringBuilder.append(",");
stringBuilder.deleteCharAt(stringBuilder.length() - 1);
stringBuilder.append("]");
}
else {
stringBuilder.append(obj);
stringBuilder.append(",");
}
}
stringBuilder.append("]");
return stringBuilder.toString();
};
}
注:關(guān)于 @Cacheable注解的參數(shù),不懂的可以點擊查看。
重啟項目,再次訪問以上接口,觀察響應(yīng)時間:
第一次:
第二次:
可以看到,第一次2.52s,第二次44ms,效率明顯提高!
通過Redis可視化工具觀察緩存數(shù)據(jù):
通過觀察緩存數(shù)據(jù)大小可知:方式一449字節(jié),方式二976字節(jié),如果從內(nèi)存占用大小的角度考慮,博主認為使用RedisTemplate方式做緩存更合適,因為這種方式所占內(nèi)存相對較少。
以上就是該篇文章的所有內(nèi)容,代碼已上傳至git:https://gitee.com/lianaozhe/springboot-redis.git
到此這篇關(guān)于SpringBoot整合Redis實現(xiàn)高并發(fā)數(shù)據(jù)緩存的文章就介紹到這了,更多相關(guān)SpringBoot Redis高并發(fā)數(shù)據(jù)緩存內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)稿件
SpringBoot整合Redis實現(xiàn)高并發(fā)數(shù)據(jù)緩存的示例講解 天天頭條
全球看熱訊:古裝廠牌到全能玩家,年均爆款的西嘻影業(yè)提供了影視公司的生存法
【旗艦】7.5見?紅魔8S Pro發(fā)布時間曝光 國內(nèi)首臺8G2超頻版 全球百事通
世界新資訊:北京市氣象臺發(fā)布高溫紅色預(yù)警信號 最高氣溫可達37-40℃
萬圣節(jié)門鈴和禮物創(chuàng)意,男生浪漫到底可行嗎?如何為這個節(jié)日增添驚喜?知乎專家來為你解答!
風(fēng)吹十里荷花香 “蓮”通鄉(xiāng)村“致富路”
世界新動態(tài):全國鐵路今天預(yù)計發(fā)送旅客1515萬人次
燃氣閥門怎樣操作,你都了解嗎?請收藏轉(zhuǎn)發(fā)!
全球資訊:三天漲超20%!工業(yè)富聯(lián)最新發(fā)聲
速遞!白玉蘭視帝視后出爐,雷佳音打敗張譯獲得視帝,吳越視后實至名歸
六月第4期:六大策略組合表現(xiàn):寬信用本周收益率為0.5%
保護知識產(chǎn)權(quán)激發(fā)創(chuàng)新活力
端午節(jié)了還沒學(xué)會包粽子?用VR試試,幫你成為特級廚師!
環(huán)球今頭條!美媒:福特擬進行新一輪裁員,主要針對美國正式員工
端午假期廈門舉辦各式活動 市民游客感受傳統(tǒng)節(jié)日魅力


