Hello everyone, meet again, I'm your friend Quanzhanjun.
1. Introduction to Redis integration
Redis is a very frequently used nosql database in our Java development. Data is stored in memory in the form of key-value pairs. The common usage scenarios of redis can be used for caching, distributed locks, self-incrementing sequences, etc. The way to use redis is similar to the way we use databases. First, we need to install a redis server on our own local computer or server. Our java client is integrated in the program, and then the addition, deletion, modification, and query operations of redis are completed through the client. There are still many types of Java clients for redis, such as jedis, redission,lettuce, etc., so when we integrate, we can choose to directly integrate these native clients. But the more common way in springBoot is to integrate spring-data-redis, which is a project provided by spring for operating redis, which encapsulates common operations on redis, and mainly encapsulates two clients, jedis and lettuce. It is equivalent to adding a layer of facade on their basis.
In this article, we will focus on the common operations used by springBoot for redis by integrating spring-data-redis.
Since there are no compatibility issues involved, we will develop directly on the feature/MybatisPlus branch.
2. Integration steps
2.1 Add dependencies
Add dependencies required for redis:
copy<!-- integrated redis rely --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency>
full pom.xml
copy<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.lsqingfeng.springboot</groupId> <artifactId>springboot-learning</artifactId> <version>1.0.0</version> <properties> <maven.compiler.source>8</maven.compiler.source> <maven.compiler.target>8</maven.compiler.target> </properties> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-dependencies</artifactId> <version>2.6.2</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- https://mvnrepository.com/artifact/org.projectlombok/lombok --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.22</version> <scope>provided</scope> </dependency> <!-- mybatis-plus required dependencies --> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.5.1</version> </dependency> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-generator</artifactId> <version>3.5.1</version> </dependency> <dependency> <groupId>org.freemarker</groupId> <artifactId>freemarker</artifactId> <version>2.3.31</version> </dependency> <!-- development hot start --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <optional>true</optional> </dependency> <!-- MySQL connect --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> <!-- integrated redis rely --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> </dependencies> </project>
Here we directly introduce spring-boot-starter-data-redis, a starter that springBoot itself has provided. We can click to see which dependencies are included in this starter:

It can be found that it contains two core packages, spring-data-redis and lettuce-core, which is why our spring-boot-starter-data-redis uses the lettuce client by default.
What if we want to use the jedis client? It is necessary to exclude the lettuce dependency, and then introduce the related dependencies of jedis.
So why do we only need to introduce different dependencies to allow spring-data-redis to switch clients freely? This actually involves the automatic configuration principle of springBoot. We can briefly explain it to you.
One of the main reasons why the springBoot framework can seamlessly integrate other technologies through various starter s is the automatic configuration function of springBoot itself. The so-called automatic configuration is that springBoot itself has pre-set integration classes of some common frameworks. Then use conditional judgment annotations like ConditionOn to identify whether there is a related class (or configuration) in your project, and then initialize the related configuration.
The automatic configuration classes preset by springBoot are located in the spring-boot-autoconfigure package. As long as we build the springBoot project, this package will be introduced.

Under this package, there is a RedisAutoConfiguration class, which, as the name suggests, is the automatic configuration of Redis. In this class, two configuration classes, LettuceConnectionConfiguration and JedisConnectionConfiguration, will be introduced, corresponding to lettuce and jedis clients respectively.

Both of these classes use the ConditionOn annotation to determine whether to load.

jedis is as follows;

Since our project automatically introduces lettuce-core without introducing jedis-related dependencies, the LettuceConnectionConfiguration class will be loaded if the judgment is established, but the judgment of Jedis will not be established, so it will not be loaded. Then the configuration of lettuce takes effect, so what we are using is the client of lettuce by default.
2.2 Add configuration
Then we need to configure the account password and other information required to connect to redis. Here you must install redis in advance to ensure that our native program can connect to our redis. If you do not know how to install redis, you can refer to the article: [Linux system installation redis6.0.5] https://blog.csdn.net/lsqingfeng/article/details/107359076
The general configuration is as follows: Configure the connection information of redis in the application.yml configuration file
copyspring: redis: host: localhost port: 6379 password: 123456 database: 0
If there are other configurations put together:
copyserver: port: 19191 spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/springboot_learning?serverTimezone=Asia/Shanghai&characterEncoding=utf-8 username: root password: root redis: host: localhost port: 6379 password: 123456 database: 0 lettuce: pool: max-idle: 16 max-active: 32 min-idle: 8 devtools: restart: enable: true third: weather: url: http://www.baidu.com port: 8080 username: test cities: - Beijing - Shanghai - Guangzhou list[0]: aaa list[1]: bbb list[2]: ccc
In this way, we can operate redis directly in the project. If you are using a cluster, use the following configuration:
copyspring: redis: password: 123456 cluster: nodes: 10.255.144.115:7001,10.255.144.115:7002,10.255.144.115:7003,10.255.144.115:7004,10.255.144.115:7005,10.255.144.115:7006 max-redirects: 3
But sometimes we want to configure a connection pool for our redis client. Just like when we connect to mysql, we will also configure the connection pool. The purpose is to increase the management of data connections, improve the efficiency of access, and ensure the rational use of resources. So how do we configure the connection pool? Everyone must pay attention here. In many online articles, the methods introduced may not be particularly accurate because the version is too low. For example, many people use spring.redis.pool to configure, which is wrong (it is not clear whether the old version is configured like this, but it is wrong in springboot-starter-data-redis). The first is the configuration file. Since we use the lettuce client, when configuring, add lettuce and pool under spring.redis to configure, as follows;
copyspring: redis: host: 10.255.144.111 port: 6379 password: 123456 database: 0 lettuce: pool: max-idle: 16 max-active: 32 min-idle: 8
If you are using jedis, replace lettuce with jedis (at the same time, pay attention to the need to change the dependencies).
But only if this is added in the configuration file, the connection pool will not take effect. Everyone must pay attention here. Many students add this paragraph to the configuration file and think that the connection pool has been configured. In fact, it is not. The most critical step is missing, which is to import a dependency. If you don't import it, configure it like this didn't work either.
copy<dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-pool2</artifactId> </dependency>
After that, the connection pool will take effect. We can make a comparison. Before and after importing the package, it can be seen by observing the value of the RedisTemplate object.
Before importing:

After importing:

After entering the job, our connection pool information is valuable, which also confirms our above conclusion.
For specific configuration information, we can look at the source code. The RedisProperties class is used in the source code to receive the configuration parameters of redis.

2.3 Use in the project
After our configuration work is ready, we can operate redis in the project. If we operate, we can use the RedisTemplate class provided for us in spring-data-redis. Let's start with a simple example, insert a key-value pair (value string).
copypackage com.lsqingfeng.springboot.controller; import com.lsqingfeng.springboot.base.Result; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; /** * @className: RedisController * @description: * @author: sh.Liu * @date: 2022-03-08 14:28 */ @RestController @RequestMapping("redis") public class RedisController { private final RedisTemplate redisTemplate; public RedisController(RedisTemplate redisTemplate) { this.redisTemplate = redisTemplate; } @GetMapping("save") public Result save(String key, String value){ redisTemplate.opsForValue().set(key, value); return Result.success(); } }
3. Tool package
We have successfully operated the redis server through RedisTemplate in the previous code. For example, to set a string, we can use:
copyredisTemplate.opsForValue().set(key, value);
To put a key-value pair of type String. redis can support five data formats of string, list, hash, set, and zset. The common operations of these five data formats are encapsulated in the RedisTemplate class. The operation of string type is to use opsForValue, the operation of list type is to use listOps, the operation of set type is to use setOps and so on. We can get a general idea of what the functions are by looking at the source code in the RedisTemplate class.

These functions are all in this class, which is not very convenient to use. In general, we encapsulate a separate tool class to abstract some commonly used methods. When operating, operate directly through the tool class.
copypackage com.lsqingfeng.springboot.utils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Component; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.TimeUnit; /** * @className: RedisUtil * @description: * @author: sh.Liu * @date: 2022-03-09 14:07 */ @Component public class RedisUtil { @Autowired private RedisTemplate redisTemplate; /** * Append an expiration time to a specified key value * * @param key * @param time * @return */ public boolean expire(String key, long time) { return redisTemplate.expire(key, time, TimeUnit.SECONDS); } /** * Get expiration time based on key * * @param key * @return */ public long getTime(String key) { return redisTemplate.getExpire(key, TimeUnit.SECONDS); } /** * Get expiration time based on key * * @param key * @return */ public boolean hasKey(String key) { return redisTemplate.hasKey(key); } /** * Remove the expiration time of the specified key * * @param key * @return */ public boolean persist(String key) { return redisTemplate.boundValueOps(key).persist(); } //- - - - - - - - - - - - - - - - - - - - - String type - - - - - - - - - - - - - - - - - - - - - /** * Get value by key * * @param key key * @return value */ public Object get(String key) { return key == null ? null : redisTemplate.opsForValue().get(key); } /** * put the value in the cache * * @param key key * @param value value * @return true success false failure */ public void set(String key, String value) { redisTemplate.opsForValue().set(key, value); } /** * put the value into the cache and set the time * * @param key key * @param value value * @param time time (seconds) -1 for indefinite * @return true success false failure */ public void set(String key, String value, long time) { if (time > 0) { redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS); } else { redisTemplate.opsForValue().set(key, value); } } /** * Add keys in batches (duplicate keys will be overwritten) * * @param keyAndValue */ public void batchSet(Map<String, String> keyAndValue) { redisTemplate.opsForValue().multiSet(keyAndValue); } /** * Add key-value in batches and add only when the key does not exist * map As long as there is one key in the key, all are not added * * @param keyAndValue */ public void batchSetIfAbsent(Map<String, String> keyAndValue) { redisTemplate.opsForValue().multiSetIfAbsent(keyAndValue); } /** * Add or subtract the value of a key-value, * If the key does not exist, a key will be created and the number will be assigned * If key exists, but value is not a long, an error will be reported * * @param key * @param number */ public Long increment(String key, long number) { return redisTemplate.opsForValue().increment(key, number); } /** * Add or subtract the value of a key-value, * If the key does not exist, a key will be created and the number will be assigned * If the key exists, but the value is not a pure number, an error will be reported * * @param key * @param number */ public Double increment(String key, double number) { return redisTemplate.opsForValue().increment(key, number); } //- - - - - - - - - - - - - - - - - - - - - set type - - - - - - - - - - - - - - - - - - - - - /** * Put data into set cache * * @param key key * @return */ public void sSet(String key, String value) { redisTemplate.opsForSet().add(key, value); } /** * get the value of a variable * * @param key key * @return */ public Set<Object> members(String key) { return redisTemplate.opsForSet().members(key); } /** * Randomly get a specified number of elements in a variable * * @param key key * @param count value * @return */ public void randomMembers(String key, long count) { redisTemplate.opsForSet().randomMembers(key, count); } /** * Randomly get elements in a variable * * @param key key * @return */ public Object randomMember(String key) { return redisTemplate.opsForSet().randomMember(key); } /** * pop element in variable * * @param key key * @return */ public Object pop(String key) { return redisTemplate.opsForSet().pop("setValue"); } /** * Get the length of the value in a variable * * @param key key * @return */ public long size(String key) { return redisTemplate.opsForSet().size(key); } /** * Query from a set according to value, whether it exists * * @param key key * @param value value * @return true exists false does not exist */ public boolean sHasKey(String key, Object value) { return redisTemplate.opsForSet().isMember(key, value); } /** * Checks if the given element is in a variable. * * @param key key * @param obj element object * @return */ public boolean isMember(String key, Object obj) { return redisTemplate.opsForSet().isMember(key, obj); } /** * Transfers the element value of the variable to the destination variable. * * @param key key * @param value element object * @param destKey element object * @return */ public boolean move(String key, String value, String destKey) { return redisTemplate.opsForSet().move(key, value, destKey); } /** * Batch remove elements from the set cache * * @param key key * @param values value * @return */ public void remove(String key, Object... values) { redisTemplate.opsForSet().remove(key, values); } /** * Find the difference between 2 set variables by the given key * * @param key key * @param destKey key * @return */ public Set<Set> difference(String key, String destKey) { return redisTemplate.opsForSet().difference(key, destKey); } //- - - - - - - - - - - - - - - - - - - - - hash type - - - - - - - - - - - - - - - - - - - - - /** * add cache * * @param key key * @param map key * @return */ public void add(String key, Map<String, String> map) { redisTemplate.opsForHash().putAll(key, map); } /** * Get all hashkey s and value s under key * * @param key key * @return */ public Map<Object, Object> getHashEntries(String key) { return redisTemplate.opsForHash().entries(key); } /** * Verify whether there is a specified hashkey under the specified key * * @param key * @param hashKey * @return */ public boolean hashKey(String key, String hashKey) { return redisTemplate.opsForHash().hasKey(key, hashKey); } /** * Get the value string of the specified key * * @param key key * @param key2 key * @return */ public String getMapString(String key, String key2) { return redisTemplate.opsForHash().get("map1", "key1").toString(); } /** * Get the specified value Int * * @param key key * @param key2 key * @return */ public Integer getMapInt(String key, String key2) { return (Integer) redisTemplate.opsForHash().get("map1", "key1"); } /** * pop element and delete * * @param key key * @return */ public String popValue(String key) { return redisTemplate.opsForSet().pop(key).toString(); } /** * Delete the HashKey of the specified hash * * @param key * @param hashKeys * @return Number of successful deletions */ public Long delete(String key, String... hashKeys) { return redisTemplate.opsForHash().delete(key, hashKeys); } /** * Add or subtract operations to the hashkey of the specified hash * * @param key * @param hashKey * @param number * @return */ public Long increment(String key, String hashKey, long number) { return redisTemplate.opsForHash().increment(key, hashKey, number); } /** * Add or subtract operations to the hashkey of the specified hash * * @param key * @param hashKey * @param number * @return */ public Double increment(String key, String hashKey, Double number) { return redisTemplate.opsForHash().increment(key, hashKey, number); } /** * Get all hashkey fields under key * * @param key * @return */ public Set<Object> hashKeys(String key) { return redisTemplate.opsForHash().keys(key); } /** * Get the number of key-value pairs under the specified hash * * @param key * @return */ public Long hashSize(String key) { return redisTemplate.opsForHash().size(key); } //- - - - - - - - - - - - - - - - - - - - - list type - - - - - - - - - - - - - - - - - - - - - /** * Add the element value to the left of the variable * * @param key * @param value * @return */ public void leftPush(String key, Object value) { redisTemplate.opsForList().leftPush(key, value); } /** * Gets the value at the specified position in the collection. * * @param key * @param index * @return */ public Object index(String key, long index) { return redisTemplate.opsForList().index("list", 1); } /** * Get the value of the specified interval. * * @param key * @param start * @param end * @return */ public List<Object> range(String key, long start, long end) { return redisTemplate.opsForList().range(key, start, end); } /** * Put the last parameter value before the first occurrence of the intermediate parameter in the specified set, * If the intermediate parameter value exists. * * @param key * @param pivot * @param value * @return */ public void leftPush(String key, String pivot, String value) { redisTemplate.opsForList().leftPush(key, pivot, value); } /** * Bulk add parameter elements to the left. * * @param key * @param values * @return */ public void leftPushAll(String key, String... values) { // redisTemplate.opsForList().leftPushAll(key,"w","x","y"); redisTemplate.opsForList().leftPushAll(key, values); } /** * Adds an element to the far right of the collection. * * @param key * @param value * @return */ public void leftPushAll(String key, String value) { redisTemplate.opsForList().rightPush(key, value); } /** * Bulk add parameter elements to the left. * * @param key * @param values * @return */ public void rightPushAll(String key, String... values) { //redisTemplate.opsForList().leftPushAll(key,"w","x","y"); redisTemplate.opsForList().rightPushAll(key, values); } /** * Add an element to an existing collection. * * @param key * @param value * @return */ public void rightPushIfPresent(String key, Object value) { redisTemplate.opsForList().rightPushIfPresent(key, value); } /** * Add an element to an existing collection. * * @param key * @return */ public long listLength(String key) { return redisTemplate.opsForList().size(key); } /** * Removes the first element from the left in the collection. * * @param key * @return */ public void leftPop(String key) { redisTemplate.opsForList().leftPop(key); } /** * Remove the left element in the collection during the waiting time, if there is still no element after the waiting time, exit. * * @param key * @return */ public void leftPop(String key, long timeout, TimeUnit unit) { redisTemplate.opsForList().leftPop(key, timeout, unit); } /** * Removes the right element from the collection. * * @param key * @return */ public void rightPop(String key) { redisTemplate.opsForList().rightPop(key); } /** * Remove the element on the right from the set during the waiting time, and exit if there is still no element after the waiting time. * * @param key * @return */ public void rightPop(String key, long timeout, TimeUnit unit) { redisTemplate.opsForList().rightPop(key, timeout, unit); } }
You can also learn more about the usage of RedisTemplate by reading this tool class. When using it, you only need to inject this tool class.
Fourth, talk about serialization
The serialization of redis is also something we need to pay attention to when using RedisTemplate. In the above case, in fact, we did not specifically set the serialization method of redis, so it actually uses the default serialization method. The generic type of the RedisTemplate class is <String,Object>, that is, it supports writing Object objects, so the way this object is serialized and stored in memory is its serialization method.
So what is serialization of redis? That is, how do we store the object in redis, it can be binary data, it can be xml or json. For example, we often use POJO object storage In Redis, it is usually serialized into a string using JSON and stored in Redis.
Redis itself provides a serialization method:
- GenericToStringSerializer: can generalize any object to string and serialize
- Jackson2JsonRedisSerializer: is actually the same as JacksonJsonRedisSerializer
- JacksonJsonRedisSerializer: Serialize object to json string
- JdkSerializationRedisSerializer: Serialize java objects
- StringRedisSerializer: Simple String Serialization
If we store a String type, the serialization method StringRedisSerializer is used by default. If we store objects, JdkSerializationRedisSerializer is used by default, which is the serialization method of Jdk (implemented through ObjectOutputStream and ObjectInputStream, the disadvantage is that we cannot visually see the stored object content).
We can set the corresponding serialization method according to the different data types operated by redis.

By observing the source code of RedisTemplate, we can see that JdkSerializationRedisSerializer is used by default. The biggest problem of this serialization is that after storing the object, it is difficult for us to visually see the stored content, which is very inconvenient for us to troubleshoot the problem:

In general, the most frequently used object serialization method is: Jackson2JsonRedisSerializer
The main way to set the serialization method is to create a RedisTemplate object by ourselves in the configuration class, and specify the corresponding serialization method during the creation process.
copyimport com.fasterxml.jackson.annotation.JsonAutoDetect; import com.fasterxml.jackson.annotation.PropertyAccessor; import com.fasterxml.jackson.databind.ObjectMapper; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer; import org.springframework.data.redis.serializer.StringRedisSerializer; @Configuration public class RedisConfig { @Bean(name = "redisTemplate") public RedisTemplate<String, Object> getRedisTemplate(RedisConnectionFactory factory) { RedisTemplate<String, Object> redisTemplate = new RedisTemplate<String, Object>(); redisTemplate.setConnectionFactory(factory); StringRedisSerializer stringRedisSerializer = new StringRedisSerializer(); redisTemplate.setKeySerializer(stringRedisSerializer); // serialization type of key Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class); ObjectMapper objectMapper = new ObjectMapper(); objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); // The method expires, change to the following code // objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL); objectMapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance , ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY); jackson2JsonRedisSerializer.setObjectMapper(objectMapper); jackson2JsonRedisSerializer.setObjectMapper(objectMapper); redisTemplate.setValueSerializer(jackson2JsonRedisSerializer); // serialization type of value redisTemplate.setHashKeySerializer(stringRedisSerializer); redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer); redisTemplate.afterPropertiesSet(); return redisTemplate; } }
When used in this way, it will be stored according to the json serialization method we set, and we can also easily view the attribute value when viewing the content in redis.
5. Distributed lock
References:
7 Solutions for Redis to Implement Distributed Locks – why414 – Blog Park
In many scenarios, technologies such as distributed transactions and distributed locks need to be used to ensure the eventual consistency of data. Sometimes, we need to ensure that a method can only be executed by one thread at a time. In a single-machine (single-process) environment, JAVA provides many concurrency-related API s, but in a multi-machine (multi-process) environment, it is powerless.
For distributed locks, it is best to meet the following points
It can be guaranteed that in a distributed application cluster, the same method can only be executed by one thread on one machine at the same time The lock must be a reentrant lock (avoid deadlock) This lock is preferably a blocking lock There are highly available lock acquisition and release lock functions Better performance in acquiring and releasing locks
There are generally three ways to implement distributed locks: 1. Database optimistic locks; 2. Redis-based distributed locks; 3. ZooKeeper-based distributed locks. This article mainly introduces the second method.
A perfect distributed lock must meet the following four conditions: 1. Mutual exclusion. At any time, only one client can hold the lock. 2. No deadlock occurs. Even if a client crashes while holding the lock and does not actively unlock it, other clients can be guaranteed to lock in the future. 3. It is fault-tolerant. Clients can lock and unlock as long as most of the Redis nodes are up and running. 4. The person who unlocks the bell must also be tied to the bell. Locking and unlocking must be done by the same client, and the client cannot unlock the locks added by others.
Redis distributed lock principle:
The implementation of the lock is mainly based on the SETNX command of redis
SETNX key value sets the value of key to value if and only if key does not exist. If the given key already exists, SETNX does nothing. SETNX is shorthand for "SET if Not eXists" (SET if not present).
**Return value: **Set successfully, return 1 . Setting failed, returns 0 .
The process and matters of using SETNX to complete the synchronization lock are as follows:
- Use the SETNX command to acquire the lock, if it returns 0 (the key already exists, the lock already exists), the acquisition fails, otherwise the acquisition succeeds
- In order to prevent the abnormality of the program after acquiring the lock, causing other threads/processes to call the SETNX command to always return 0 and enter a deadlock state, it is necessary to set a "reasonable" expiration time for the key
- Release the lock and use the DEL command to delete the lock data
The introduction of locks in Redis in this article is relatively comprehensive.
8 big pits for Redis to realize distributed locks! Remember! -Technology Circle
There are many ways to implement Redis locks, and there are more or less problems at that time. The relatively perfect solution is to use lua scripts. The most perfect solution is to use RedissionRedLock in the Redission framework. The specific implementation is not given, you can follow this idea to find relevant information. When I have the time and energy, I'll come back and add more.
Copyright statement: The content of this article is contributed by Internet users, and the opinions of this article only represent the author himself. This site only provides information storage space services, does not own ownership, and does not assume relevant legal responsibilities. If you find any content suspected of infringing/violating laws and regulations on this site, please send an email to report. Once verified, this site will be deleted immediately.
Publisher: Full-stack programmer, please indicate the source: https://javaforall.cn/190694.html Original link: https://javaforall.cn