Java redis使用場(chǎng)景介紹
1.作為緩存
1.1 為何使用
數(shù)據(jù)存儲(chǔ)在內(nèi)存中,數(shù)據(jù)查詢速度快??梢苑?jǐn)倲?shù)據(jù)庫(kù)壓力。
1.2 什么樣的數(shù)據(jù)適合放入緩存
查詢頻率比較高,修改頻率比較低。
安全系數(shù)低的數(shù)據(jù)
1.3 使用redis作為緩存
1.3.1 未使用配置類
注意要將實(shí)體類實(shí)現(xiàn)序列化:
@Data @AllArgsConstructor @NoArgsConstructor @TableName(value = "tb_dept") public class Dept implements Serializable { @TableId(value = "id",type = IdType.AUTO) private Integer id; private String name; private String realname; }
對(duì)應(yīng)依賴:
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!--連接數(shù)據(jù)源--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency> <!--mp的依賴--> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.4.2</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies>
controller層對(duì)應(yīng)代碼:
@RestController @RequestMapping("order") public class DeptController { @Resource private DeptService deptService; @GetMapping("getById/{id}") //order/getById/1 //{}可以放多個(gè),由下面的傳參函數(shù)對(duì)應(yīng) //@PathVariable:獲取請(qǐng)求映射中{}的值 public Dept getById(@PathVariable Integer id){ return deptService.findById(id); } @GetMapping("deleteById/{id}") public String deleteById(@PathVariable Integer id){ int i = deptService.deleteById(id); return i>0?"刪除成功":"刪除失敗"; } @GetMapping("insert") public Dept insert(Dept dept){ Dept insert = deptService.insert(dept); return insert; } @GetMapping("update") public Dept update(Dept dept){ Dept update = deptService.update(dept); return update; } }
service層對(duì)應(yīng)代碼:
@Service public class DeptService { @Resource private DeptMapper deptMapper; //當(dāng)存儲(chǔ)的value類型為對(duì)象類型使用redisTemplate //存儲(chǔ)的value類型為字符串。StringRedisTemplate @Autowired private RedisTemplate redisTemplate; //業(yè)務(wù)代碼 public Dept findById(Integer id){ ValueOperations forValue = redisTemplate.opsForValue(); //查詢緩存 Object o = forValue.get("dept::" + id); //緩存命中 if(o!=null){ return (Dept) o; } Dept dept = deptMapper.selectById(id); if(dept!=null){ //存入緩存中 forValue.set("dept::"+id,dept,24, TimeUnit.HOURS); } return dept; } public int deleteById(Integer id){ redisTemplate.delete("dept::"+id); int i = deptMapper.deleteById(id); return i; } public Dept insert(Dept dept){ int insert = deptMapper.insert(dept); return dept; } public Dept update(Dept dept){ redisTemplate.delete("dept::"+dept.getId()); int i = deptMapper.updateById(dept); return dept; } }
配置源:
# 配置數(shù)據(jù)源
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/mydb?serverTimezone=Asia/Shanghai
spring.datasource.username=root
spring.datasource.password=root
#sql日志
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
#連接redis
spring.redis.host=192.168.22*.1**
spring.redis.port=6379
查看的緩存: 前部分代碼相同@before通知,后部分代碼也相同后置通知。 我們可以AOP完成緩存代碼和業(yè)務(wù)代碼分離。
spring框架它應(yīng)該也能想到。--使用注解即可完成。解析該注解。
1.3.2 使用配置類
(1)把緩存的配置類加入
@Bean public CacheManager cacheManager(RedisConnectionFactory factory) { RedisSerializer<String> redisSerializer = new StringRedisSerializer(); Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class); //解決查詢緩存轉(zhuǎn)換異常的問題 ObjectMapper om = new ObjectMapper(); om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL); jackson2JsonRedisSerializer.setObjectMapper(om); // 配置序列化(解決亂碼的問題),過期時(shí)間600秒 RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig() .entryTtl(Duration.ofSeconds(600)) //緩存過期10分鐘 ---- 業(yè)務(wù)需求。 .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer))//設(shè)置key的序列化方式 .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer)) //設(shè)置value的序列化 .disableCachingNullValues(); RedisCacheManager cacheManager = RedisCacheManager.builder(factory) .cacheDefaults(config) .build(); return cacheManager;
(2) 使用開啟緩存注解
(3)使用注解
//業(yè)務(wù)代碼 //使用查詢注解:cacheNames表示緩存的名稱 key:唯一標(biāo)志---dept::key //先從緩存中查看key為(cacheNames::key)是否存在,如果存在則不會(huì)執(zhí)行方法體,如果不存在則執(zhí)行方法體并把方法的返回值存入緩存中 @Cacheable(cacheNames = {"dept"},key="#id") public Dept findById(Integer id){ Dept dept = deptMapper.selectById(id); return dept; } //先刪除緩存在執(zhí)行方法體。 @CacheEvict(cacheNames = {"dept"},key = "#id") public int deleteById(Integer id){ int row = deptMapper.deleteById(id); return row; } //這個(gè)注釋可以確保方法被執(zhí)行,同時(shí)方法的返回值也被記錄到緩存中,實(shí)現(xiàn)緩存與數(shù)據(jù)庫(kù)的同步更新。 @CachePut(cacheNames = "dept",key="#dept.id") public Dept update(Dept dept){ int insert = deptMapper.updateById(dept); return dept; }
2.分布式鎖
使用壓測(cè)工具測(cè)試高并發(fā)下帶來線程安全問題
2.1 壓測(cè)工具的使用
內(nèi)部配置:
2.2 庫(kù)存項(xiàng)目
2.2.1 controller層
@RestController @RequestMapping("bucket") public class BucketController { @Autowired private BucketService bucketService; @GetMapping("update/{productId}") public String testUpdate(@PathVariable Integer productId){ String s = bucketService.updateById(productId); return s; } }
2.2.2 dao層
//此處寫就不需要在啟動(dòng)類使用注解 @Mapper public interface BucketMapper extends BaseMapper<Bucket> { public Integer updateBucketById(Integer productId); }
2.2.3 entity層
@Data @AllArgsConstructor @NoArgsConstructor public class Bucket { @TableId(value = "productId",type = IdType.AUTO) private Integer productId; private Integer num; }
2.2.4 service層
@Service public class BucketService { @Resource private BucketMapper bucketMapper; public String updateById(Integer productId){ //查看該商品的庫(kù)存數(shù)量 Bucket bucket = bucketMapper.selectById(productId); if(bucket.getNum()>0){ //修改庫(kù)存每次減1 Integer integer = bucketMapper.updateBucketById(productId); System.out.println("扣減成功!剩余庫(kù)存數(shù):"+(bucket.getNum()-1)); return "success"; }else { System.out.println("扣減失??!庫(kù)存數(shù)不足"); return "fail"; } } }
2.2.5 mapper
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.qy151wd.dao.BucketMapper"> <update id="updateBucketById" parameterType="int"> update bucket set num=num-1 where productId=#{productId} </update> </mapper>
2.2.6 依賴
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!--連接數(shù)據(jù)源--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency> <!--mp的依賴--> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.4.2</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies>
2.2.7 測(cè)試結(jié)果
我們看到同一個(gè)庫(kù)存被使用了n次。以及數(shù)據(jù)庫(kù)中庫(kù)存為負(fù)數(shù)。 線程安全問題導(dǎo)致。
2.3 解決方案
2.3.1 使用 synchronized 或者lock鎖
對(duì)應(yīng)的service層修改為
@Service public class BucketService { @Resource private BucketMapper bucketMapper; public String updateById(Integer productId){ //加自動(dòng)鎖 synchronized (this){ //查看該商品的庫(kù)存數(shù)量 Bucket bucket = bucketMapper.selectById(productId); if(bucket.getNum()>0){ //修改庫(kù)存每次減1 Integer integer = bucketMapper.updateBucketById(productId); System.out.println("扣減成功!剩余庫(kù)存數(shù):"+(bucket.getNum()-1)); return "success"; }else { System.out.println("扣減失??!庫(kù)存數(shù)不足"); return "fail"; } } } }
如果搭建了項(xiàng)目集群,那么該鎖無效 。
2.3.2 使用redisTemplate
(1)使用idea開集群項(xiàng)目
(2)使用nginx
(3)測(cè)試結(jié)果
發(fā)現(xiàn)又出現(xiàn): 重復(fù)數(shù)字以及庫(kù)存為負(fù)數(shù)。
(4)解決方法
service對(duì)應(yīng)代碼修改
@Service public class BucketService { @Resource private BucketMapper bucketMapper; @Autowired private RedisTemplate redisTemplate; public String updateById(Integer productId){ ValueOperations<String,String> forValue = redisTemplate.opsForValue(); Boolean flag = forValue.setIfAbsent("aaa::" + productId, "-----------------"); if(flag){ try{ //查看該商品的庫(kù)存數(shù)量 Bucket bucket = bucketMapper.selectById(productId); if(bucket.getNum()>0){ //修改庫(kù)存每次減1 Integer integer = bucketMapper.updateBucketById(productId); System.out.println("扣減成功!剩余庫(kù)存數(shù):"+(bucket.getNum()-1)); return "success"; }else { System.out.println("扣減失??!庫(kù)存數(shù)不足"); return "fail"; } }finally { redisTemplate.delete("aaa::"+productId); } } return "服務(wù)器正忙,請(qǐng)稍后再試......."; } }
注意此處的測(cè)壓速度不易太快(推薦使用5秒100個(gè)線程)
經(jīng)過測(cè)壓測(cè)試后,結(jié)果為:
到此這篇關(guān)于Java redis數(shù)據(jù)庫(kù)使用場(chǎng)景介紹的文章就介紹到這了,更多相關(guān)Java redis內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
關(guān)于各種排列組合java算法實(shí)現(xiàn)方法
這篇文章介紹了幾種用JAVA實(shí)現(xiàn)的排列組合算法,有需要的朋友可以參考一下2013-06-06Java中字符串與byte數(shù)組之間的相互轉(zhuǎn)換
Java語(yǔ)言中字符串類型和字節(jié)數(shù)組類型相互之間的轉(zhuǎn)換經(jīng)常發(fā)生,網(wǎng)上的分析及代碼也比較多,這篇文章將主要介紹Java中字符串與byte數(shù)組之間的相互轉(zhuǎn)換,有需要的朋友們可以參考借鑒,下面來一起看看吧。2016-10-10Java生成隨機(jī)數(shù)之Random與ThreadLocalRandom性能比較詳解
大家項(xiàng)目中如果有生成隨機(jī)數(shù)的需求,我想大多都會(huì)選擇使用Random來實(shí)現(xiàn),它內(nèi)部使用了CAS來實(shí)現(xiàn)。?實(shí)際上,JDK1.7之后,提供了另外一個(gè)生成隨機(jī)數(shù)的類ThreadLocalRandom,那么他們二者之間的性能是怎么樣的呢?本文就來詳細(xì)說說2022-12-12JAXB命名空間_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理
這篇文章主要為大家詳細(xì)介紹了JAXB命名空間的相關(guān)資料,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-08-08java Socket實(shí)現(xiàn)簡(jiǎn)單模擬HTTP服務(wù)器
這篇文章主要介紹了java Socket實(shí)現(xiàn)簡(jiǎn)單模擬HTTP服務(wù)器,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-05-05Arthas-java程序運(yùn)行時(shí)debug工具使用
這篇文章主要介紹了Arthas-java程序運(yùn)行時(shí)debug工具使用,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-11-11解讀CommandLineRunner和@PostConstruct區(qū)別與應(yīng)用場(chǎng)景
這篇文章主要介紹了解讀CommandLineRunner和@PostConstruct區(qū)別與應(yīng)用場(chǎng)景,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-12-12JAVA進(jìn)階篇之詳細(xì)了解File文件的常用API
這篇文章主要給大家介紹了關(guān)于JAVA進(jìn)階篇之詳細(xì)了解File文件的常用API的相關(guān)資料,File用于表示文件系統(tǒng)中的一個(gè)文件或目錄,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下2023-11-11