• Java Redis缓存穿透/缓存雪崩/缓存击穿,Redis分布式锁实现秒杀,限购等


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

      

  • 相关阅读:
    记一次oracle新建用户及分配指定表权限的操作记录
    [转]word中不显示mathtype公式,只显示方框,双击后可以再mathtype里面看到公式
    C、C++成员变量对齐
    include头文件:C++标准库与C标准库
    [转]本专业部分国际会议及刊物影响因子排名
    使用Winbase.h
    [转]printf 字符串格式化
    1.6.2如何使用位逻辑运算(例如与、或、位移)来实现位向量?
    文章中图表自动编号
    取样问题 总数n事先不知道,等概率取样 (编程珠玑chapter12 课后题10)
  • 原文地址:https://www.cnblogs.com/smartsmile/p/11636736.html
Copyright © 2020-2023  润新知