“This is the 10th day of my participation in the First Challenge 2022. For details: First Challenge 2022”

This article will take you through the Redisson code, how to lock and guard dog lock delay. Mainly explain part of the core code, let you understand Redisson’s Redis lock mechanism.

Redisson Lock implementation process

Redissonlock. lock(); redisson.lock ();Copy the code

Implementation of Redisson atomicity

Redisson uses some Lua scripts to implement atomic operations at its base. Redis has written about Redisson before

The Lua script

Redis introduced scripting in 2.6, allowing developers to write scripts in Lua and upload them to Redis for execution. The benefits of using scripts are as follows:

1. Reduce network overhead: the original 5 network requests can be completed with one request, and the logic of the original 5 requests can be completed on the Redis server. Scripts are used to reduce network round-trip latency. This is similar to pipes.

2. Atomic operation: Redis will execute the script as a whole and will not be inserted by other commands. Pipes are not atomic, but redis’s batch operation commands (like mset) are.

3, replace redis transaction function: Redis transaction function is very weak, and Redis Lua script almost achieved the routine transaction function, the official recommendation if you want to use redis transaction function can be replaced by Redis Lua.

The document on the official website reads:

A Redis script is transactional by definition, so everything you can do with a Redis transaction, you can also do with a script, and usually the script will be both simpler and faster.
Copy the code

Starting with Redis2.6.0, Lua scripts can be evaluated using the EVAL command through the built-in Lua interpreter. The EVAL command has the following format:

EVAL script numkeys key [key ...]  arg [arg ...]Copy the code

The script argument is a Lua script that will be run in the Redis server context. This script need not (and should not) be defined as a Lua function. The numkeys parameter specifies the number of key name parameters. Key name parameter key [key… Starting with the third argument to EVAL, which represents the Redis KEYS used in the script, these KEYS can be accessed in Lua using the global KEYS array as a base address of 1 (KEYS[1], KEYS[2], and so on).

At the end of the command, the additional arguments that are not key arguments arg [arg…] , can be accessed in Lua through the ARGV array of global variables, similar to KEYS (ARGV[1], ARGV[2], and so on). For example,

127.0.0.1:6379> eval "return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}" 2 key1 key2 first second 
1) "key1" 
2) "key2" 
3) "first" 
4) "second"
Copy the code

Return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]},ARGV[2],ARGV[2]},ARGV[1],ARGV[2]},ARGV[1],ARGV[2]},ARGV[1],ARGV[2]} The last first and second are additional parameters that can be accessed through ARGV[1] and ARGV[2].

In Lua scripts, you can use the redis.call() function to execute the redis command

See the Jedis connection example above for examples of Jedis calls:

public static void main(String[] args) { jedis.set("product_10016", "15"); String script = "local count = redis. Call ('get', KEYS[1]) " + " local a = tonumber(count) " + " local b = tonumber(ARGV[1]) " + " if a >= b then " + " redis.call('set', KEYS [1], a - b) "+ / / exception code" b = = 2 "+" return "+" 1 "+" end return 0 "; Object obj = jedis.eval(script, Arrays.asList("product_10016"), Arrays.asList("10")); System.out.println(obj); }Copy the code

Script parsing: First, set product_10016 to Redis and set the value to 15. Second, redis.call(‘get’,KEYS[1]) gets the value of the incoming key. Then, with local a = tonumber(count), the numeric conversion is performed and assigned to A. Then, with local b = tonumber(ARGV[1]), the value of the input parameter \ is obtained

If a>=b, if it is the first access, that is, 15>=10, if true, then execute redis set command, and put a-b value into the incoming KYES[1], under normal circumstances, redis key value becomes 5, at the same time send back 1. However, if the exception code b==2 comment is turned on, the code will get an exception, and then the value of redis’ set execution will be “rolled back”, that is, Redis has implemented the atomic operation of multiple instructions by executing Lua code. Note that the subscripts for Lua script arguments start at 1, which is different from Java code \

Color {red}{attention} Note: do not use the Lua script to appear infinite loop and time-consuming operations, otherwise redis will block, will not accept other commands, so use caution do not appear infinite loop, time-consuming operations. Redis is a single-process, single-thread script. The pipe does not block Redis. Redisson also uses Lua’s atomicity to implement distributed locking.

Find a way to implement the core

1, enter lock() method 2, enter lock interface lock method

3, Then look at the implementation of lock method, as follows:

4, enter RedissonLock, implement the lock method

5. Enter lockInterruptiblyThis is where two values are passed, one -1 and one NULL, but remember that 6 will be used later. Enter lockInterruptibly

Enter tryAcquireAsync

8. Enter tryLockInnerAsync9. Go to the tryLockInnerAsync core

9-1. Lua script parsing begins by looking at the meaning of the passed value

GetName is the original value, coupon_100

Look at the first line of the Lua script \

"if (redis.call('exists', KEYS[1]) == 0) then " +
Copy the code

If coupon_100 does not exist in the cache the second line of Lua script \

"redis.call('hset', KEYS[1], ARGV[2], 1); "+Copy the code

Hset coupon_100 thread_1000 1 The thread Id is: thread_1000 line 3 Lua script

"redis.call('pexpire', KEYS[1], ARGV[1]); "+Copy the code

Set coupon_100 to 30 seconds and return null by return nil

Line 6 Lua script

"if (redis.call('hexists', KEYS[1], ARGV[2]) == 1) then " +
Copy the code

If coupon_100 exists, go to line 7 of the Lua script

"redis.call('hincrby', KEYS[1], ARGV[2], 1); "+Copy the code

Give coupon_100 thread thread_1000 number of +1 line 8 of Lua script

"redis.call('pexpire', KEYS[1], ARGV[1]); "+Copy the code

Setting the expiration time of coupon_100 again finally returns coupon_100 PTTL, which is how long the key will expire

"return redis.call('pttl', KEYS[1]);" .Copy the code

First set coupon_100, Lua script through, then will by adding a listener, walk the scheduleExpirationRenewal below

Then go inside the method and you’ll see the following code

As you can see at the bottom, there is the initial lock time 30s,30s/3, that is, the timed task can be executed once in 10 seconds.

30 seconds and you can see here that it’s initialized

The listener eventually executes this method every 10 seconds (passing in the ID of the current thread) as follows:

Enter the method

Tread_100 = 0; tread_100 = 0; tread_100 = 0; tread_100 = 0; tread_100 = 0;

This part of the code to achieve the function of the lock to renew life

Redisson Distributed lock 10s polling principle

Through the above code tracking and parsing, detailed we have a certain understanding of a thread 10s polling lock, let us consider that if other threads come in how to execute? Let’s move on to the code

We then trace the steps up to see the following code:

If TTL ==null, that is, the current thread is successful. If TTL is not null, that is, the previous thread lock has not been invalid, then the next step is to spin until the TTL of the last lock expiresIf the above redis is set up in the master-slave mode, if the lock is on the master node, the master node will break down during the synchronization of the slave node. After the master is programmed by the slave through the election, the previous lock will be lost. How to achieve this? Zookeeper is recommended because it is highly consistent.

Conclusion: The sequence of code execution is quite complicated. It is suggested that you can understand the core code clearly when studying it, so that you can master the core principle of Redisson.

Segmented lock

If the number of coupon_100 coupons is one million, then if the number of coupon_100 coupons is one million, then if the number of coupon_100 coupons is one million, even if redis is a cluster, it can only operate to one node, so how to deal with it? If you want to increase performance by 10 times, you can segment coupon_100, for example, coupon_100_1/coupon_100_2/coupon_100_3….. I’m going to allocate a million coupons to these segments and I’m going to have a segmented locking of coupons. When the user obtains the lock, he can obtain an unlocked key from these segments and lock the inventory. Do you have any good ideas about that? Welcome to leave a message