Read the catalogue:
- Overview
- Distributed locks
- Multi-Instance distributed lock
- Summarize
Overview
In a multithreaded environment, locks are typically used to guarantee that there is only one thread that can manipulate shared resources. Like what:
Object obj = new object (); lock (obj) {//Operation shared Resource}
Using the locking mechanism provided by the operating system, you can ensure that concurrent and unique operations under multiple threads or processes. However, if in a multi-machine environment can not be satisfied, when a, b two machines simultaneously operate the shared resources of C machine, the third-party lock mechanism is required to ensure the coordination of resources in the distributed environment, also known as distributed locks.
Redis has three of the most basic properties to ensure effective implementation of distributed locks:
- Security: Mutex, at any time, only one client can hold a lock.
- Active A: There is no deadlock, even if the client crashes while holding the lock, there will be other clients to obtain the lock, timeout mechanism.
- Active B: Failure tolerance, only the majority of Redis nodes survive, the client can still get locks and release locks.
Distributed locks
Because Redis is a single-threaded model, commands operate atomically, so it is easy to implement distributed locks with this feature. Get a lock
SET key uuid nx px Timeoutset resource_name uniqueval nx px 30000
NX in the command indicates that if the key does not exist, it is added, and the presence is returned directly. The PX indicates the expiration time of key in milliseconds, which is 30000ms. Setting an expiration time is a sudden crash or other exception that prevents the client from acquiring a lock, causing the object lock in Redis to fail to release, causing a deadlock.
The value of key needs to be guaranteed to be a unique value in all clients that request the lock service. This is to ensure that the client that gets the lock can safely release the lock, preventing the lock object from being deleted by other clients.
As an example:
- A client gets the object lock, but is blocked for some reason and cannot release the lock in time.
- The lock object in Redis is deleted because the expiration time has expired.
- The B client request acquires the lock successfully.
- A client at this time the blocking operation is complete, delete key release lock.
- C Client request to acquire lock succeeded.
- At this point B, c all got the lock, so the distributed lock failure.
To avoid the situation in the example, it is necessary to ensure that the value of key is unique and only the client that gets the lock can delete it. For this reason, the Normal del command is not sufficient, and we need a command that can determine whether the value of the client is the same as the value of the lock object. Unfortunately, Redis does not have such a command, but it can be done through LUA scripting:
If Redis.call ("Get", keys[1]) = = Argv[1] Then
return 0 End
The logic is simple, get the value in key and the value in the parameter compare, equal delete, not equal return 0.
Multi-Instance distributed lock
The problem with implementing distributed locks on a single Redis instance is that if this instance crashes for some reason, all client lock services are invalidated.
Redis itself supports the master-slave structure, can be a master multi-slave, using a highly available method, you can ensure that the master hangs automatically switch to slave. However, due to the asynchronous synchronization of data between Master and slave, Redis does not fully implement the security of locks. For example:
- A client obtains a lock on the master instance.
- Master crashes before the object lock key is transferred to slave.
- A slave is elected as master.
- The B client can obtain a lock of the same key, but a also has the lock, causing the lock to fail.
This algorithm is implemented in multiple master cases and guarantees the security of the lock. The steps are as follows:
- The client gets the current time in milliseconds.
- Using the same key and value, loops get locks in multiple instances. To obtain a lock, the client should set an offset time that is less than the lock auto-release time (that is, the expiration time of the key). For example, if a lock auto-release time is 10 seconds, the offset time should be set to a range of 5~50 milliseconds. Prevent the client from taking too long to acquire the lock because an instance crashes or other reasons.
- Calculates the time taken to get all locks, that is, the current time minus the start time, to get a value. With the lock auto-release time minus a value, minus the offset time, the C value is obtained, if the number of successful instances is greater than half of the actual number, and C is greater than 0, then the lock is successfully obtained.
- When the lock gets successful, the lock object's effective time is the above C value.
- If the client fails for some reason, the reason may be that the above C value is negative or the number of locks succeeds is less than the number of instances to use n/2+1 as the standard (n is the number of instances). Then the locks on all instances are freed.
The above description may not be easy to understand and is represented in code as follows:
Lock Auto-release time TimeSpan ttl=new TimeSpan (0,0,0,30000)//Gets the number of lock successes int n = 0; Record start time var startTime = DateTime.Now; Acquires the lock For_each_redis on each instance ( Redis = { if (lockinstance (Redis, Resource, Val, TTL)) n + = 1; } );//offset time is 1% of the lock auto-release time, according to the above 10s is 5-50 milliseconds to launch. var drift = Convert.ToInt32 (ttl. TotalMilliseconds * 0.01); Lock object's Effective time = Lock Auto-release time-(current time-start time)-offset time var validity_time = ttl-(datetime.now-starttime)-New TimeSpan (0, 0, 0, 0, drift) ;//Determine whether the number of successes and the effective time C value is greater than 0 if (n >= (n/2+1) && validity_time. TotalMilliseconds > 0) {}
Summarize
Using Redis for distributed locks is simpler and faster than other distributed locks (zookeeper).
It is implemented directly on the Servicestack.redis client component with a support lock.
Or with the Stackexchange client component, the lock implementation and the example code: HTTPS://GITHUB.COM/KIDFASHION/REDLOCK-CS.
Official Introduction Document: Http://redis.io/topics/distlock.
Http://www.cnblogs.com/mushroom/p/4752499.html
Redis Distributed Lock Service (eight)