分布式锁其实可以理解为:控制分布式系统有序的去对共享资源进行操作,通过互斥来保持一致性。通俗的讲就是,一间厕所就这一个坑,我先来了,上锁。只有我走之后,打开了锁,你才能进去,然后你在上锁。依次类推,来保持一致性。
使用redis命令 set key value NX EX(PX) max-lock-time 实现加锁
使用redis命令 EVAL 实现解锁
加锁
Jedis jedis = new Jedis("127.0.0.1", 6379);
private static final String SUCCESS = "OK";
/**
* 加锁操作
* @param key 锁标识
* @param value 客户端标识
* @param timeOut 过期时间
*/
public Boolean lock(String key,String value,Long timeOut){
String var1 = jedis.set(key,value,"NX","EX",timeOut);
if(LOCK_SUCCESS.equals(var1)){
return true;
}
return false;
}
解读:
加锁操作:jedis.set(key,value,“NX”,“EX”,timeOut)【保证加锁的原子操作】
key就是redis的key值作为锁的标识,value在这里作为客户端的标识,只有key-value都比配才有删除锁的权利【保证安全性】
通过timeOut设置过期时间保证不会出现死锁【避免死锁】
NX(XX),EX(PX)什么意思?
NX:只有这个key不存才的时候才会进行操作,if not exists;
XX:只有这个key存才的时候才会进行操作,if exists;
EX:设置key的过期时间为秒,具体时间由第5个参数决定
PX:毫秒未单位
解锁
Jedis jedis = new Jedis("127.0.0.1", 6379);
private static final Long UNLOCK_SUCCESS = 1L;
/**
* 解锁操作
* @param key 锁标识
* @param value 客户端标识
* @return
*/
public static Boolean unLock(String key,String value){
String luaScript = "if redis.call(\"get\",KEYS[1]) == ARGV[1] then return redis.call(\"del\",KEYS[1]) else return 0 end";
Object var2 = jedis.eval(luaScript,Collections.singletonList(key), Collections.singletonList(value));
if (UNLOCK_SUCCESS == var2) {
return true;
}
return false;
}
解读:
luaScript 这个字符串是个lua脚本,代表的意思是如果根据key拿到的value跟传入的value相同就执行del,否则就返回0【保证安全性】
jedis.eval(String,list,list);这个命令就是去执行lua脚本,KEYS的集合就是第二个参数,ARGV的集合就是第三参数【保证解锁的原子操作】
上面那只是讲了加锁与解锁的操作,试想一下如果在业务中去拿锁如果没有拿到是应该阻塞着一直等待还是直接返回,这个问题其实可以写一个重试机制,根据重试次数和重试时间做一个循环去拿锁,当然这个重试的次数和时间设多少合适,是需要根据自身业务去衡量的
/**
* 重试机制
* @param key 锁标识
* @param value 客户端标识
* @param timeOut 过期时间
* @param retry 重试次数
* @param sleepTime 重试间隔时间
* @return
*/
public Boolean lockRetry(String key,String value,Long timeOut,Integer retry,Long sleepTime){
Boolean flag = false;
try {
for (int i=0;i
文中set命令详解:http://redisdoc.com/string/set.html