When using distributed locks to access mutually exclusive resources, many of our schemes are implemented by Redis. Of course, redis single-node locks can be problematic in extreme cases. Assuming your business allows occasional failures, using single-node Redis locks is sufficient, simple and efficient. Redis lock failure:

Client 1 obtains the lock from the master node. The master node is down, and the key for storing the lock has not been synchronized to the slave node. The slave node is upgraded to the master node

Therefore, client 1 and client 2 hold the lock on the same resource, and the lock security is broken. If we ignore this extreme case, we need to implement a rough flow based on a single-node Redis lock:

set cache_key random_seed NX PX 30000

Unpack the set command:

setnx cache_key random_seed expire cache_key 30

Although the two sets of commands execute the same effect, the second non-atomic operation, if setnx succeeds but expire fails, will cause the key to remain in existence and cannot be released.

The authors of Redis also point out that when using single-node Redis locks, it is necessary to set a random seed as the value of the key to ensure that the lock released by a client must be the same lock it owns. If the lock is set to a fixed value instead of a random number, the following situation may occur:

Client 1 succeeded in obtaining the lock. Client 1 blocked an operation for a long time. When the expiration time expires, the lock is automatically released (but client 1 still appears to be holding the lock). So if you release the lock held by client 2 and the lock held by client 2 is compromised by client 1. The key is deleted by the lock owner. The key is deleted by the lock owner. So that’s where random numbers come in. There are three steps to release the lock:

Get holds the lock

Determine if the lock is owned by itself

Delete hold lock

So, these three steps have to be atomic. Use lua script to execute, redis official has provided the script file.

    return redis.call("del",KEYS[1])
else
    return 0
end
Copy the code

The script executes by passing in the preceding random number as argv[1] and cache_key as keys[1].

@Resource private R2mClusterClient r2mClusterClient; /** * function similar to setNx, Expire time is set to expire milliseconds * * @param key lock key * @param value Specifies a unique factor within the lock time * @param expire milliseconds * @return */ private String  setLock(String key, String value, long expire) { return this.set(key, value, "NX", "PX", expire); } /** * delete the specified key value * if the value of key in r2m ==value return 1 * if the value of key in R2m ==value! Private Boolean atomDelete(String key, String value) { List<String> values = new ArrayList<>(); values.add(value); String sb = "if redis.call('get',KEYS[1])==ARGV[1] then " + " return redis.call('del',KEYS[1]) " + " else " + " return 0" + " end"; if (this.eval(sb, key, values) == 1) { return true; } return false; } private Long eval(String mobel, String key, List<String> value) { return (Long) this.r2mClusterClient.eval(mobel, key, value); } private String set(String key, String value, String nxxx, String expx, long time) { return this.r2mClusterClient.set(key, value, nxxx, expx, time); }}Copy the code

The above content hopes to help you, more free PHP factory PDF, PHP advanced architecture video materials, PHP wonderful good article can be wechat search concerns: PHP open source community

2021 Jinsanyin four big factory interview real questions collection, must see!

Four years of PHP technical articles collation collection – PHP framework

A collection of four years’ worth of PHP technical articles – Microservices Architecture

Distributed Architecture is a four-year collection of PHP technical articles

Four years of PHP technical essays – High Concurrency scenarios

Four years of elite PHP technical article collation collection – database