您当前的位置:首页 > 电脑百科 > 程序开发 > 编程百科

分布式锁的实现方式

时间:2022-05-06 09:08:37  来源:  作者:邬小明

1. 为什么需要锁

在并发场景下,多个进程/线程同时对同一个资源进行访问时,会产生冲突。
举个例子:核酸采样时,如果一次100个人同时要求大白进行采样(并发),那么大白就要崩溃了,所以必须要控制一个大白一次只能对一个人采样,其他人等待采样完成,这就是对大白进行”加锁”。

2. 锁是用来解决什么问题的

锁是用来解决并发问题的,如:

  • 多个线程并发访问同一个资源
  • 分布式系统中不同模块对同一资源进行修改

锁的使用场景:

  • 秒杀
  • 抢红包
  • 库存更新

3. 分布式锁的解决方案

  • 通过数据库实现(利用数据库唯一约束的特性实现)
  • 通过Zookeeper实现(利用zookeeper的唯一节点特性或者有序临时节点特性获得最小节点作为锁)
  • 通过redis实现(setNx命令)

4 通过Redis实现分布式锁示例

通过JAVA程序连接Redis,提示以下错误:

redis.clients.jedis.exceptions.JedisConnectionException: FAIled to connect to any host resolved for DNS name.
    at redis.clients.jedis.DefaultJedisSocketFactory.connectToFirstSuccessfulHost(DefaultJedisSocketFactory.java:63)
    at redis.clients.jedis.DefaultJedisSocketFactory.createSocket(DefaultJedisSocketFactory.java:87)
    at redis.clients.jedis.Connection.connect(Connection.java:180)
    at redis.clients.jedis.Connection.initializeFromClientConfig(Connection.java:338)

可能有如下原因:

  • Redis未启动
  • Redis IP地址或者端口不对
  • Redis不允许远程连接

4.1 配置允许远程连接Redis

4.1.1 开放Redis端口(6379)

//查看6379端口状态 mo表示未开放
[root@192 bin]# firewall-cmd --zone=public --query-port=6379/tcp 
no
//配置放行6379端口
[root@192 bin]# firewall-cmd --zone=public --add-port=6379/tcp --permanent
success
//防火墙重载
[root@192 bin]#  firewall-cmd --reload
success
//再次查看端口状态
[root@192 bin]# firewall-cmd --zone=public --query-port=6379/tcp
yes

4.1.2 修改redis.conf配置文件

[root@192 redis]# vim redis.conf

将配置改成如下所示:

# 允许任何主机连接、访问
bind 0.0.0.0
# 关闭保护模式
protected-mode no
# 允许启动后在后台运行,即关闭命令行窗口后仍能运行
daemonize yes

4.2 模拟秒杀下单减库存的场景

4.2.1 新建以下商品表(t_goods)与订单表(t_order)

DROP TABLE IF EXISTS `t_goods`;
CREATE TABLE `t_goods` (
  `id` int(11) NOT NULL,
  `name` varchar(60) DEFAULT NULL,
  `qty` int(11) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

--初始化数量qty=20
INSERT INTO `t_goods` VALUES ('1', '华为nova7', '20');
DROP TABLE IF EXISTS `t_order`;
CREATE TABLE `t_order` (
  `oid` varchar(120) NOT NULL,
  `createtime` datetime DEFAULT NULL,
  `goodname` varchar(255) DEFAULT NULL,
  `user` varchar(120) DEFAULT NULL,
  PRIMARY KEY (`oid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

4.2.2 编写main方法,同时启动10000条线程模拟秒杀

    public static void main(String[] args) throws ParseException {

        //设置秒杀开始时间
        String startTime="2022-4-27 23:24:00";
        SimpleDateFormat simpleDateFormat=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        Date start=simpleDateFormat.parse(startTime);
        System.out.println("等待中...");
        boolean isStart=false;
        while (!isStart) {
            if (start.compareTo(new Date()) < 0) {
                isStart=true;
                System.out.println("秒杀开始...");
                //同时启动10000条线程
                CountDownLatch countDownLatch = new CountDownLatch(10000);
                for (int i = 0; i < 10000; i++) {
                    new Thread(() -> {
                        try {
                            countDownLatch.await();
                            //secKillGoodsByRedisLock();//Redis锁
                            secKillGoods();  //未加锁
                           // secKillGoodsByLock();//synchronized

                        } catch (InterruptedException | SQLException e) {
                            e.printStackTrace();
                        }
                    }).start();
                    countDownLatch.countDown();
                }
            }
        }
    }

4.2.3 未加锁的情况

private static void secKillGoods() throws SQLException {
    List list = DButil.query("select id,name,qty from t_goods where id=1 and qty>0");
    //判断是否还有库存
    if(list.size()>0){
        String id=JedisUtil.getId();
        Object[] insertObject={id,new Date(),"nova7",Thread.currentThread().getName()};
        DButil.excuteDML("update t_goods set qty=qty-1 where id=1 ");
        DButil.excuteDML("insert into t_order values(?,?,?,?);",insertObject);
        System.out.println(Thread.currentThread().getName()+">>>抢到了."+id);
    }
}

以上代码每次执行都会先判断是否还有库存,如果有库存则秒杀成功,否则秒杀失败,没有并发的情况下是可以正常运行的,但是一旦存在并发,则会出现负库存(超卖)

分布式锁的实现方式

 

4.2.4 通过synchronized关键字对代码块加锁

private static void secKillGoodsByLock() throws SQLException {
    synchronized (lockObj) {
        secKillGoods();
    }
}

以上程序在进入秒杀方法时,都会通过synchronized关键字加锁,再次运行程序,我们发现不会出现负库存了

分布式锁的实现方式

 

但是如果在多进程或者分布式环境中,synchronized关键字会失效,让我们再启动一个进程,两个进程同时启动100000个线程进行秒杀(ps:同时启动十万个线程差点让我的电脑没缓过来…),终于出现了负库存的现象

分布式锁的实现方式

 

4.2.5 Redis锁

private static void secKillGoodsByRedisLock(){
        String id="1";
        String key="lock"+id;
        JedisUtil jedisUtil=new JedisUtil();
        String lockId=jedisUtil.getLock(key,5);

        if(null!=lockId){
            try{
                List list= DButil.query("select id,name,qty from t_goods where id=1 and qty>0");
                if(list!=null && list.size()>0){
                    Object[] insertObject={lockId,new Date(),"nova7",Thread.currentThread().getName()};
                    DButil.excuteDML("update t_goods set qty=qty-1 where id=1 ");
                    DButil.excuteDML("insert into t_order values(?,?,?,?);",insertObject);
                    System.out.println(Thread.currentThread().getName()+">>>抢到了."+lockId);
                    jedisUtil.unLock(key,lockId);

                }else {
                    System.out.println("抢完了");
                }
            } catch (SQLException e) {
                jedisUtil.unLock(key,lockId);
                e.printStackTrace();
            }
        }


    }

以上代码只有在获取到Redis锁成功后,才会去执行扣库存和下单的逻辑,重复和上一步一样,两个进程同时启动100000个线程进行秒杀,看看结果

分布式锁的实现方式

 


分布式锁的实现方式

 

以上结果没有出现负库存的现象,显然是扛住了“秒杀”,getLock 的实现如下所示,其原理就是利用Redis setnx的原子性操作来控制并发,以下示例还设置了锁失效的时间,避免死锁。当然还有许多的问题需要在实际应用场景中考虑,如在锁失效时间到了,秒杀动作未完成如何处理,Redis服务器崩溃了怎么办等等。


    public String getLock(String key,int timeout){
        Jedis jedis=null;
        try {
            jedis=getJedis();
            String value=getId();
            long end=System.currentTimeMillis()+timeout;
            while (System.currentTimeMillis()<end) {
                //设置value成功,获取锁
                if(jedis.setnx(key,value)==1){
                    //设置失效时间
                    jedis.expire(key,timeout);
                    System.out.println(Thread.currentThread().getName()+">>>>>获取锁成功。");
                    return value;
                }
                //当 key 存在但没有设置剩余生存时间时
                if(jedis.ttl(key)==-1){
                    //设置失效时间
                    jedis.expire(key,timeout);
                }
            }

        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            if(null!=jedis){
                jedis.close();
            }
        }
        return null;
    }

参考文献:
为什么需要锁,锁分类,锁粒度

各种锁以及使用场景



Tags:分布式锁   点击:()  评论:()
声明:本站部分内容及图片来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,不构成投资建议。投资者据此操作,风险自担。如有任何标注错误或版权侵犯请与我们联系,我们将及时更正、删除。
▌相关推荐
在Redis中如何实现分布式锁的防死锁机制?
在Redis中实现分布式锁是一个常见的需求,可以通过使用Redlock算法来防止死锁。Redlock算法是一种基于多个独立Redis实例的分布式锁实现方案,它通过协调多个Redis实例之间的锁...【详细内容】
2024-02-20  Search: 分布式锁  点击:(50)  评论:(0)  加入收藏
手动撸一个 Redis 分布式锁
大家好呀,我是楼仔。今天第一天开工,收拾心情,又要开始好好学习,好好工作了。对于使用 Java 的小伙伴,其实我们完全不用手动撸一个分布式锁,直接使用 Redisson 就行。但是因为这些...【详细内容】
2024-02-19  Search: 分布式锁  点击:(47)  评论:(0)  加入收藏
Redis分布式锁常见坑点分析
日常开发中,基于 Redis 天然支持分布式锁,大家在线上分布式项目中都使用过 Redis 锁。本文主要针对日常开发中加锁过程中某些异常场景进行讲解与分析。本文讲解示例代码都在 h...【详细内容】
2023-12-11  Search: 分布式锁  点击:(119)  评论:(0)  加入收藏
一文详解分布式锁的看门狗机制
我们今天来看看这个 Redis 的看门狗机制,毕竟现在还是有很多是会使用 Redis 来实现分布式锁的,我们现在看看这个 Redis 是怎么实现分布式锁的,然后我们再来分析这个 Redis 的看...【详细内容】
2023-11-29  Search: 分布式锁  点击:(219)  评论:(0)  加入收藏
全新的分布式锁,功能简单且强大
作者:donnie4w链接:https://my.oschina.net/donnie4w/blog/10114233前言:分布式锁是分布式系统中一个极为重要的工具。目前有多种分布式锁的设计方案,比如借助 redis,mq,数据库,zoo...【详细内容】
2023-10-30  Search: 分布式锁  点击:(269)  评论:(0)  加入收藏
Redis分布式锁失效,数据是否仍存在于内存中?
正文大家好,我是小米,欢迎来到小米的技术分享!今天,我要和大家一起探讨一个有趣而又深奥的话题:Redis分布式锁失效了,数据还存在Redis内存中吗?这个问题在面试中经常被提出,也是我们...【详细内容】
2023-10-11  Search: 分布式锁  点击:(321)  评论:(0)  加入收藏
Redis魔法:点燃分布式锁的奇妙实现
分布式锁是一种用于在分布式系统中控制对共享资源的访问的锁。它与传统的单机锁不同,因为它需要在多个节点之间协调以确保互斥访问。本文将介绍什么是分布式锁,以及使用Redis...【详细内容】
2023-10-11  Search: 分布式锁  点击:(248)  评论:(0)  加入收藏
分布式锁,原来这么简单!
作者 | 蔡柱梁审校 | 重楼目录 分布式锁介绍 如何实现分布式锁 实现分布式锁1 分布式锁介绍现在的服务往往都是多节点,在一些特定的场景下容易产生并发问题,比如扣减库存,送完...【详细内容】
2023-09-22  Search: 分布式锁  点击:(237)  评论:(0)  加入收藏
lua+redis:分布式锁解决方案分享
介绍当我们涉及到多进程或多节点的分布式系统时,传统的单机锁机制不再足够应对并发控制的需求。这是因为在分布式环境中,多个进程或节点同时访问共享资源,传统锁无法有效地协调...【详细内容】
2023-09-13  Search: 分布式锁  点击:(343)  评论:(0)  加入收藏
分布式锁的3种实现!
分布式锁是一种用于保证分布式系统中多个进程或线程同步访问共享资源的技术。同时它又是面试中的常见问题,所以我们本文就重点来看分布式锁的具体实现(含实现代码)。在分布式系...【详细内容】
2023-09-13  Search: 分布式锁  点击:(311)  评论:(0)  加入收藏
▌简易百科推荐
Meta如何将缓存一致性提高到99.99999999%
介绍缓存是一种强大的技术,广泛应用于计算机系统的各个方面,从硬件缓存到操作系统、网络浏览器,尤其是后端开发。对于Meta这样的公司来说,缓存尤为重要,因为它有助于减少延迟、扩...【详细内容】
2024-04-15    dbaplus社群  Tags:Meta   点击:(2)  评论:(0)  加入收藏
SELECT COUNT(*) 会造成全表扫描?回去等通知吧
前言SELECT COUNT(*)会不会导致全表扫描引起慢查询呢?SELECT COUNT(*) FROM SomeTable网上有一种说法,针对无 where_clause 的 COUNT(*),MySQL 是有优化的,优化器会选择成本最小...【详细内容】
2024-04-11  dbaplus社群    Tags:SELECT   点击:(2)  评论:(0)  加入收藏
10年架构师感悟:从问题出发,而非技术
这些感悟并非来自于具体的技术实现,而是关于我在架构设计和实施过程中所体会到的一些软性经验和领悟。我希望通过这些分享,能够激发大家对于架构设计和技术实践的思考,帮助大家...【详细内容】
2024-04-11  dbaplus社群    Tags:架构师   点击:(2)  评论:(0)  加入收藏
Netflix 是如何管理 2.38 亿会员的
作者 | Surabhi Diwan译者 | 明知山策划 | TinaNetflix 高级软件工程师 Surabhi Diwan 在 2023 年旧金山 QCon 大会上发表了题为管理 Netflix 的 2.38 亿会员 的演讲。她在...【详细内容】
2024-04-08    InfoQ  Tags:Netflix   点击:(5)  评论:(0)  加入收藏
即将过时的 5 种软件开发技能!
作者 | Eran Yahav编译 | 言征出品 | 51CTO技术栈(微信号:blog51cto) 时至今日,AI编码工具已经进化到足够强大了吗?这未必好回答,但从2023 年 Stack Overflow 上的调查数据来看,44%...【详细内容】
2024-04-03    51CTO  Tags:软件开发   点击:(9)  评论:(0)  加入收藏
跳转链接代码怎么写?
在网页开发中,跳转链接是一项常见的功能。然而,对于非技术人员来说,编写跳转链接代码可能会显得有些困难。不用担心!我们可以借助外链平台来简化操作,即使没有编程经验,也能轻松实...【详细内容】
2024-03-27  蓝色天纪    Tags:跳转链接   点击:(16)  评论:(0)  加入收藏
中台亡了,问题到底出在哪里?
曾几何时,中台一度被当做“变革灵药”,嫁接在“前台作战单元”和“后台资源部门”之间,实现企业各业务线的“打通”和全域业务能力集成,提高开发和服务效率。但在中台如火如荼之...【详细内容】
2024-03-27  dbaplus社群    Tags:中台   点击:(13)  评论:(0)  加入收藏
员工写了个比删库更可怕的Bug!
想必大家都听说过删库跑路吧,我之前一直把它当一个段子来看。可万万没想到,就在昨天,我们公司的某位员工,竟然写了一个比删库更可怕的 Bug!给大家分享一下(不是公开处刑),希望朋友们...【详细内容】
2024-03-26  dbaplus社群    Tags:Bug   点击:(9)  评论:(0)  加入收藏
我们一起聊聊什么是正向代理和反向代理
从字面意思上看,代理就是代替处理的意思,一个对象有能力代替另一个对象处理某一件事。代理,这个词在我们的日常生活中也不陌生,比如在购物、旅游等场景中,我们经常会委托别人代替...【详细内容】
2024-03-26  萤火架构  微信公众号  Tags:正向代理   点击:(14)  评论:(0)  加入收藏
看一遍就理解:IO模型详解
前言大家好,我是程序员田螺。今天我们一起来学习IO模型。在本文开始前呢,先问问大家几个问题哈~什么是IO呢?什么是阻塞非阻塞IO?什么是同步异步IO?什么是IO多路复用?select/epoll...【详细内容】
2024-03-26  捡田螺的小男孩  微信公众号  Tags:IO模型   点击:(10)  评论:(0)  加入收藏
站内最新
站内热门
站内头条