preface

Recently, the issue of distributed has been widely mentioned, such as distributed transactions, distributed frameworks, ZooKeeper, SpringCloud, and so on. This article first reviews the concept of locking, then introduces distributed locking, and how Redis can implement distributed locking. Redis’ learning notes are summarized, and the following is the technology related knowledge sharing.

First, the basic understanding of lock

First, let’s review the concept of locking in our work study.

Why talk about locks before we talk about distributed locks?

As we all know, the function of lock is to solve the thread safety problem caused by multi-threaded access to shared resources, but in daily life, the situation of lock is actually not much, some friends may not be very clear about the concept of lock and some basic use, so we first look at the lock, and then in-depth introduction of distributed lock.

Let’s take a quick example of selling tickets

For example, if you go to grab doTA2 TI9 tickets, what will happen if you leave them unlocked? At this point, the code is as follows:

Code analysis:

Here are 8 ti9 tickets, set up 10 threads (that is, simulation of 10 people) to concurrent grab tickets, if the success of the grab is successful, it shows that the failure of the grab failed. Logically, there should be 8 people to rob successfully, 2 people to rob failed, here is the running result:

We found that the result of the run was inconsistent with the expected situation, even 10 people bought the tickets, which means there was a thread safety issue, so what caused it?

The reason is the time difference between multiple threads.

As shown in the figure, there is only one ticket left, but the balance of tickets read by both threads is 1, which means that thread B has successfully acquired the ticket before thread A changes its inventory.

What’s the solution? “Synchronized” means that while one thread is using reduce, the other threads are blocked in the wait queue, so that multiple threads are not competing for shared variables.

For example

For example, if we go to the gym to work out, if a lot of people are using a machine at the same time, and running on a treadmill at the same time, there will be a big problem, and people will fight like cats and dogs. If we add a lock at the door of the gym, only those who have the key to the lock can go in to exercise, while others wait outside the door, so that we can avoid competition for the fitness equipment. The code is as follows:

Running result:

Sure enough, two people failed to get the tickets, so it seems that we have achieved our goal.

Second, lock performance optimization

2.1 Shorten the lock holding time

In fact, according to our understanding of everyday life, it’s impossible for an entire gym to have just one person exercising. So we just need to put a lock on a certain machine, for example, one person can run, the other person can do other sports.

For the ticketing system, we only need to lock the code for modifying the inventory. Other codes can be carried out in parallel, which will greatly reduce the holding time of the lock. The code changes are as follows:

The goal is to make full use of CPU resources and improve the efficiency of code execution.

Here we make a print of the time of the two methods:

Sure enough, locking only parts of your code greatly improves code execution.

So, after solving the thread-safety problem, we also need to consider the efficiency of code execution after locking.

2.2 Reduce lock granularity

For example, for two movies, the recently released Nezha and Spider-Man, we simulate the purchase process by making the method wait and adding an await method on CountDownLatch. The result is as follows:

Execution result:

The remaining number of votes for Nezha is 20

In fact, the two movies are independent of each other, so we need to reduce the granularity of the lock and change the lock of the entire movie object into the lock of two global variables. Modify the code as follows:

Execution result:

Nezha’s remaining votes are: 20 and Spider-man’s remaining votes are: 100

Now the tickets for the two movies will not affect each other, which is the second way to optimize the lock: to reduce the granularity of the lock. By the way, the ConcurrentHashMap in Java concurrent dispatch turns one large lock into 16 small locks, achieving efficient concurrency security through fragmentation locking.

2.3 lock separation

Lock separation is often referred to as read/write separation. We divide locks into read locks and write locks. Read locks do not block, and write locks consider concurrency.

Type of lock

  • Fair lock: ReentrantLock
  • Non-fair locks: Synchronized, ReentrantLock, and CAS
  • Synchronized
  • Optimistic lock: CAS
  • Exclusive locks: Synchronized and ReentrantLock
  • Shared lock: Semaphore

Here is not a description of the concept of each kind of lock, you can learn, lock can also be classified according to biased lock, lightweight lock, heavyweight lock.

4. Redis distributed lock

After understanding the basic concepts of lock and lock optimization, the concept of distributed lock is mainly introduced.

The figure above shows the distributed environment we built. There are three ticket purchasing items, corresponding to one inventory. Each system has multiple threads.

Of course not, because each ticketing system has its own JVM process, which is independent of each other, and synchronized can only make one system thread-safe, not distributed thread-safe.

So one middleware common to all three systems is needed to solve this problem.

In this case, we choose Redis as the distributed lock. Multiple systems set the same key in Redis. The setting can be successful only when the key does not exist, and the key will correspond to the unique identifier of one of the systems.

4.1 What points should be paid attention to in distributed locking

1) Mutual exclusion

Only one client can acquire the lock at any one time.

This is easy to understand. Only one of the systems can hold the lock.

2) anti-deadlock

If a client crashes while holding a lock and does not release the lock, then another client cannot acquire the lock, resulting in a deadlock, so make sure the client releases the lock.

In Redis, we can set the lock expiration time to ensure that deadlock does not occur.

3) Unlock by the person holding the lock

The lock and unlock must be the same client. The lock added by the thread of client A must be unlocked by the thread of client A. The client cannot unlock the lock of other clients.

4) Reentrant

After a client acquires a lock on an object, the client can acquire the lock on the object again.

4.2 Redis distributed lock process

Redis distributed lock specific process:

1) Firstly, set a key-value pair in the form of key-value in Redis by using the nature of Redis cache. Key is the name of the lock, and then multiple threads of the client compete for the lock. If the competition succeeds, set value as the unique identifier of the client. Java Learning Circles :14 201 9 080

2) A client competing for a lock needs to do two things:

  • Set the lock duration to prevent deadlocks (very critical)

The validity period needs to be continuously stress-tested according to business needs.

  • Assign a unique identifier to the client to ensure unlocking by the lock holder (very important)

So value is set to a unique identifier (such as a UUID).

3) Access shared resources

4) Release the lock. There are two ways to release the lock. The first way is to release the lock automatically after the expiration date; the second way is to judge whether you have the permission to release the lock according to the unique identifier.

4.3 Locking and unlocking

This lock

1) Lock the setnx command

Set if not exists We use the Redis command setnx. Setnx means that the lock will only be set if it does not exist.

2) Set the lock expire time to prevent deadlock expire

Lock needs two steps, think about what will be the problem?

What if the client suddenly dies after we’ve locked it? The lock will then become a lock with no expiry date, and a deadlock may occur. Although the probability of this happening is very small, once it happens, it will be very serious, so we need to combine these two steps into one step.

Fortunately, Redis3.0 has combined these two instructions into a new one.

Look at the source code in the jedis official documentation:

This is what we want!

4.3.2 unlock

  • Check whether you own the lock (for unique identification);
  • Remove the lock.

The unlocking is also a two-step process, which also has to be atomic, so it’s a one-step process.

This is not possible with Redis, but with Lua scripts.

This is a Lua script that determines if it has a lock and releases it.

Why are Lua scripts atomic? Because the Lua script is executed by Jedis using the eval() function, it will all be executed.

Five, Redis distributed lock code implementation

  • Use a context global variable to record the UUID of the person holding the lock. When unlocking, pass this UUID as a parameter to the Lua script to determine whether the lock can be unlocked.
  • If the lock is held by the current thread, the lock is successfully locked.
  • Use the eval function to execute the Lua script to ensure atomicity when unlocking.

Comparison of distributed locks

6.1 Database-based distributed Locking

1) Implementation

A lock is inserted when it is acquired, and data is deleted when it is unlocked.

2) disadvantages

  • If the database fails, the service system becomes unavailable.
  • Cannot set expiration time, resulting in deadlock.

6.2 Distributed Locks Based on ZooKeeper

1) Implementation

When a lock is created, a new node is created in the directory of the specified node. When the lock is released, the temporary node is deleted. Because of the presence of heartbeat detection, deadlocks do not occur and are more secure.

2) disadvantages

Performance is mediocre and not as efficient as Redis.

So:

  • From a performance perspective: Redis > ZooKeeper > Database
  • In terms of reliability (security) : ZooKeeper > Redis > Database

Seven,

In this paper, starting from the basic concept of the lock, there will be a multithreaded access to a Shared resource of thread safety problem, and then through the way of locking to solve the problem of thread safety, this method will be performance will decline, need to pass: shorten the holding time, reducing lock granularity of lock, lock separation three ways to optimize the lock, benefit sharing today: organize a Redis interview questions.

After that, four features of distributed lock are introduced:

  • Mutual exclusivity
  • Deadlock prevention
  • Lock the person to unlock
  • reentrancy

Then, Redis is used to implement distributed locking. When locking, Redis is used to lock the command, and when unlocking, Lua scripts are used to ensure atomicity.

Finally, the advantages and disadvantages of the three kinds of distributed locks and their use scenarios are compared.

I hope you have a new understanding of distributed locking, and I hope you think about performance as well as solving the problem.