实现锁的关键点:1.独占性;2.避免死锁
思路很多,举几个例子:
1.INCR
思路:如果 key 不存在,那么 key 的值会先被初始化为 0 ,然后再执行 INCR 操作进行加一。然后其它用户在执行 INCR 操作进行加一时,如果返回的数大于 1 ,说明这个锁正在被使用当中。
独占性:返回值为1才算获得了锁,因为redis的命令具有原子性,即使多个客户端同时执行,只会有一个客户端的返回值为1。
避免死锁:expire设置过期时间,过期后锁失效。
1、 客户端A请求服务器获取key的值为1表示获取了锁
2、 客户端B也去请求服务器获取key的值为2表示获取锁失败
3、 客户端A执行代码完成,删除锁
4、 客户端B在等待一段时间后在去请求的时候获取key的值为1表示获取锁成功
5、 客户端B执行代码完成,删除锁
incr lockid;
expire lockid 5; //设置5秒后过期
2.SETNX
思路:如果 key 不存在,将 key 设置为 value。如果 key 已存在,则 SETNX
不做任何动作。
独占性:如果key已存在,其他客户端通过setnx无法获取锁。
避免死锁:expire设置过期时间,过期后锁失效。
1、 客户端A请求服务器设置key的值,如果设置成功就表示加锁成功
2、 客户端B也去请求服务器设置key的值,如果返回失败,那么就代表加锁失败
3、 客户端A执行代码完成,删除锁
4、 客户端B在等待一段时间后在去请求设置key的值,设置成功
5、 客户端B执行代码完成,删除锁
setnx lockid pid;
expire lockid 5; //设置5秒后过期
新问题:借助 expire 来设置过期时间,获取锁就不是原子性操作了。如果代码执行出现意外,只创建了锁,没有执行expire,锁将会一直存在。
解决思路:SET
命令本身已经从版本 2.6.12 开始包含了设置过期时间的功能,redis的命令具有原子性。
3.SET
思路:类似SETNX,SET还可以设置过期时间。SET key value [EX seconds|PX milliseconds] [NX|XX]。
独占性:[NX|XX]中NX参数表示key不存在时,才能创建key。
避免死锁:[EX seconds|PX milliseconds]中,设置过期时间的单位是秒还是毫秒。
1、 客户端A请求服务器设置key的值,如果设置成功就表示加锁成功
2、 客户端B也去请求服务器设置key的值,如果返回失败,那么就代表加锁失败
3、 客户端A执行代码完成,删除锁
4、 客户端B在等待一段时间后在去请求设置key的值,设置成功
5、 客户端B执行代码完成,删除锁
set lockid pid ex 5 nx;
新问题:如果锁过期后,客户端A还没执行完,然后客户端B获取到了锁,这时候客户端A执行完了,会不会在删锁的时候把B的锁给删掉?
解决思路:key命名为锁id,value设置为客户端id,客户端要删除锁之前,先获取锁的value,对比自己的客户端id,再判断要不要删除。
这一部分大家在业务代码里面自行实现。