数据库锁
基于数据库实现分布式锁
要实现分布式锁,最简单的方式就是创建一张锁表,然后通过操作该表中的数据来实现。当我们要锁住某个资源时,就在该表中增加一条记录,想要释放锁的时候就删除这条记录。数据库对共享资源做了唯一性约束,如果有多个请求被同时提交到数据库的话,数据库会保证只有一个操作可以成功,操作成功的那个线程就获得了访问共享资源的锁,可以进行操作。基于数据库实现的分布式锁,是最容易理解的。但是,因为数据库需要落到硬盘上,频繁读取数据库会导致 IO 开销大,因此这种分布式锁适用于并发量低,对性能要求低的场景。对于双 11、双 12 等需求量激增的场景,数据库锁是无法满足其性能要求的。而在平日的购物中,我们可以在局部场景中使用数据库锁实现对资源的互斥访问。
案例:电商
下面,我们还是以电商售卖吹风机的场景为例。吹风机库存是 2 个,有 5 个来自不同地区的用户 {A,B,C,D,E} 想要购买,其中用户 A 想买 1 个,用户 B 想买 2 个,用户 C 想买 1 个。 用户 A 和用户 B 几乎同时下单,但用户 A 的下单请求最先到达服务器。因此,该商家的产品数据库中增加了一条关于用户 A 的记录,用户 A 获得了锁,他的订单请求被处理,服务 器修改吹风机库存数,减去 1 后还剩下 1 个。
当用户 A 的订单请求处理完成后,有关用户 A 的记录被删除,服务器开始处理用户 B 的订 单请求。这时,库存只有 1 个了,无法满足用户 B 的订单需求,因此用户 B 购买失败。从数据库中,删除用户 B 的记录,服务器开始处理用户 C 的订单请求,库存中 1 个吹风机 满足用户 C 的订单需求。所以,数据库中增加了一条关于用户 C 的记录,用户 C 获得了锁,他的订单请求被处理,服务器修改吹风机数量,减去 1 后还剩下 0 个。
可以看出,基于数据库实现分布式锁比较简单,绝招在于创建一张锁表,为申请者在锁表里建立一条记录,记录建立成功则获得锁,消除记录则释放锁。该方法依赖于数据库,主要有两个缺点:
- 单点故障问题。一旦数据库不可用,会导致整个系统崩溃。
- 死锁问题。数据库锁没有失效时间,未获得锁的进程只能一直等待已获得锁的进程主动释放锁。一旦已获得锁的进程挂掉或者解锁操作失败,会导致锁记录一直存在数据库中,其他进程无法获得锁。