文章目录
简单的分布式锁实现流程
最初的版本,使用setnx命令加锁,判断加锁是否成功。–> 执行业务代码 —> 释放锁
问题: 业务代码出异常了就没有释放锁
**优化:**使用try{}finally{}包起来释放锁
**问题:**执行业务代码时服务器宕机了,锁就不会释放了
优化: 加过期时间,和setnx一起,保证操作的原子性
**问题:**业务代码执行耗时超过了锁过期时间,其他进程加锁了,前一个进程业务代码执行释放锁时把其他进程加的锁给释放掉了
**优化:**生成唯一id放value中,释放锁时判断是否相等
**问题:**校验value是否相等与释放锁不是原子性的,可能会出现高并发问题
**优化: ** 锁续命 + lua脚本保证校验value是否相等与释放锁的原子性
Lua脚本介绍
Redis2.6推出脚本功能
使用脚本的好处:
- 减少网络开销,可以一次执行多条命令
- 原子操作,保证了多条命令的原子性
- 代替redis事务功能,redis事务一般不用,官方推荐如果要使用redis的事务功能可以用redis lua替代。
在redis-cli中可以使用EVAL命令对lua脚本进行求值,EVAL命令格式如下:
EVAL script numbers key [key...] arg [arg...]
- script是一断lua脚本
- numbers的指定之后的多个参数,其中前面多少个是Key
- Key 从第三个参数开始算起,表示脚本中用到的哪些Key,这些Key是通过全局变量KEYS数组,用1为基数的访问形式KEYS[1]、KEYS[2]… …
- arg,这些不是键名参数的附加参数,可以用全局变量ARGV数组访问,ARGV[1],ARGV[2]… …
案例:
127.0.0.1:6379> eval "return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}" 2 key1 key2 arg1 arg2
1) "key1"
2) "key2"
3) "arg1"
4) "arg2"
java代码案例
// 一个扣减库存的操作,把剩余库存和要减的数量先变为能比较的数字型,然后在进行比较和减法操作
jedis.set("product_stock_10016", "15"); //初始化商品10016的库存
String script = " local count = redis.call('get', KEYS[1]) " +
" local a = tonumber(count) " +
" local b = tonumber(ARGV[1]) " +
" if a >= b then " +
" redis.call('set', KEYS[1], a-b) " +
" return 1 " +
" end " +
" return 0 ";
Object obj = jedis.eval(script, Arrays.asList("product_stock_10016"), Arrays.asList("10"));
System.out.println(obj);
Redisson实现分布式锁原理
基本使用
引入依赖
<!--使用redisson作为分布式锁-->
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>3.6.5</version>
</dependency>
配置Redisson
@Configuration
public class RedissonConfig {
@Bean
public Redisson redissonClient() {