[TOC]
基于Redis的分布式锁(不公平锁)
锁的应用场景
Redis其实是有事务功能的,但是Redis的事务不能回滚,具体原因就忘了,有兴趣可以看下<Redis设计与实现(第二版)>这本书.
分布式事务不会应用在MySQL等数据库中,因为这种数据库已经有完美的事务和锁机制了.
主要用在, 比如在集群机器抢购时,你没有使用Redis队列,而是在Redis的string定义一个数字作为商品总量.比如set phone_count 10000
台手机.
这时候如果出现并发修改(肯定会有并发),比如10个用户同时修改该字段decr phone_count
可能是不准确的,因为可能会很多人都在修改.
这时候就需要我们自己来实现一套基于Redis锁机制,悲观锁.
锁的实现介绍
Redis的所有命令都是具有原子性的,之所以说要用锁,是因为可能有多个用户同时修改,那么我们的得到的数据就会是不准确的.
- Ggetset: 字段存在时同时返回字段的内容
- Setnx: 字段存在时,set会失败
- Decr: 会出现小于0的负数情况
假定,超时时间为2秒.
假定商品数量为1000台 set phone_count 1000
get phone_count
如果返回值小于等于零,直接提示已抢完.- 直接声明$time=time()尝试进行加锁,
Setnx Lock $time
- 如果setnx失败,说明已有锁,执行while循环
get Lock
和while中的$time=time()对比,如果差大于2秒则为超时, 直接del Lock
,然后Setnx Lock $time
- 如果setnx成功,则为加锁成功,开始执行逻辑
decr phone_count
,如果这个过程执行超过了2秒,必然会被后面来的del Lock
掉 - 所以在逻辑执行完毕之后, 此时我们再次
get Lock
查看Redis的时间戳是否是$time,如果不相等则回滚事物. - 如果相等, 再对比当前时间戳和
get Lock
内容,如果差小于2秒,判断decr的返回值如果大于等于0则提示success同时del Lock
. - 如果当前时间戳和Lock的内容差大于2或者和之前保存的时间戳不是同一个.则提示抢购失败.或者decr的结果小于等于0,则提示抢购完毕.
如果要做公平锁的话就需要用到zset的zadd等有序队列的命令
可以使用Redis命令中混合lua的形式
这篇博文是真的不错, 一直关注博主, 跟随博主学习好多年了.
看完上面的博客之后,结合上面说的分布式锁的思路, 一套基于redis的lua脚本的形式的完成的分布式锁的代码就出现了. 这里就不累述了