我是靠谱客的博主 碧蓝跳跳糖,最近开发中收集的这篇文章主要介绍基于redis实现秒杀针对单进程实现针对集群实现redis优化方案zookeeper与redis实现分布锁区别,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

基于redis实现秒杀

  • 针对单进程实现
  • 针对集群实现
    • 1、redisTemplate.opsForValue().setIfAbsent方式
    • 2、加入try{}finally{}、过期时间
    • 3、redissonLock.lock()方式
    • 4、读写锁(缓存与数据库读写不一致问题)
  • redis优化方案
  • zookeeper与redis实现分布锁区别

针对单进程实现

   @RequestMapping("/miaosha1")
    @ResponseBody
    public String miaosha1() {

        synchronized (this){
            Map<String,Object> mapListmaps = new HashMap<>();
            //在redis中获取库存值
            int kucun = Integer.valueOf(redisUtil.getStrKV("kucun").toString());
            //如果库存大于0
            if(kucun>0){
                int newkucun = kucun - 1;
                //将库存值存入redis中(代表剩余库存)
                redisUtil.setStrKV("kucun",newkucun);
                System.out.println("剩余库存"+newkucun);
            }else{
                System.out.println("库存不足");
            }
        }
        return "list";
    }

jdk提供synchronized方式单机情况下可用(单进程内置锁,只能控制当前进程),如果集群,就会存在并发问题

针对集群实现

1、redisTemplate.opsForValue().setIfAbsent方式

@RequestMapping("/miaosha2")
    @ResponseBody
    public String miaosha2() {
        String lockKey = "lockKey";
        String clientId = UUID.randomUUID().toString();
        //返回true代表保存成功
        Boolean result = redisTemplate.opsForValue().setIfAbsent(lockKey,clientId);
        if(!result){
            return  "当前系统繁忙,请稍后再试";
        }
         Map<String,Object> mapListmaps = new HashMap<>();
            //在redis中获取库存值
            int kucun = Integer.valueOf(redisUtil.getStrKV("kucun").toString());
            //如果库存大于0
            if(kucun>0){
                int newkucun = kucun - 1;
                //将库存值存入redis中(代表剩余库存)
                redisUtil.setStrKV("kucun",newkucun);
                System.out.println("剩余库存"+newkucun);
            }else{
                System.out.println("库存不足");
            }
            //执行完之后,删除key(让下一个进来)
        redisTemplate.delete(lockKey);
        return "list";

    }

redis分布式锁(入门版本)
用redisTemplate.opsForValue().setIfAbsent
如果出现相同的key值,redis默认会执行第一个,之后发现第二个和第一个key值相同,则第二个则不会执行
》》》缺点:如果redis数据减了,但是后续业务抛出异常(系统宕机),无法执行删除rediskey操作,后续就会死锁

2、加入try{}finally{}、过期时间

@RequestMapping("/miaosha3")
    @ResponseBody
    public String miaosha3() {
        String lockKey = "lockKey";
        //uuid(为每一个锁添加一个唯一标识)
        String clientId = UUID.randomUUID().toString();
        //返回true代表保存成功
        //Boolean result = redisTemplate.opsForValue().setIfAbsent(lockKey,clientId);

        //添加超时间时间
        Boolean result = redisTemplate.opsForValue().setIfPresent(lockKey,clientId,10, TimeUnit.SECONDS);
        if(!result){
            return  "当前系统繁忙,请稍后再试";
        }
        try{
            Map<String,Object> mapListmaps = new HashMap<>();
            //在redis中获取库存值
            int kucun = Integer.valueOf(redisUtil.getStrKV("kucun").toString());
            //如果库存大于0
            if(kucun>0){
                int newkucun = kucun - 1;
                //将库存值存入redis中(代表剩余库存)
                redisUtil.setStrKV("kucun",newkucun);
                System.out.println("剩余库存"+newkucun);
            }else{
                System.out.println("库存不足");
            }
        }finally {
            //判断当前的redis是否是clientId下的redis
            if(clientId.equals(redisUtil.getStrKV("lockKey"))){
                //执行完之后,删除key(让下一个进来)
                redisTemplate.delete(lockKey);
            }

        }
        return "list";

    }

redis分布式锁(入门版本)
上一个缺点:如果redis数据减了,但是后续业务抛出异常,无法执行删除rediskey操作,后续就会死锁
》》》解决:加入try{}finally{},不管执行过程如何都执行删除rediskey操作()、(加入过期时间,避免系统宕机导致无法删除)
》》》缺点:因为时间问题,如果第一个耗时时间长,第一个过去10秒后超时将key删除,所以第二个进来了,但是第一个又走到finally将第二个key删除了,以此反复
》》》解决:为每一个锁加一个唯一标识(在finally之后判断当前的redis是否是clientId下的redis)
》》》问题:担心10秒过期时间超时,“锁续命”可以解决;设置一个定时任务查看锁是否执行完毕,如果未执行完毕,将10秒重置成10秒
》》》问题:分布式锁,释放锁得问题(原子性)

3、redissonLock.lock()方式

@RequestMapping("/miaosha4")
    @ResponseBody
    public String miaosha4() {
        String lockKey = "lockKey";

        RLock redissonLock = RedisConfig.redisson().getLock(lockKey);
        try{
            //加锁
            redissonLock.lock();

            //在redis中获取库存值
            int kucun = Integer.valueOf(redisUtil.getStrKV("kucun").toString());
            //如果库存大于0
            if(kucun>0){
                int newkucun = kucun - 1;
                //将库存值存入redis中(代表剩余库存)
                redisUtil.setStrKV("kucun",newkucun);
                System.out.println("剩余库存"+newkucun);
            }else{
                System.out.println("库存不足");
            }
        }finally {
            //释放锁
            redissonLock.unlock();

        }
        return "list";

    }

redis分布式锁(中级版本)(公司可以够用了)
应用redisson加锁(redis升级版:如果加锁成功,每隔10秒检查”锁续命“;而且它本身自己会加标识,如果第一个锁未执行完毕,第二个不会进来)
》》》redissonLock.lock()源码:
lua脚本(里面内容就是上一个步骤:加key,加标识,加超时时间,还有一个锁续命的逻辑)
》》》问题:主从节点同步问题(第一个redis加key值时,会先到主节点,再到从节点。但是现在主节点挂了,第一个无法完成。这时候第二个又进来了)

4、读写锁(缓存与数据库读写不一致问题)

 @RequestMapping("/miaosha5")
    @ResponseBody
    public String miaosha5() {
        String lockKey = "lockKey";
        //读写锁
        RReadWriteLock rReadWriteLock =  RedisConfig.redisson().getReadWriteLock(lockKey);
        //读
        RLock writeLock = rReadWriteLock.writeLock();
        //写
        RLock rLock = rReadWriteLock.readLock();
        try{
            //读加锁
            writeLock.lock();
            //写加锁
            rLock.lock();

            //在redis中获取库存值
            int kucun = Integer.valueOf(redisUtil.getStrKV("kucun").toString());
            //如果库存大于0
            if(kucun>0){
                int newkucun = kucun - 1;
                //将库存值存入redis中(代表剩余库存)
                redisUtil.setStrKV("kucun",newkucun);
                System.out.println("剩余库存"+newkucun);
            }else{
                System.out.println("库存不足");
            }
        }finally {
            //释放锁
            writeLock.unlock();
            rLock.unlock();

        }
        return "list";

    }

问题:缓存与数据库读写不一致问题
1、更新完数据库删缓存;另一个线程卡顿
2、延迟双删(sleep多少毫秒);另一个线程卡顿时间不好估计,
3、内存队列(数据操作直接到一个队列中,原子性)
4、加分布式锁(每一段操作)
4.5、读写锁(分布式读锁和写锁,读写有关联,读跟读无关联)----源码:读写锁key是相同的,区别是加一个模式read、write
》》》场景:购买商品时,因为期间会有很多步骤“0加购物车、付款等”到真正付款时缓存库存值清零了,缓存不一致问题,
》》》解决方案:设置一个超时时间,expire(30s);
读多写多用缓存不太好,命中率降低,也可以用canal,canal中间件可以监控数据库执行的先后顺序,然后再更新缓存

redis优化方案

1、较少锁粒度,在锁内尽可能减少代码量,
2、分段锁:将库存分为几个库存,让同时进行这个扣减,增加效率

zookeeper与redis实现分布锁区别

保存数据上,redis主节点保存之后直接会回调程序保存成功,zk是主节点保存之后会让自己余下的从节点比如3个,保存2个1(一半以上)才会回复主程序
保存成功。安全性比redis高,但是速度慢
》》》redis有类似zk的方式解决方式:Redlock(小问题比较多);执行方式就是用三个redissonLock.lock();加锁,然后用Redlock控制

最后

以上就是碧蓝跳跳糖为你收集整理的基于redis实现秒杀针对单进程实现针对集群实现redis优化方案zookeeper与redis实现分布锁区别的全部内容,希望文章能够帮你解决基于redis实现秒杀针对单进程实现针对集群实现redis优化方案zookeeper与redis实现分布锁区别所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(43)

评论列表共有 0 条评论

立即
投稿
返回
顶部