redis分布式锁简易实现

关于锁
  • 锁存在的意义就是避免多个线程同时操作一个变量或数据资源时,无序和同时读写造成的数据结果异常,不可控与期望结果不一致的问题。
  • synchronized关键字,lock类等, 可以保证此锁在单个JVM中唯一,但在多个JVM(集群/分布式)的架构下就无法使用JAVA自带的解决方案来保证锁唯一。
  • 此时就需要引入一个跨JVM的共用唯一锁,也就是分布式锁。
分布式锁具备的条件
  • 在分布式系统环境下,一个方法在同一时间只能被一个机器的一个线程执行
  • 高可用的获取锁与释放锁
  • 高性能的获取锁与释放锁
  • 具备可重入特性(可理解为重新进入,由多于一个任务并发使用,而不必担心数据错误)
  • 具备锁失效机制,防止死锁
  • 具备非阻塞锁特性,即没有获取到锁将直接返回获取锁失败
实现
  • 分布式锁的实现也有很多种. 基于redis,zookeeper等等, 也可以基于数据库.
  • 本文主要说一下 使用redis做分布式锁的简单实现。
获取锁

1.使用setnx 和 expire 两条命令 来实现加锁和设置锁有效时间,
setnx 和 expire 本身是原子性的操作,但是叠加使用就是非原子操作,根据redis官网的说明可以使用set命令来实现
redis官网说明 SETNX
redis官网说明 SET

可以看到 set命令已经完全可以覆盖setnx的全部功能,同时可以设置key的有效期 秒/毫秒 可选。

  • EX seconds – Set the specified expire time, in seconds.
  • PX milliseconds – Set the specified expire time, in milliseconds.
  • NX – Only set the key if it does not already exist.
  • XX – Only set the key if it already exist.
  • EX seconds – 设置键key的过期时间,单位时秒
  • PX milliseconds – 设置键key的过期时间,单位时毫秒
  • NX – 只有键key不存在的时候才会设置key的值
  • XX – 只有键key存在的时候才会设置key的值

在SET命令页面同时也给出了释放锁的例子,使用lua脚本来实现原子性操作同时 get和del。

简单实例代码如下:

//NX – 只有键key不存在的时候才会设置key的值
//EX seconds – 设置键key的过期时间,单位时秒
String result = jedis.set(redisKey, String.valueOf("testvalue", "NX", "EX", 2);
if (result == null || !result.equalsIgnoreCase("OK")) {
}           
//用Lua 脚本 保证操作(get+del)原子性 删除可能的垃圾key
String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
Object jret = jedis.eval(script, Collections.singletonList(redisKey), Collections.singletonList(redisValue));
//jret返回值 成功是1L 未成功是0L
//"if redis.call('get', KEYS[1])==false or tonumber(redis.call('get', KEYS[1])) < tonumber(ARGV[1]) then return redis.call('del', KEYS[1]) else return 0 end"

网上找了个比较清晰的demo,供参考:
redis-lock

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。