赞
踩
在单体项目中,我们处理多线程同时操作某一处代码块或者变量时就使用 Synchronized 或者 Lock 锁去保证数据的安全性,但是,现在我们基本上都是使用微服务,当我们把服务部署到多个进程中去,这时候使用 Synchronized 或者 Lock 锁就没办法保证数据的安全性,这时候就需要用到分布式锁。
想要实现分布式锁,需要借助一个外部系统,所有进程都去这个系统上申请加锁,而这个外部系统,必须要实现互斥的能力,即两个请求同时进来,只会给一个进程返回成功,另一个返回失败(或等待)。
使用 SETNX 命令,这个命令表示SET if Not eXists,即如果 key 不存在,才会设置它的值,否则什么也不做,两个客户端进程可以执行这个命令,达到互斥,就可以实现一个分布式锁。
客户端 1 申请加锁,加锁成功:
127.0.0.1:6379> SETNX lock 1
(integer) 1 // 客户端1,加锁成功
客户端 2 申请加锁,因为它后到达,加锁失败:
127.0.0.1:6379> SETNX lock 1
(integer) 0 // 客户端2,加锁失败
此时,加锁成功的客户端,就可以去操作共享资源,例如,修改 MySQL 的某一行数据,或者调用一个 API 请求。
操作完成后,还要及时释放锁,给后来者让出操作共享资源的机会,使用 DEL 命令删除这个 key 即可释放分布式锁:
127.0.0.1:6379> DEL lock // 释放锁
(integer) 1
这个逻辑非常简单,整体的路程就是这样:
但是,它存在一个很大的问题,当客户端 1 拿到锁后,如果发生下面的场景,就会造成死锁:
1、程序处理业务逻辑异常,没及时释放锁;
2、进程挂了,没机会释放锁;
这时,这个客户端就会一直占用这个锁,而其它客户端就拿不到这把锁了。
我们很容易想到的方案是,在申请锁时,给这把锁设置一个到期时间,过了这个到期时间,就把锁释放掉。
在 Redis 中实现时,就是给这个 key 设置一个过期时间。这里我们假设,操作共享资源的时间不会超过 10s,那么在加锁时,给这个 key 设置 10s 过期即可,在 Redis 2.6.12 之后,Redis 扩展了 SET 命令的参数,用一条命令就可以实现加锁的时候同时给锁加上时间,来避免死锁的发生,命令如下
EX
后面跟的是秒,如果想要设置毫秒的话就用 PX
# 设置秒级的分布式锁
127.0.0.1:6379> SET lock 1 EX 10 NX
OK
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。