Reference link: https://www.cnblogs.com/linjiqin/p/8003838.html
1: Introduction
There are three ways to implement distributed lock: 1. Optimistic database lock; 2. redis based; 3. zookeeper based. redis server is a single thread operation, which perfectly avoids the problem of multithreading synchronization and realizes atomicity.
First of all, in order to ensure the availability of distributed locks, we need to ensure that the implementation of locks meets the following four conditions at the same time: Mutual exclusion. At any time, only one client can hold the lock.
No deadlock will occur. Even if one client crashes during the lock holding period without active unlocking, it can ensure that other clients can lock later.
Fault tolerance. As long as most Redis nodes operate normally, the client can be locked and unlocked.
You have to tie the bell. Locking and unlocking must be the same client. The client cannot unlock the lock added by others.
Two: Implementation
1, lock up
-
1. For the new version of jedis (2.2.0 and above), set supports judging whether it exists and setting the expiration time
// 2.2.0 and above version set supports multi parameter setting and realizes existence judgment and expiration time setting String result = jedis.set("lockKey", "requesId", "NX", "PX", 20000); if ("OK".equals(result)){ return true; } return false;
-
2. The old version of jedis, using setnx and expire together = = > defective
Long result = jedis.setnx("lockKey", "App1Lock"); if (result == 1) { // If the program crashes suddenly here, the expiration time cannot be set, and a deadlock will occur jedis.expire("lockKey", 10000); }
-
3. Use Lua script to improve the old version of jedis.
eval Command execution Lua Code time, Lua The code will be executed as a command until eval Command execution complete, Redis Will execute other commands. So the whole is atomic // KEYS is the value of setnxMsg, and ARGV is the value of expireMsg. In fact, all parameters can be set in setnxMsg, which is obtained by KEYS [] String script = "if redis.call('setnx', KEYS[1], KEYS[2]) == 1 then return redis.call('expire', ARGV[1], ARGV[2]) else return 0 end"; LinkedList<String> setnxMsg = new LinkedList<>(); LinkedList<String> expireMsg = new LinkedList<>(); setnxMsg.add("lockKey"); setnxMsg.add("app-1-lock"); expireMsg.add("lockKey"); expireMsg.add("20"); // This is second. Object result = jedis.eval(script, setnxMsg, expireMsg); if ("1".equals(result)) { return true; } return false;
2, unlock
-
1. Direct del problem = = > anyone can delete it, which can't
jedis.del("lockKey");
-
2. Although the judgment has been made, there will still be bug s if it is executed separately
if ("app-1-lockKey".equals(jedis.get("lockKey"))){ // At this time, the key expires. If other app s acquire the lock, they will delete others' key jedis.del("lockKey"); }
-
3. The most reliable unlocking: lua expression integrates judgment and deletion
String script = "if redis.call('get', KEYS[1]) == KEYS[2] then return redis.call('del', KEYS[1]) else return 0 end"; LinkedList<String> msgList = new LinkedList<>(); msgList.add("lockKey"); msgList.add("app-1-lock"); Object result = jedis.eval(script, msgList, new LinkedList<>()); // Last parameter cannot be null if ("1".equals(result)) { return true; } return false;