Redis中的分布式锁详解与应用

前言

分布式系统中的 CAP 理论

分布式的 CAP 理论告诉我们:

任何一个分布式系统都无法同时满足一致性(Consistency)、可用性(Availability)和分区容错性(Partition tolerance),最多只能同时满足两项。

现在很多的网站都是分布式的,在分布式的场景中,数据的一致性问题是一个特别重要的话题。基于 CAP 理论,我们知道,我们需要在这三者中进行折中选择,通常都是牺牲强一致性来换取系统的高可用性,系统往往只需要保证最终的一致性。在分区容错性上是把某一个节点的数据更新到所有节点上,保证访问系统的时候可以访问地到数据。

上面的理论知识用一句话来概括就是

在系统设计时,一定要让整个系统有数据,这就是可用性;同时也一定要让用户在访问的时候能够有效访问到数据,这就是分区容错性;在所有的数据中可能会存在着小部分的不一致,在某些特定的范围内,这是可以被允许的。这就是保证最终一致性而牺牲了强一致性。

分布式锁的实现

了解了 CAP 理论以后,我们来开始设计一个分布式系统的锁,来保证分布式系统中数据的一致性。

首先,分布式锁的实现有三个常见的实现方式,一个是数据库层级的,如乐观锁;一个是 Redis中的分布式锁;一个是 ZooKeeper 中的分布式锁。本文主要是讲解 Redis 中的分布式锁实现的优势和存在的问题,讨论什么样类型的业务可以选择 Redis 分布式锁来解决。

分布式锁的演进

我们以技术的推进流程来重演一下分布式锁的历史背景,首先,为什么会出现分布式锁,这个问题的本质是分布式锁解决了什么样的问题。根据 CAP 理论,分布式锁的出现是为了解决当多个线程了来抢同一个数据的时候,只能保证被一个线程来抢到,如图:

image

我们会经常遇到的出现两个线程同时对一个数据进行争抢,如图中所示,当两个线程都同时对同一个数据进行操作的时候,就会出现关于数据操作过后错误的情况,这个时候需要一把锁来进行控制两个线程的先后顺序。

在业务中,这个锁就是一个key,只有拿到这个key的线程才能进行操作数据。

1
2
3
4
5
6
7
8
9
10
//设置 key,在这里这个 key 就是 spacedong
127.0.0.1:0>setnx spacedong true
"1"

//处理业务
...work...

//释放锁
127.0.0.1:0>del spacedong
"1"

大家可以思考一下,如果在业务处理的过程中,抛出了异常,那么这个key就没有办法删除,锁就无法释放。这种情况应该怎么解决呢?

这时需要设置一个过期时间,过期时间主要是用来解决关于在处理业务过程中,出现异常情况下没有办法删除掉 key 的情况。

1
2
3
4
5
6
7
8
9
10
11
12
13
//设置 key,在这里这个 key 就是 spacedong
127.0.0.1:0>setnx spacedong true
"1"

//设置key的过期时间为5秒
127.0.0.1:0>expire spacedong 5

//处理业务
...work

//释放锁
127.0.0.1:0>del spacedong
"1"

这样,看起来处理的逻辑已经不会出现问题了,但是,还是有隐患的,如果在setnx 和 expire 这两条指令之间出现宕机或者是人为性地 kill 掉机器,这个时候还是会出现死锁。

原因就是 setnx 和 expire 这两个命令不是原子性的命令,不是同时操作的。为了解决这个问题,
你可能会想到是使用 Redis 的事务来处理。但是这里是不行的,因为要想让 expire 来执行,得依靠 setnx 的执行结果,如果 setnx 没有设置 key 成功,那么是不会执行 expire 的。而事务是要么一起执行,要么都不执行,所以在这里无法解决。

Redis 社区出现大量的复杂的 libary 包来解决这个问题,但是对于小白来讲,这个的学习成本是特别高的。后来,在 Redis 更新的版本中,Redis 的作者 Antirez 引入了 set 的 扩展参数,可以使得 set 和 expire 同时能够执行。

1
2
3
4
5
6
// 设置key的同时也在设置过期时间
127.0.0.1>set spacedong true expire 5 nx

... work ..

127.0.0.1>del spacedong

超时问题

redis 分布式锁是没有解决超时问题的,例如,当在执行的业务逻辑比较长的时候,还没有等到业务执行完成,这个时候锁就已经过期了,就会导致这个线程放弃掉这把锁,然后第二个线程获取,这样子是会导致出现数据问题的,所以分布式锁不适合处理长时间的业务。

spacedong wechat
愿意交个朋友吗~
觉得有收获么