Steve's Blog

Talk is cheap, show me the code.

0%

Redis实现分布式锁

1. 业务场景

经常在需要防止重复调用的业务逻辑中(例如秒杀、防止重复消费等逻辑)使用分布式锁,使用redis实现时常见的实现方式。平时只是使用,但是并没有专门总结,今天来做下总结。

2. redis实现

先使用docker开启一个redis实例

1
2
3
4
5
6
# 开启6379端口映射 对数据和配置进行volume持久化 并且启动后执行
# redis-server /etc/redis/redis.conf命令
$ docker run -d -p 6379:6379 --name my-redis \
-v /mydata/redis/data:/data \
-v /mydata/redis/conf/redis.conf:/etc/redis/redis.conf \
--restart=always --network common-network redis:latest \ redis-server /etc/redis/redis.conf

进入redis内部

1
$ docker exec -it my-redis redis-cli

a. 使用 setnx + expire

1
2
3
4
5
6
7
8
9
# setnx设置kv
127.0.0.1:6379> setnx test_key dummy
OK
# expire设置过期时间
127.0.0.1:6379> expire test_key 100
(integer) 1
# 再次配置,由于key还是没有过期,所以设置返回0,表示失败
127.0.0.1:6379> setnx test_key aa
(integer) 0

缺点:setnx和expire不是原子操作,如果setnx后还没有expire操作,redis挂了,这个锁将没法过期了,影响业务。

b. 使用set 扩展命令

命令如下

1
2
# set命令格式
127.0.0.1:6379> set key value [NX|XX] [GET] [EX seconds|PX milliseconds|EXAT unix-time-seconds|PXAT unix-time-milliseconds|KEEPTTL]

例如可以通过以下命令设置

1
2
# 设置100秒超时,这是一个原子操作
127.0.0.1:6379> set test_key dummy NX EX 100

c. 使用Lua脚本实现

1
2
3
4
5
if redis.call('setnx',KEYS[1],ARGV[1]) == 1 then
redis.call('expire',KEYS[1],ARGV[2])
else
return 0
end;

redis中

3. Java实现