redis分布式锁

一般而言,在对数据进行“加锁”时,程序首先要获取锁来得到对数据进行排他性访问的能力,然后才能对数据执行一系列操作,最后还要将锁释放给其他程序。

传统上,redis采用一种称之为“乐观锁”的方式对数据进行加锁,采用watch命令来监视数据,watch只会在其他客户端抢先修改了的情况下通知执行这个命令的客户端,然后这个客户端进行相应的操作。(这种情况下,所有客户端都能访问数据,并进行修改)

性能问题:一般而言,如果watch的数据改变,那么监视的客户端,会重新定位watch处重新运行,那么之前运行过的命令就会重新执行。

redis分布式锁:这种锁不是给同一个进程的多线程去竞争的,也不是同一近期的多进程去竞争的,而是由不同机器上的客户端去竞争的。

redis分布式锁

1. 锁的重要性

redis分布式锁

market是市场,user是玩家,inventory是玩家包裹里拥有的物品,本次我们要解决一个问题:在市场里购买物品。

watch

我们先进行物品上架的功能

redis分布式锁

redis分布式锁

redis分布式锁

redis分布式锁

redis分布式锁

redis分布式锁

锁介绍

我们开始第一版的锁实现,这个锁非常简单。注意我们刚开始构建锁时,并不会立即处理那些可能会导致锁无法正常工作的问题,而是先构建出可以运行的锁获取操作和锁释放过程。

redis分布式锁

虽然redis用户对锁,加锁以及锁超时有所了解,遗憾的是,大部分redis实现的锁只是基本上正确,它们发生故障的时间和方式通常难以预料,下面列出了锁的不正确行为的原因:

redis分布式锁

简易锁

redis分布式锁

redis分布式锁

redis分布式锁

redis分布式锁

这就是锁的解决方法,有一个问题,就是我们锁所包裹的范围太宽了,严格来说,只有在操作市场数据的时候才能加锁,这种较宽的锁机制降低了性能。

我们看看释放锁的代码:

redis分布式锁

redis分布式锁

(上面这句话没太看懂)

让我们看看现在的性能表现

redis分布式锁

目前来说,不同上架(需要商场的锁)和买入进程(也需要商场的锁)之间的竞争限制了商品购买操作性能的进一步提升(5倍),而接下来将介绍细粒度锁。

细粒度锁

redis分布式锁

redis分布式锁

通过以上实验可以看出,在高负载的情况下,使用锁可以减少重试次数、降低延迟时间、提升性能并将加锁的粒度调整到合适的大小。

超时限制特性的锁

有一个问题,目前的锁,持有者崩溃的时候不会自动释放,这将导致锁一直处于获取状态。

为了给锁加上超时特性,调用expire来设置过期时间。为了确保锁在客户端崩溃的情况下,仍能正常释放,客户端会在尝试获取锁失败后,为未设置超时时间的锁设置超时时间。

来我们看看,添加时间设置之后的锁。

redis分布式锁

 

计数信号量

计数信号量是一种锁,它可以让用户限制一项资源最多能够被多少个进程访问。同时,不同于锁,当客户端获取信号失败时,客户端通常会选择立即返回结果。

构建基本的计数信号量

1. 判断哪个客户端获得了锁;

2. 处理客户端获得锁之后崩溃的情况;

3. 如何处理锁超时的问题。

redis分布式锁

redis分布式锁

redis分布式锁

redis分布式锁

公平信号量

当各个系统的系统时间并不完全相同的时候,前面介绍的基本信号量就会出现问题,系统时间较慢的系统上运行的客户端可以偷走快的客户端上面的信号量。

所以,我们需要减少这种影响,使得只要时间差不超过1秒,就不会偷或者提前过期。

为了解决这个问题,我们给信号量添加一个计数器以及一个有序集合,确保先对计数器执行自增操作的客户端能够获得信号量(也就是说程序会将计数器生成的值用作分值)

redis分布式锁

redis分布式锁

删除公平信号量

redis分布式锁

这段话很绕(翻译的问题?)

redis分布式锁

刷新信号量

redis分布式锁

redis分布式锁

消除竞争条件

竞争条件可能会导致操作重试或者数据出错,而解决竞争条件并不容易。

redis分布式锁

redis分布式锁