美文网首页
千帆竞发 —— 分布式锁

千帆竞发 —— 分布式锁

作者: DreamsonMa | 来源:发表于2019-03-21 17:25 被阅读0次

一个操作要修改用户的状态,修改状态需要先读出用户的状态,在内存里进行修改,改完了再存回去。如果这样的操作同时进行了,就会出现并发问题,因为读取和保存状态这两个操作不是原子的。这个时候就要使用到分布式锁来限制程序的并发执行。

同时操作一个context,存在并发问题

分布式锁

一般是使用 setnx(set if not exists) 指令占坑, 用完再调用 del 指令释放茅坑。如果逻辑执行到中间出现异常了,可能会导致 del 指令没有被调用,这样就会陷入死锁,锁永远得不到释放。于是我们在拿到锁之后,再给锁加上一个过期时间,比如 5s,这样即使中间出现异常也可以保证 5 秒之后锁会自动释放。

/**
 * @Auther: majx2
 * @Date: 2019-3-21 16:02
 * @Description:
 */
public class DistributedLockTest {

    Jedis jedis = RedisDS.create().getJedis();

    final static String KEY = "KEY";

    @Test
    public void testLock() throws InterruptedException {

        new Thread(new Runnable() {
            @Override
            public void run() {
                Assert.assertTrue(exec());
            }
        }).start();
        Thread.sleep(1000);
        Assert.assertFalse(exec());
        Thread.sleep(3000);

    }

    public boolean exec(){
        return new RedLock().trylock(KEY, new LockWrap() {
            @Override
            public boolean invoke() {
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {}
                return true;
            }
        });
    }


    public class RedLock{

        public boolean trylock(String key,LockWrap wrap){
           
            Long result = jedis.setnx(key, KEY);// 占坑
            if(result == 1L){  
                jedis.expire(key,5000); // 避免没有删除
                boolean invoke = wrap.invoke();
                jedis.del(key);
                return invoke;
            }
            return false;
        }
    }

    public interface LockWrap{

        boolean invoke();
    }
}

但是以上逻辑还有问题。如果在 setnx 和 expire 之间服务器进程突然挂掉了,可能是因为机器掉电或者是被人为杀掉的,就会导致 expire 得不到执行,也会造成死锁。这种问题的根源就在于 setnx 和 expire 是两条指令而不是原子指令。 解决这些问题,可以使用开源分布式组建redission。

超时问题

Redis 的分布式锁不能解决超时问题,如果在加锁和释放锁之间的逻辑执行的太长,以至于超出了锁的超时限制,就会出现问题。因为这时候锁过期了,第二个线程重新持有了这把锁,第二个线程就会在第一个线程逻辑执行完之间拿到了锁;紧接着第一个线程执行完了业务逻辑,就把锁给释放了,第三个线程就会在第二个线程逻辑执行完之间拿到了锁。为了避免这个问题,Redis 分布式锁不要用于较长时间的任务。如果真的偶尔出现了,数据出现的小波错乱可能需要人工介入解决。

集群问题

在 Sentinel 集群中,主节点挂掉时,从节点会取而代之,客户端上却并没有明显感知。原先第一个客户端在主节点中申请成功了一把锁,但是这把锁还没有来得及同步到从节点,主节点突然挂掉了。然后从节点变成了主节点,这个新的节点内部没有这个锁,所以当另一个客户端过来请求加锁时,立即就批准了。这样就会导致系统中同样一把锁被两个客户端同时持有,不安全性由此产生。

集群环境下,分布式锁存在问题

如果你很在乎高可用性,希望挂了一台 redis 完全不受影响,那就应该考虑 redlock算法。不过代价也是有的,需要更多的 redis 实例,性能也下降了。

注: Redlock算法,需要提供多个 Redis 实例,这些实例之前相互独立没有主从关系。加锁时,它会向过半节点发送 set(key, value, nx=True, ex=xxx) 指令,只要过半节点 set 成功,那就认为加锁成功。释放锁时,需要向所有节点发送 del 指令。不过, Redlock 算法还需要考虑出错重试、时钟漂移等很多细节问题,同时因为 Redlock 需要向多个节点进行读写,意味着相比单实例 Redis 性能会下降一些。

本文基于《Redis深度历险:核心原理和应用实践》一文的JAVA实践。更多文章请参考:高性能缓存中间件Redis应用实战(JAVA)

相关文章

  • 千帆竞发 —— 分布式锁

    一个操作要修改用户的状态,修改状态需要先读出用户的状态,在内存里进行修改,改完了再存回去。如果这样的操作同时进行了...

  • 分布式锁

    为什么要用分布式锁 数据库乐观锁redis分布式锁zookeeper分布式锁 使用分布式锁的场景 实现分布式锁的方...

  • 什么是分布式锁?几种分布式锁分别是怎么实现的?

    一、什么是分布式锁: 1、什么是分布式锁: 分布式锁,即分布式系统中的锁。在单体应用中我们通过锁解决的是控制共享资...

  • 4:Redis 分布式锁 (文末有项目连接)

    1:什么是缓存分布式锁 2:分布式锁的关键代码 3:业务代码使用分布式缓存锁 4:业务代码使用分布式缓存锁 5:测...

  • 锁(2)-- 分布式锁

    前言: 锁分3种:java锁、分布式锁、DB锁 分布式锁的几种实现方式 目前几乎很多大型网站及应用都是分布式部署...

  • java锁的概念

    参考文档探究分布式并发锁并发编程-锁的发展和主流分布式锁比较总结从构建分布式秒杀系统聊聊分布式锁探索并发编程(六)...

  • Redis实现分布式锁

    分布式下的分布式锁一般实现有三种: 基于数据库的乐观锁 基于redis的分布式锁 基于zookeeper的分布式锁...

  • 分布式锁

    为什么要用分布式锁? 分布式锁是悲观锁的实现; 如果采用乐观锁的方案就用不着分布式锁了。 能用乐观锁的地方尽量用乐...

  • 3.10:分布式锁

    本文将梳理微服务架构下,分布式锁的常用方案。整体包含以下三部分: 分布式锁的提出 分布式锁主流方案 分布式锁选择 ...

  • Redis实现分布式锁

    1. 分布式锁分类 数据库乐观锁 基于Redis的分布式锁 基于ZooKeeper的分布式锁 2. 组件依赖 po...

网友评论

      本文标题:千帆竞发 —— 分布式锁

      本文链接:https://www.haomeiwen.com/subject/zoccvqtx.html