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; } }
/** * 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"; }