Redis实现分布式锁思路
常用的是redis函数是setnx(),这个应该是实现分布式锁最主要的函数。首先是将某一业务标识名作为键存到redis里,并为其设个过期时间,如果是还有加锁请求过来,先是通过setnx()看看是否能将锁的标识插入到redis里,可以的话就返回true,不可以就返回false。
此处使用Lua脚本简单的实现Redis锁的实现 :
package com.tencent.sxwx.wechat.util;
import redis.clients.jedis.Jedis;
public class DistributeLocker {
private Jedis jedis;
private Lock lock;
/**
* 加锁成功标识
*/
private static final String LOCK_SUCCESS = "1";
/**
* 脚本入参个数
*/
private static final int KEY_COUNT = 1;
/**
* 加锁脚本
*/
private static String LOCK_SCRIPT;
/**
* 解锁脚本
*/
private static String RELEASE_SCRIPT;
/**
* 初始化脚本
*/
static {
LOCK_SCRIPT = "local key = redis.call('get',KEYS[1]) if ('1' == key) then return 0 "
+ "else redis.call('set',KEYS[1],'1') redis.call('expire',KEYS[1],ARGV[1]) return 1 end";
RELEASE_SCRIPT = "if (redis.call('exists',KEYS[1])) then redis.call('del',KEYS[1]) end";
}
/**
* 定义带参构造函数
*/
public DistributeLocker (Lock lock) { }
public void lock (final String key, final int exprieTime) {
if (isLocked(key, exprieTime)) {
lock.success();
} else {
lock.failure();
}
}
/**
* 加锁逻辑
*/
private boolean isLocked(final String key, final int exprieTime) {
Object obj = jedis.eval(LOCK_SCRIPT, KEY_COUNT, key, String.valueOf(exprieTime));
if (LOCK_SUCCESS.equals(obj)) {
return true;
}
return false;
}
/**
* 解锁逻辑
*/
public void releaseLock (String key) {
jedis.eval(RELEASE_SCRIPT, KEY_COUNT, key);
}
}
package com.tencent.sxwx.wechat.util;
public interface Lock {
void success();
void failure();
}
package com.tencent.sxwx.wechat.util;
public class Test {
void purchase(String key, int secondTime){
DistributeLocker locker = new DistributeLocker(new Lock() {
@Override
public void success() {
// do something
}
@Override
public void failure() {
// do something
}
});
try {
locker.lock(key,secondTime);
} finally {
locker.releaseLock(key);
}
}
}
注:Redis分布式锁的实现方式有很多种,各有利弊,加锁的时候主要注意检查锁与加锁操作要有原子性,防止重复加锁成功。过期时间主要是为了防止未释放锁导致锁的一直存在,从而无法获取锁操作。