Example analysis of the implementation principle of redis distributed lock
First of all, in order to ensure that distributed locks are available, we must at least ensure that the lock implementation meets the following four conditions at the same time:
1. Mutual exclusivity. At any time, only one client can hold the lock.
2. No deadlock will occur. Even if a client crashes while holding the lock without actively unlocking it, it is guaranteed that other clients can subsequently lock it.
3. Fault-tolerant. As long as most Redis nodes are running normally, the client can lock and unlock.
4. To untie the bell, you must also tie the bell. The locking and unlocking must be done by the same client. The client itself cannot unlock the lock added by others.
The following is the code implementation. First, we need to introduce the Jedis
open source component through Maven, and add the following code to the pom.xml
file:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>3.1.0</version> </dependency>
Distributed lock implementation code, DistributedLock.java
import redis.clients.jedis.Jedis; import redis.clients.jedis.JedisPool; import redis.clients.jedis.JedisPoolConfig; import redis.clients.jedis.Transaction; import redis.clients.jedis.exceptions.JedisException; import java.util.List; import java.util.UUID; /** * @author swadian * @date 2022/3/4 * @Version 1.0 * @describetion Redis分布式锁原理 */ public class DistributedLock { //redis连接池 private static JedisPool jedisPool; static { JedisPoolConfig config = new JedisPoolConfig(); // 设置最大连接数 config.setMaxTotal(200); // 设置最大空闲数 config.setMaxIdle(8); // 设置最大等待时间 config.setMaxWaitMillis(1000 * 100); // 在borrow一个jedis实例时,是否需要验证,若为true,则所有jedis实例均是可用的 config.setTestOnBorrow(true); jedisPool = new JedisPool(config, "192.168.3.27", 6379, 3000); } /** * 加锁 * @param lockName 锁的key * @param acquireTimeout 获取锁的超时时间 * @param timeout 锁的超时时间 * @return 锁标识 * Redis Setnx(SET if Not eXists) 命令在指定的 key 不存在时,为 key 设置指定的值。 * 设置成功,返回 1 。 设置失败,返回 0 。 */ public String lockWithTimeout(String lockName, long acquireTimeout, long timeout) { Jedis jedis = null; String retIdentifier = null; try { // 获取连接 jedis = jedisPool.getResource(); // value值->随机生成一个String String identifier = UUID.randomUUID().toString(); // key值->即锁名 String lockKey = "lock:" + lockName; // 超时时间->上锁后超过此时间则自动释放锁 毫秒转成->秒 int lockExpire = (int) (timeout / 1000); // 获取锁的超时时间->超过这个时间则放弃获取锁 long end = System.currentTimeMillis() + acquireTimeout; while (System.currentTimeMillis() < end) { //在获取锁时间内 if (jedis.setnx(lockKey, identifier) == 1) {//关键:设置锁 jedis.expire(lockKey, lockExpire); // 返回value值,用于释放锁时间确认 retIdentifier = identifier; return retIdentifier; } // ttl以秒为单位返回 key 的剩余过期时间,返回-1代表key没有设置超时时间,为key设置一个超时时间 if (jedis.ttl(lockKey) == -1) { jedis.expire(lockKey, lockExpire); } try { Thread.sleep(10); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } } catch (JedisException e) { e.printStackTrace(); } finally { if (jedis != null) { jedis.close(); } } return retIdentifier; } /** * 释放锁 * @param lockName 锁的key * @param identifier 释放锁的标识 * @return */ public boolean releaseLock(String lockName, String identifier) { Jedis jedis = null; String lockKey = "lock:" + lockName; boolean retFlag = false; try { jedis = jedisPool.getResource(); while (true) { // 监视lock,准备开始redis事务 jedis.watch(lockKey); // 通过前面返回的value值判断是不是该锁,若是该锁,则删除,释放锁 if (identifier.equals(jedis.get(lockKey))) { Transaction transaction = jedis.multi();//开启redis事务 transaction.del(lockKey); List<Object> results = transaction.exec();//提交redis事务 if (results == null) {//提交失败 continue;//继续循环 } retFlag = true;//提交成功 } jedis.unwatch();//解除监控 break; } } catch (JedisException e) { e.printStackTrace(); } finally { if (jedis != null) { jedis.close(); } } return retFlag; } }
In order to verify it, we create the SkillService.java business class
import lombok.extern.slf4j.Slf4j; @Slf4j public class SkillService { final DistributedLock lock = new DistributedLock(); public static final String LOCK_KEY = "lock_resource"; int n = 500; /** * 线程业务方法 */ public void seckill() { // 返回锁的value值,供释放锁时候进行判断 String identifier = lock.lockWithTimeout(LOCK_KEY, 5000, 1000); log.info("线程:"+Thread.currentThread().getName() + "获得了锁"); log.info("剩余数量:{}",--n); lock.releaseLock(LOCK_KEY, identifier); } }
If the @Slf4j log cannot be found, inpom.xml
Add the following code to the file:
<!--@Slf4j日志依赖组件--> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency>
Edit a test class TestLock.java
/** * @author swadian * @date 2022/3/4 * @Version 1.0 */ public class TestLock { public static void main(String[] args) { SkillService service = new SkillService(); for (int i = 10; i < 60; i++) { //开50个线程 SkillThread skillThread = new SkillThread(service, "skillThread->" + i); skillThread.start(); } } } class SkillThread extends Thread { private SkillService skillService; public SkillThread(SkillService skillService, String skillThreadName) { super(skillThreadName); this.skillService = skillService; } @Override public void run() { skillService.seckill(); } }
The test results show that the remaining numbers after locking are all sequential and serial, 499,498,497...
We modify the SkillService.java business class and comment out the locking logic
@Slf4j public class SkillService { final DistributedLock lock = new DistributedLock(); public static final String LOCK_KEY = "lock_resource"; int n = 500; /** * 线程业务方法 */ public void seckill() { // 返回锁的value值,供释放锁时候进行判断 //String identifier = lock.lockWithTimeout(LOCK_KEY, 5000, 1000); log.info("线程:"+Thread.currentThread().getName() + "获得了锁"); log.info("剩余数量:{}",--n); //lock.releaseLock(LOCK_KEY, identifier); } }
Re-execute the test and after commenting out the locking logic, the remaining quantities are all messed up In sequence, 472,454,452...
The above is the detailed content of Example analysis of the implementation principle of redis distributed lock. For more information, please follow other related articles on the PHP Chinese website!

Hot AI Tools

Undresser.AI Undress
AI-powered app for creating realistic nude photos

AI Clothes Remover
Online AI tool for removing clothes from photos.

Undress AI Tool
Undress images for free

Clothoff.io
AI clothes remover

Video Face Swap
Swap faces in any video effortlessly with our completely free AI face swap tool!

Hot Article

Hot Tools

Notepad++7.3.1
Easy-to-use and free code editor

SublimeText3 Chinese version
Chinese version, very easy to use

Zend Studio 13.0.1
Powerful PHP integrated development environment

Dreamweaver CS6
Visual web development tools

SublimeText3 Mac version
God-level code editing software (SublimeText3)

Hot Topics

Redis cluster mode deploys Redis instances to multiple servers through sharding, improving scalability and availability. The construction steps are as follows: Create odd Redis instances with different ports; Create 3 sentinel instances, monitor Redis instances and failover; configure sentinel configuration files, add monitoring Redis instance information and failover settings; configure Redis instance configuration files, enable cluster mode and specify the cluster information file path; create nodes.conf file, containing information of each Redis instance; start the cluster, execute the create command to create a cluster and specify the number of replicas; log in to the cluster to execute the CLUSTER INFO command to verify the cluster status; make

How to clear Redis data: Use the FLUSHALL command to clear all key values. Use the FLUSHDB command to clear the key value of the currently selected database. Use SELECT to switch databases, and then use FLUSHDB to clear multiple databases. Use the DEL command to delete a specific key. Use the redis-cli tool to clear the data.

To read a queue from Redis, you need to get the queue name, read the elements using the LPOP command, and process the empty queue. The specific steps are as follows: Get the queue name: name it with the prefix of "queue:" such as "queue:my-queue". Use the LPOP command: Eject the element from the head of the queue and return its value, such as LPOP queue:my-queue. Processing empty queues: If the queue is empty, LPOP returns nil, and you can check whether the queue exists before reading the element.

Using Redis to lock operations requires obtaining the lock through the SETNX command, and then using the EXPIRE command to set the expiration time. The specific steps are: (1) Use the SETNX command to try to set a key-value pair; (2) Use the EXPIRE command to set the expiration time for the lock; (3) Use the DEL command to delete the lock when the lock is no longer needed.

Using the Redis directive requires the following steps: Open the Redis client. Enter the command (verb key value). Provides the required parameters (varies from instruction to instruction). Press Enter to execute the command. Redis returns a response indicating the result of the operation (usually OK or -ERR).

The best way to understand Redis source code is to go step by step: get familiar with the basics of Redis. Select a specific module or function as the starting point. Start with the entry point of the module or function and view the code line by line. View the code through the function call chain. Be familiar with the underlying data structures used by Redis. Identify the algorithm used by Redis.

Use the Redis command line tool (redis-cli) to manage and operate Redis through the following steps: Connect to the server, specify the address and port. Send commands to the server using the command name and parameters. Use the HELP command to view help information for a specific command. Use the QUIT command to exit the command line tool.

On CentOS systems, you can limit the execution time of Lua scripts by modifying Redis configuration files or using Redis commands to prevent malicious scripts from consuming too much resources. Method 1: Modify the Redis configuration file and locate the Redis configuration file: The Redis configuration file is usually located in /etc/redis/redis.conf. Edit configuration file: Open the configuration file using a text editor (such as vi or nano): sudovi/etc/redis/redis.conf Set the Lua script execution time limit: Add or modify the following lines in the configuration file to set the maximum execution time of the Lua script (unit: milliseconds)
