41 | 如何设计更优的分布式锁?
41 | 如何设计更优的分布式锁?
讲述:李良
时长12:02大小11.01M
数据库实现分布式锁
Zookeeper 实现分布式锁
Redis 实现分布式锁
Redlock 算法
总结
思考题
赞 11
提建议
精选留言(43)
- -W.LI-2019-08-24老师好!基于数据库的实现,我现在项目中直接不开事务,select后插入(oeder_no做唯一约束)。try_catch 异常,重试3次。如果查到了返回成功保证密等。这么做会有问题么? 课后题:万一收到的N/2+1节点全部挂了肯定会有问题。不知道,从新选为master节点的算法不知,如果会选择没有收到的节点做master也会有问题。
作者回复: 没有问题。 问题的答案:redis实现的分布式锁,都是有一个过期时间,如果一旦服务A出现stop the world的情况,有可能锁过期了,而此时服务A中仍然存在持有锁,此时另外一个服务B又获取了锁,这个时候存在两个服务同时获取锁的可能。
共 2 条评论27 - 风轻扬2019-09-25老师,zk实现的锁,不会出现redis锁一样的问题吗? 设想: 应用1和应用2两个服务分别部署到不同的服务器上。是使用zookeeper实现分布式锁。应用1获取到锁,然后开始长时间gc,应用2也开始长时间gc。应用1的zk锁由于心跳超时释放了锁,应用2结束gc获取到锁,应用1结束gc开始执行任务,此时不就有两个任务在同时执行了吗?展开
作者回复: 是的,这种情况也同样存在同时获取锁的可能
共 2 条评论15 - a、2019-08-24不一定,因为如果集群中有5个redis,abcde,如果发生网络分区,abc在一个分区,de在一个分区,客户端A向abc申请锁成功,在c节点master异步同步slave的时候,master宕机了,slave接替,然后c的slave又和de在一个分区里,这时候如果客户端B来申请锁,也就可以成功了。 zk锁也会出现问题,如果客户端A申请zk锁成功,这时候客户端A和zk不在一个分区里,zk就会把临时节点删除,然后如果客户端B再去申请,也就可以申请成功展开
作者回复: 对的,这种情况也是可能发生的,前提是c节点在宕机之前没有持久化锁。 第二zk锁的问题,如果连接session已经断开,客户端的锁是会释放的,不会存在同时获取锁的情况。
共 2 条评论9 - Geek_cecf232020-04-27老师,之前你讲过一节 36节,记一次线上SQL死锁事故,for update 使用会导致死锁问题,直接使用for update 实现分布式锁,不也会暗含了死锁的可能么
作者回复: 是的,当在RR事务级别下,在数据库中不存在当前key值的情况下,多线程竞争锁会因为意向锁的问题,导致死锁。可降低数据库隔离级别为 Read Commited,这样的话多个事务不会因为意向锁的原因导致死锁了。
6 - neohope2019-11-26老师您好,我有两个问题: redisson会向所有的redis节点并发发起请求,获取一半以上的锁才会进行后续操作。那我的疑问是, 1、这样不会让整体性能变得比redis集群要差一些吗?而且redis节点越多,redisson性能越差? 2、redisson的客户端,如果无法获取到足够多redis节点的锁,其释放规则及防止冲突的规则又是如何处理的呢?如果没有合理的防冲突规则,感觉并发量越高,性能会越差。展开
作者回复: 鱼和熊掌不可兼得,保证可靠性的前提下,会带来一定的性能损失。 当在一定时间内没有获取到足够节点时,会通过定时任务将已经超时的锁通过lua脚本来释放。
共 2 条评论3 - 钱2019-09-13我们的导入功能就是用的redis分布式锁,防止多个业务操作人员同时导入,超时时间一般为五分钟。 出现网络分区只能二选一要A或者C,不过互联网企业基本都会选择A。3
- rong2019-08-27老师,使用select for update防止幻读那里,直接把order_no设置成唯一索引,事务里面只有一条insert语句就可以吧?如果之前有,插入不成功,没有的话,插入成功
作者回复: 是的,唯一索引可以实现该功能。
3 - 桔子2020-04-08redisson分布式锁解决单实例或多个完全互相独立master节点分布式环境获取锁的问题。但是主从或cluster集群下,异步通信导致获取锁多次获取的问题并没解决,理解对否?
作者回复: 对的
2 - 风轻扬2019-09-24老师,互联网行业,多数都是redis集群啊,如果这样,基于redis实现的分布式锁是不是就不能用了?
作者回复: 可以,使用Redisson就好了
2 - 知行合一2019-09-05老师,想问个问题,redis集群已经分了槽,客户端写入根据算法应该写入一个节点啊,为啥要多个节点同时枷锁?
作者回复: 写入一个单点只实现了高可用,没有实现集群式分布式锁。单点的问题会存在单个节点挂了的情况下,不同应用服务同时获取锁的可能。
共 5 条评论2 - 我已经设置了昵称2019-08-25不太懂redission机制,每个节点各自去获取锁。超过一半以上获取成功就算成功。那是不是还有这么一步:这些一半以上的机器获取了以后,是否还要决定谁真正拿到锁,才能真正执行这个任务
作者回复: 都会设置锁对象
2 - Geek_cecf232020-04-14基于数据库的for update的分布锁,不会导致死锁么
作者回复: 会的
1 - Zend2019-12-12老师在数据库实现分布式锁的例子中,保证查询和插入在同一个事务里面,就能防止幻读。是不是不指定在一个事务里,查询和插入操作虽然在一个方法里,但是两个事务。
作者回复: 是的
1 - 尔冬橙2019-10-09老师,分布式锁到底锁什么呢,如果说是锁数据库表,分布式应用集群的情况下,如果是单机数据库,数据库自身的锁机制可以保证并发问题吧?难道是分布式锁只是用在数据库分库分表的情况下?
作者回复: 分布式锁是在分布式服务的情况下保证原子性操作,而不是因为数据库产生的分布式锁。 数据库可以实现分布式锁,是一种实现方式。
1 - 风轻扬2019-09-26老师,我试了一下zookeeper的集群分布式锁。测试代码如下: public class TestZookeeperLock { private static int count = 10; public static void main(String[] args) { //重试策略,以下写法为:重试3次,每次间隔时间为3秒 final RetryPolicy retryPolicy = new RetryNTimes(3,2000); final String connUrl = "192.111.111.111:2181,192.222.222.222:2181"; for (int i = 0; i < 10; i++) { new Thread(new Runnable() { @Override public void run() { //zookeeper分布式锁 CuratorFramework zk = CuratorFrameworkFactory.newClient(connUrl, retryPolicy); zk.start(); InterProcessMutex lock = new InterProcessMutex(zk, "/opt/uams/zookeeper-3.4.7/locks"); try { if (lock.acquire(3, TimeUnit.SECONDS)){ get(); } } catch (Exception e) { e.printStackTrace(); } finally { try { //释放锁 lock.release(); } catch (Exception e) { e.printStackTrace(); } } } }).start(); } } public static void get (){ count --; if (count == 3) { try { TimeUnit.SECONDS.sleep(3);//这里设置该线程睡眠2秒,已达到锁住效果 } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println(count); } } 输出了: 9 8 7 6 5 4 java.lang.IllegalMonitorStateException: You do not own the lock: /opt/uams/zookeeper-3.4.7/locks at org.apache.curator.framework.recipes.locks.InterProcessMutex.release(InterProcessMutex.java:140) at cn.org.test.TestZookeeperLock$1.run(TestZookeeperLock.java:47) at java.lang.Thread.run(Thread.java:745) 3 多次输出的结果一致,这是怎么回事呢?展开
作者回复: 将以下代码提出到new Thread之外: //zookeeper分布式锁 CuratorFramework zk = CuratorFrameworkFactory.newClient(connUrl, retryPolicy); zk.start(); InterProcessMutex lock = new InterProcessMutex(zk, "/opt/uams/zookeeper-3.4.7/locks");
1 - K2019-09-08老师好,课后问题还是没听懂,首先我理解redis集群可能同时获取锁,是因为锁时间超时了,别的线程也能拿到,是这个原因。Redlock 算法是怎样解决这个问题的呢?
作者回复: RedLock算法是会去每一个节点获取锁,正常情况下,别的线程无法同时获取锁的。
1 - Jxin2019-09-041.锁超时,也会出现多个任务同时持有锁进行。 2.解决方式,守护线程续航锁持有时间。 3.弊端,浪费线程,开销太大。 4.根据业务情况设置合理的超时时间是最棒的。 5.集群环境还会导致事务失效(同时提交多个key,多个key在不同节点)挺蛋疼。展开1
- 再续啸傲2019-09-03Redisson的“看门狗”watch机制,解决了业务执行时间长于锁过期时间的问题。但是为每一个获取锁的线程设置监听线程,会不会在高并发的场景下耗费过多资源呢?
作者回复: 应该是一个线程监听,具体需要看源码实现。
共 2 条评论1 - -W.LI-2019-08-26谢谢老师!STW问题之前都没想到,不过正常情况STP时间比较短的吧,除非是CMS下的超大老年代,或者代码不合理。G1分segment回收STW应该不会长吧。项目中数据库锁和redis锁用的比较多,不过超时时间都是随意设置10,20S。正常一般几十ms就能就能完成的。请问redis锁超时时间设置多少比较合理呢?项目中大部分情况锁冲突概率比较小。电商项目,商家余额这种冲突概率很大的适合用zk锁是么?
作者回复: 是的,根据自己的需求设定。zk锁则没有超时时间问题。
1 - summer2022-07-28 来自陕西Red lock集群环境下单点故障,网上说延迟重启能解决,对吗