Use Redis to implement locks (Supporting Distributed applications) (organizing network data), redis

Source: Internet
Author: User

Use Redis to implement locks (Supporting Distributed applications) (organizing network data), redis
Implement locks using Redis (supports distributed applications) 1. Introduction

Use Redis commands such as setnx, expire, and getset to access mutex Resources

The content of this article is network sorting. For details, refer:

Http://www.linuxidc.com/Linux/2014-12/110958.htm

Http://www.jeffkit.info/2011/07/1000/

Http://blog.csdn.net/java2000_wl/article/details/8740911

2. Background

In special business logic, it is necessary to ensure that there is only one thread for one operation at the same time to ensure data consistency. Prevent data from being rewritten multiple times or multiple duplicate data entries.

3. get and set commands

This method is easy to think of. When each request arrives, get is used to determine whether the lock exists. If it does not exist, set is created. This method has one drawback. Because get and set are two Redis requests, there is a latency between them. In a highly concurrent environment, it is possible that the lock has been set by another thread before the set after the get detects that the lock is not saved. Then, the current thread is set again, so that the lock will become invalid. Therefore, this method can only deal with a low concurrency.

Use the setnx and expire commands to implement

When you access resources that require mutual access, use the setnx command to set a lock key. setnx is used to determine whether the lock exists. If it does not exist, the lock is created and a success is returned, if yes, an error is returned. The server returns the error to the client and instructs the client to try again later. The expire command is used to set an expiration time for the lock, which is used to prevent the thread from crash, resulting in the lock being valid all the time, resulting in a deadlock. For example, if the lock validity period is set to 100 seconds, the lock will automatically expire after 100 seconds even if the thread crashes. (In fact, there is also a problem in this place, which is being executed in high concurrency.ExpireCommands occasionally fail (Redis socketLink issue), lock after failureIt will not automatically expire, and the value will always exist. When a deadlock occurs, subsequent retry operations will never succeed!To ensure successful execution, consider executing expire multiple times when the execution fails.)

 

Setnx lock "lock"

Expire lock 100 // If the lock is successful, set the expiration time

Do work code //Work logic code

Del lock // Delete the lock after the access to the mutex resource ends

 

Use the setnx and getset commands to add timespan + timeout (recommended)

How to solve the deadlock problem of setnx + expire? The timestamp corresponding to the lock key can be used to determine whether the lock has occurred. If the current time is greater than the lock value, the lock has expired and can be reused.

 

In this case, you cannot simply delete the lock by DEL, and then SETNX once. When multiple clients detect that the lock times out, they will try to release it, here there may be a race condition. Let's simulate this scenario:

 

The C0 operation times out, but it still holds the lock. C1 and C2 read the lock check Timestamp and found that it times out.

C1 sends DEL lock

C1 sent the SETNX lock and succeeded.

C2 sends DEL lock

C2 sends the SETNX lock and succeeds.

 

In this way, C1 and C2 get the lock! A big problem!

 

Fortunately, this problem can be avoided. Let's take a look at how the C3 client works:

 

C3 sends SETNX lock to get the lock. Since C0 still holds the lock, Redis returns a 0 to C3.

C3 sends a GET lock to check whether the lock times out. If the lock does not time out, wait or try again.

Otherwise, if the lock times out, C3 tries to obtain the lock through the following operation:

GETSET lock <current Unix time + lock timeout + 1>

If the timestamp obtained through GETSET and C3 still times out, it indicates that C3 is expected to get the lock.

If a client named C4 executed the above operation step faster than C3 before C3, then the timestamp obtained by C3 is a value that has not timed out, c3 does not get the lock as scheduled. You need to wait again or try again. Note that although C3 does not get the lock, it overrides the lock timeout value set by C4, but the impact of this tiny error is negligible.

 

Note: To make the distributed lock algorithm more stable, the lock-holding client should check whether its lock has timed out before unlocking, and then perform the DEL operation, the client may be suspended due to a time-consuming operation. when the operation is complete, the lock has been obtained by someone else because of the timeout, so you do not have to unlock it.

Pseudocode:

# Get lock

Lock = 0

While lock! = 1:

Timestamp = current Unix time + lock timeout + 1

Lock = SETNX lock. foo timestamp

If lock = 1 or (now ()> (GET lock. foo) and now ()> (GETSET lock. foo timestamp )):

Break;

Else:

Sleep (10 ms)

# Do your job

Do_job ()

# Release

If now () <GET lock. foo:

DEL lock. foo

4. The code is implemented using the setnx and expire commands.
 1 public boolean tryLock(String key, int timeout, int expiretime, int sleeptime) throws Exception { 2  3   4  5         Jedis redis = jedisPool.getResource(); 6  7         try { 8  9             long nano = System.nanoTime();10 11             do {12 13                 Long i = redis.setnx(key, "key");14 15                 jedisPool.returnResource(redis);16 17                 if (i == 1) {18 19                     redis.expire(key, expiretime);20 21                     return Boolean.TRUE;22 23                 }24 25                 if (timeout == 0) {26 27                     break;28 29                 }30 31                 Thread.sleep(sleeptime);32 33             } while ((System.nanoTime() - nano) < TimeUnit.SECONDS.toNanos(timeout));34 35             return Boolean.FALSE;36 37         } catch (RuntimeException | InterruptedException e) {38 39             if (redis != null) {40 41                 jedisPool.returnBrokenResource(redis);42 43             }44 45             throw e;46 47         }48 49     }50 51  

 

Use the setnx and getset commands to add timespan + timeout (recommended code)

 

 1 public boolean tryLock(String key, int timeout, int expiretime, int sleeptime) throws Exception { 2  3   4  5         Jedis redis = jedisPool.getResource(); 6  7         try { 8  9             long nano = System.nanoTime();10 11  12 13             do {14 15                 long timestamp = System.currentTimeMillis() + TimeUnit.SECONDS.toMillis(expiretime) + 1;16 17                 Long i = redis.setnx(key, String.valueOf(timestamp));18 19                 jedisPool.returnResource(redis);20 21                 if (i == 1) {22 23                     return Boolean.TRUE;24 25                 }26 27                 String lockVal = getString(key);28 29                 if (StringUtils.isBlank(lockVal) || !StringUtils.isNumeric(lockVal)) {30 31                     lockVal = "0";32 33                 }34 35                 if (System.currentTimeMillis() > Long.valueOf(lockVal)) {36 37                     lockVal = getAndset(key, String.valueOf(timestamp));38 39                     if (StringUtils.isBlank(lockVal) || !StringUtils.isNumeric(lockVal)) {40 41                         lockVal = "0";42 43                     }44 45                     if (System.currentTimeMillis() > Long.valueOf(lockVal)) {46 47                         return Boolean.TRUE;48 49                     }50 51                 }52 53                 if (timeout == 0) {54 55                     break;56 57                 }58 59                 Thread.sleep(sleeptime);60 61             } while ((System.nanoTime() - nano) < TimeUnit.SECONDS.toNanos(timeout));62 63             return Boolean.FALSE;64 65         } catch (RuntimeException | InterruptedException e) {66 67             if (redis != null) {68 69                 jedisPool.returnBrokenResource(redis);70 71             }72 73             throw e;74 75         }76 77     }

 

 

 

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.