Java Redis缓存穿透/缓存雪崩/缓存击穿,Redis分布式锁实现秒杀,限购等
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 | package com.example.redisdistlock.controller; import com.example.redisdistlock.util.RedisUtil; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.util.StringUtils; import org.springframework.web.bind.annotation.RestController; import java.util.HashMap; import java.util.Map; @RestController public class CacheController { @Autowired private StringRedisTemplate stringRedisTemplate = null ; @Autowired private RedisUtil redisUtil = null ; /** * ****************************** 缓存穿透 ****************************** * 缓存穿透,是指查询一个数据库一定不存在的数据。 * 正常的使用缓存流程大致是,数据查询先进行缓存查询, * 如果key不存在或者key已经过期,再对数据库进行查询, * 并把查询到的对象,放进缓存。如果数据库查询对象为空,则不放进缓存。 * 灾难现场:想象一下这个情况,如果传入的参数为-1,会是怎么样?这个-1,就是一定不存在的对象。就会每次都去查询数据库, * 而每次查询都是空,每次又都不会进行缓存。假如有恶意攻击,就可以利用这个漏洞,对数据库造成压力,甚至压垮数据库。 * 解决方案:如果从数据库查询的对象为空,也放入缓存,只是设定的缓存过期时间较短,比如设置为60秒。 */ /** * ****************************** 缓存雪崩 ****************************** * 是指在某一个时间段,缓存集中过期失效。此刻无数的请求直接绕开缓存,直接请求数据库。 * 灾难现场:比如天猫双11,马上就要到双11零点,很快就会迎来一波抢购,这波商品在23点集中的放入了缓存,假设缓存一个小时。 * 那么到了凌晨24点的时候,这批商品的缓存就都过期了。而对这批商品的访问查询,都落到了数据库上,对于数据库而言,就会产生周期性的压力波峰。 * 对数据库造成压力,甚至压垮数据库。 */ /** * ****************************** 缓存击穿 ****************************** * 是指一个key非常热点,在不停的扛着大并发,大并发集中对这一个点进行访问,当这个key在失效的瞬间,持续的大并发就穿破缓存,直接请求数据库,就像在一个屏障上凿开了一个洞。 * 灾难现场:比如某个爆款商品(这种爆款很难对数据库服务器造成压垮性的压力。达到这个级别的公司没有几家的。)但我们也要做好防护方案 * 解决方案:对爆款商品都是早早的做好了准备,让缓存永不过期。即便某些商品自己发酵成了爆款,也是直接设为永不过期。 */ public Object cacheBreakDown(){ Map<String, Object> map = new HashMap<String, Object>(); try { Object zhangsan = redisUtil.get( "zhangsan" ); //System.out.println("zhangsan" + zhangsan); /* 使用双重验证锁解决高并发环境下的缓存穿透问题 */ if (StringUtils.isEmpty(zhangsan)) { // 第一重验证 synchronized ( this ) { zhangsan = redisUtil.get( "zhangsan" ); if (StringUtils.isEmpty(zhangsan)) { // 第二重验证 System.out.println( "查询数据库............" ); // 缓存为空,则查询数据库将相关数据存储到redis中 redisUtil.set( "zhangsan" , "张三" , 10 ); //10秒后过期 } else { System.out.println( "2 查询缓存............" ); } } } else { System.out.println( "1 查询缓存............" ); } map.put( "success" , true ); ////entity实体类 //User user = new User(); //user.setUserId(1000); //user.setUserName("张三"); //user.setAddress("深圳市南山区"); //user.setMobile("13988886666"); //redisUtil.set("userInfo", user.toString(), 10); //10秒后过期自动删除 ////获取显示 //String str = String.valueOf(redisUtil.get("userInfo")); //JSONObject jsonObj = new JSONObject(str); //map.put("userInfo", jsonObj.get("userId")); } catch (Exception e) { map.put( "success" , false ); e.printStackTrace(); } finally { } return map; } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 | /** * Redis分布式并发锁(针对业务场景:库存超卖 秒杀 限购等) * * @return */ @RequestMapping ( "/reductstore" ) @ResponseBody //直接输出字符串 public String ReductStore() { System.out.println( "访问接口" ); String lockKey = "lock" ; // setnx redisson RLock lock = redissonClient.getLock(lockKey); try { int stock = Integer.parseInt(stringRedisTemplate.opsForValue().get( "stock" )); lock.lock(); if (stock > 0 ) { //业务逻辑减少库存 stringRedisTemplate.opsForValue().set( "stock" , (stock - 1 ) + "" ); System.out.println( "扣减库存成功,库存stock:" + (stock - 1 )); } else { System.out.println( "商品已售罄" ); } } catch (NumberFormatException e) { e.printStackTrace(); } finally { lock.unlock(); } return "OK" ; } /** * 单体式架构 * * @return */ @RequestMapping ( "/reduct" ) @ResponseBody //直接输出字符串 public String Reduct() { //System.out.println("访问接口"); try { synchronized ( this ) { //jvm核心技术 int stock = Integer.parseInt(stringRedisTemplate.opsForValue().get( "stock" )); if (stock > 0 ) { //业务逻辑减少库存 stringRedisTemplate.opsForValue().set( "stock" , (stock - 1 ) + "" ); System.out.println( "扣减库存成功,库存stock:" + (stock - 1 )); } else { System.out.println( "商品已售罄" ); } } } catch (NumberFormatException e) { e.printStackTrace(); } finally { } return "OK" ; } |
分类:
JAVA
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 提示词工程——AI应用必不可少的技术
· Open-Sora 2.0 重磅开源!
· 周边上新:园子的第一款马克杯温暖上架