Redis 分布式锁

2022-04-08  本文已影响0人  hemiao3000

0. 单机的并发问题

以一个『流水号生成』的场景为例,普通的后台应用通常都是使用时间戳的方式生成流水号,但是在用户量非常大的情况下,可能会出现并发问题。例如如下代码所示:

@Test
public void x() {
    final CountDownLatch down = new CountDownLatch(1);
    for (int i = 0; i < 10; i++) {
        new Thread(() -> {
            try {
                down.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss|SSS");
            String orderNo = sdf.format(new Date());
            System.out.println("生成的订单号是 : " + orderNo);
        }).start();
    }

    down.countDown();
}

运行以上代码,你会发现生成的 10 个订单号中,有不少是重复的。究其原因,就是因为在没有进行同步的情况下,出现了并发问题。

1. 分布式锁使用场景

一般我们使用分布式锁有两个场景:

Redis 因为其性能好,实现起来分布式锁简单,所以让很多人都对基于 Redis 实现的分布式锁十分青睐。

::: tip 提示
除了能使用 Redis 实现分布式锁之外,Zookeeper 也能实现分布式锁。但是项目中不可能仅仅为了实现分布式锁而专门引入 Zookeeper ,所以,除非你的项目体系中本来就有 Zookeeper<small>(来实现其它功能)</small>,否则不会单独因为分布式锁而引入它。
:::

2. SETNX 命令

早期,SETNX 是独立于 SET 命令之外的另一条命令。它的意思是 SET if Not eXists,即,在键值对不存在的时候才能设值成功。

::: warning 注意
SETNX 命令的价值在于:它将 判断设值 两个操作合二为一,从而避免了 查查改改 的情况的出现。
:::

后来,在 Redis 2013 年推出的 2.6.12 版本中,Redis 为 SET 命令官方提供了 NX 选项,是的 SET 命令也能实现 SETNX 命令的功能。其语法如下:

SET <key> <value> [EX seconds] [PX milliseconds] [NX | XX]

EX 值的是 key 的存活时间,单位为秒。PXEX 作用一样,唯一的不同就是后者的单位是微秒<small>(使用较少)</small>。

NXXX 作用是相反的。NX 表示只有当 key『不存在时』才会设置其值;XX 表示当 key 存在时才设置 key 的值。

在 “升级” 了 SET 命令之后,Redis 官方说:“由于 SET 命令选项可以替换 SETNX,SETEX,PSETEX,因此在 Redis 的将来版本中,这三个命令可能会被弃用并最终删除”。

所以,现在我们口头所说的 SETNX 命令,并非单指 SETNX 命令,而是包括带 NX 选项的 SET 命令<small>(甚至以后就没有 SETNX 命令了)</small>。

3. SETNX 的使用

在使用 SETNX 操作实现分布式锁功能时,需要注意以下几点:

在代码层面,与 Setnx 命令对应的接口是 ValueOperations 的 setIfAbsent 方法。

4. Redis SETNX 的问题

如果在代码中使用 Redis 的 SETNX 命令,那么使用逻辑的伪代码如下:

String uuid1 = ...;
// lock
set Test uuid1 NX PX 3000
try {
// biz handle....
} finally {
    // unlock
    String uuid2 = get Test;
    if (uuid1.equals(uuid2) {
        redisTool.del('Test');
    }
}

上面的代码逻辑有 2 个小问题:

  1. 上锁时,设置的超时自动删除时长<small>(3 秒)</small>,设置多长合适?万一设置短了怎么办?

    如果设置短了,在业务逻辑执行完之前时间到期,那么 Redis 自动就把键值对给删除了,即,把锁给释放了,这不符合逻辑。

  2. 解锁时,查 - 删 操作是 2 个操作,由两个命令完成,非原子性。

当然,上述两个问题我们都能解决点,不过有人<small>( Redisson )</small>帮我们把这些事情做好了。

上一篇下一篇

猜你喜欢

热点阅读