When you open this article, you will probably know a lot about Redis. I will not introduce Redis in detail here, but only when necessary.

Here is a brief introduction, you can skip the configuration: **

A brief introduction to Redis

** Data types supported by Redis include:

3, Sorted Set: Sorted Set: Sorted SetCopy the code

The key advantages of Redis include its speed, support for rich data types, atomicity of operations, and versatility:

Redis can perform approximately 100,000 SETS and approximately 100,000 Get operations per second;

spring-boot-starter-data-redis

Spring Boot provides an integrated component package for Redis: spring-boot-starter-data-redis, which relies on spring-data-redis and lettuce. Spring Boot 1.0 uses the Jedis client by default, and 2.0 replaces the Lettuce, but if you switch from Spring Boot 1.5.X, you will feel almost the same. This is because spring-boot-starter-data-redis isolates the differences for us.

Lettuce: is a scalable thread-safe Redis client, multiple threads can share the same RedisConnection, it uses the excellent Netty NIO framework to efficiently manage multiple connections. Spring Data: A major project within the Spring framework that aims to simplify building Data access for Spring framework-based applications, including non-relational databases, map-Reduce frameworks, cloud Data services, and relational database access support. Spring Data Redis: it is a major module in the Spring Data project, which realizes the high encapsulation of the Redis client API and makes the operation of Redis more convenient.

The relationship can be expressed as follows:

Lettuce → Spring Data Redis → Spring Data → spring-boot-starter-data-redis

Therefore, spring-boot-starter-data-redis has almost all the functions that Spring Data Redis and Lettuce have.

Start Configuration

Importing dependency packages

<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-pool2</artifactId> The < version > 2.6.1 < / version > < / dependency >Copy the code

Commons-pool 2 is introduced because Commons -pool 2 is required to create a Redis connection pool.

Application configuration

Database =0 # Redis server address Spring.redis. Host =localhost # Redis server connection port Port =6379 # Redis server connection password (default empty) #spring.redis. Password = # Maximum number of connections in the connection pool (use negative values to indicate no limit) Default 8 Spring. Redis. Lettuce. Pool. Max - active = 8 # connection pool biggest jam waiting time (use a negative value indicates no limit) - 1 spring. By default redis. Lettuce. The pool. The Max - wait = 1 # Connection pool in the largest free connection 8 spring. By default redis. Lettuce. The pool. The Max - idle = 8 # connection pool in minimum free connection 0 spring. By default redis. Lettuce. Pool. Min - idle = 0Copy the code

You can also see from the configuration that Spring Boot supports Lettuce connection pooling by default. Cache configuration (optional)

Here you can set some global configurations for Redis, such as the production policy KeyGenerator for the primary key. Otherwise, the parameter name is used as the primary key by default.

@Configuration @EnableCaching public class RedisConfig extends CachingConfigurerSupport{ @Bean public KeyGenerator keyGenerator() { return new KeyGenerator() { @Override public Object generate(Object target, Method method, Object... params) { StringBuilder sb = new StringBuilder(); sb.append(target.getClass().getName()); sb.append(method.getName()); for (Object obj : params) { sb.append(obj.toString()); } return sb.toString(); }}; }}Copy the code

Note that we use the annotation @enablecaching to EnableCaching.

Test using

In the unit test, inject the RedisTemplate. String is the most commonly used data type. Ordinary key/value stores can be classified into this type. Value is not only a String but also a number.

@RunWith(SpringRunner.class) @SpringBootTest public class TestRedisTemplate { @Autowired private RedisTemplate redisTemplate; @Test public void testString() { redisTemplate.opsForValue().set("neo", "ityouknow"); Assert.assertEquals("ityouknow", redisTemplate.opsForValue().get("neo")); }}Copy the code

In this unit test, we used the redisTemplate to store a string “ityouKnow”, which was retrieved after storage for verification. If we set the same key multiple times, the corresponding value of the key would be overwritten.

Using spring-boot-starter-data-redis, you can quickly integrate Redis in three steps. Here is how Redis operates on various data types. Various types of practices

We know that Redis supports multiple data types: entities, hashes, lists, collections, and ordered collections. How to use them in Spring Boot? entity

Take a look at Redis’s support for POJOs. Create a User object, put it in the cache, and retrieve it.

@Test
public void testObj(){
    User user=new User("[email protected]", "smile", "youknow", "know","2020");
    ValueOperations<String, User> operations=redisTemplate.opsForValue();
    operations.set("com.neo", user);
    User u=operations.get("com.neo");
    System.out.println("user: "+u.toString());
}
Copy the code

Output result:

user: com.neo.domain.User@16fb356[id=,userName=know,passWord=youknow,[email protected],nickName=smile,regTime=2020]

Verification found perfect support for storing and reading objects. Timeout failure

Redis can set a timeout time when storing each data, after which the data will be automatically deleted. This feature is very suitable for caching phase data.

Create a User object, save it to Redis and set it to expire 100 ms later. Set a thread to pause 1000 ms later to determine whether the data exists and print the result.

@Test public void testExpire() throws InterruptedException { User user=new User("[email protected]", "expire", "youknow", "expire","2020"); ValueOperations<String, User> operations=redisTemplate.opsForValue(); operations.set("expire", user,100,TimeUnit.MILLISECONDS); Thread.sleep(1000); boolean exists=redisTemplate.hasKey("expire"); if(exists){ System.out.println("exists is true"); }else{ System.out.println("exists is false"); }}Copy the code

Output result:

exists is false

The result shows that the User object no longer exists in Reids, the data has expired, and we use the hasKey(“expire”) method in the test method to determine whether the key exists. Delete the data

In some cases, we need to delete an expired cache. Let’s test this scenario. First set a string “ityouknow”, then delete the value of this key, and then determine.

@Test public void testDelete() { ValueOperations<String, User> operations=redisTemplate.opsForValue(); redisTemplate.opsForValue().set("deletekey", "ityouknow"); redisTemplate.delete("deletekey"); boolean exists=redisTemplate.hasKey("deletekey"); if(exists){ System.out.println("exists is true"); }else{ System.out.println("exists is false"); }}Copy the code

Output result:

exists is false

The result indicates that the string “ityouknow” has been successfully deleted. Hash

When we store a key, it’s natural to use get/set to store it, which is actually not a good idea. Redis has a minimum memory for storing a key, and no matter how small the key is, it can’t be less than that, so using Hash properly can save a lot of memory.

The Hash Set is Set to value for the Field in the Key. If the Key does not exist, a new hash table is created and the Hset operation is performed. If the Field already exists in the hash table, the old value will be overwritten.

@Test
public void testHash() {
    HashOperations<String, Object, Object> hash = redisTemplate.opsForHash();
    hash.put("hash","you","you");
    String value=(String) hash.get("hash","you");
    System.out.println("hash value :"+value);
}
Copy the code

Output result:

hash value :you

According to the above test case, three parameters are passed in the Hash set. The first parameter is key, the second parameter is Field, and the third parameter is the stored value. In general, Key represents a group of data, Field is the key-related attribute, and Value is the corresponding Value of the attribute. List

Redis List is one of the most important data structures in Redis. List can be used to easily implement a queue. The typical application scenario of List is message queue. The Push operation of List can be used to store tasks in List, and then the worker thread can take out tasks for execution with POP operation.

@Test
public void testList() {
    ListOperations<String, String> list = redisTemplate.opsForList();
    list.leftPush("list","it");
    list.leftPush("list","you");
    list.leftPush("list","know");
    String value=(String)list.leftPop("list");
    System.out.println("list value :"+value.toString());
}
Copy the code

Output result:

list value :know

In the above example, we insert a queue with a key of “list” from the left and fetch the most recent data from the left. There are a lot of apis that you can do with a List, like insert queue from the right and read queue from the right, or read part of the queue through the method range. Following the example above we use range to read.

A List < String > values = List. Range (" List ", 0, 2); for (String v:values){ System.out.println("list range :"+v); }Copy the code

Output result:

list range :know list range :you list range :it

The two parameters after range are the positions to insert data. Input different parameters to retrieve the corresponding data in the queue.

The implementation of Redis List is a two-way linked List, which can support reverse lookup and traversal, which is more convenient to operate, but brings some extra memory overhead. Many implementations of Redis, including sending buffer queues, are also using this data structure.Copy the code

Set

Redis Set provides a List function similar to that of List. The special feature is that Set can be automatically sorted. When you need to store a List of data and do not want duplicate data, Set is a good choice. And sets provide an important interface for determining whether a member is in a Set, which lists do not.

@Test public void testSet() { String key="set"; SetOperations<String, String> set = redisTemplate.opsForSet(); set.add(key,"it"); set.add(key,"you"); set.add(key,"you"); set.add(key,"know"); Set<String> values=set.members(key); for (String v:values){ System.out.println("set value :"+v); }}Copy the code

Output result:

set value :it set value :know set value :you

In the example above, we can see that the Set automatically reloads the queue by entering two identical values of “you” and reading all but one of them.

Redis provides the set of intersection, union, difference and other operations, can be very convenient to use.

Test the difference

SetOperations<String, String> set = redisTemplate.opsForSet();
String key1="setMore1";
String key2="setMore2";
set.add(key1,"it");
set.add(key1,"you");
set.add(key1,"you");
set.add(key1,"know");
set.add(key2,"xx");
set.add(key2,"know");
Set<String> diffs=set.difference(key1,key2);
for (String v:diffs){
    System.out.println("diffs set value :"+v);
}
Copy the code

Output result:

diffs set value :it diffs set value :you

As you can see from the above example, the difference() function will compare the data in key 1 that is different from key 2. This feature is suitable for the reconciliation of accounts in financial scenarios.

Test the unions

SetOperations<String, String> set = redisTemplate.opsForSet();
String key3="setMore3";
String key4="setMore4";
set.add(key3,"it");
set.add(key3,"you");
set.add(key3,"xx");
set.add(key4,"aa");
set.add(key4,"bb");
set.add(key4,"know");
Set<String> unions=set.union(key3,key4);
for (String v:unions){
    System.out.println("unions value :"+v);
}
Copy the code

Output result:

unions value :know unions value :you unions value :xx unions value :it unions value :bb unions value :aa

The unions take a Set of two sets, and Set also has many similar operations.

The internal implementation of a Set is a HashMap with an always-null Value, which in effect computs the Hash to quickly sort out the weight, which is why a Set provides a way to determine whether a member is in the Set.Copy the code

ZSet

The usage scenario of Redis Sorted Set is similar to that of Set, except that Set is not automatically ordered. However, Sorted Set can sort its members by providing an additional parameter of priority (Score), and it is ordered by insertion, that is, automatic sorting.

When using Zset, we need to input an extra parameter Score, which will automatically sort the set according to the value of Score. We can make use of this feature to make a queue with weight, for example, Score of ordinary messages is 1, Score of important messages is 2. The worker thread can then choose to obtain work tasks in reverse order of Score.

@Test public void testZset(){ String key="zset"; redisTemplate.delete(key); ZSetOperations<String, String> zset = redisTemplate.opsForZSet(); zset.add(key,"it",1); zset.add(key,"you",6); zset.add(key,"know",4); zset.add(key,"neo",3); Set the < String > zsets = zset. Range (key, 0, 3); for (String v:zsets){ System.out.println("zset value :"+v); } the Set < String > zsetB = zset. RangeByScore (key, 0, 3); for (String v:zsetB){ System.out.println("zsetB value :"+v); }}Copy the code

Output result:

zset value :it zset value :neo zset value :know zset value :you zsetB value :it zsetB value :neo

Through the above example, we found that the data inserted into Zset will be automatically sorted according to Score. According to this feature, we can do various common scenarios such as priority queue. In addition, Redis also provides a method such as rangeByScore, which can only get the sorted data within the Score range.

In Redis Sorted Set, HashMap and SkipList are used internally to ensure data storage and order. HashMap stores members' mapping to Score, while SkipList stores all members. Sorting is based on the Score stored in HashMap. The structure of skip list can obtain relatively high search efficiency and is relatively simple to implement.Copy the code

encapsulation

In our actual use process, we will not inject redisTemplate into every used class for direct use. Generally, we will simply wrap the business and finally provide it for external use.

Let’s take two examples.

Start by defining a RedisService service to inject the RedisTemplate into the class.

@Service
public class RedisService {
    @Autowired
    private RedisTemplate redisTemplate;
}
Copy the code

Encapsulate simple insert operation:

public boolean set(final String key, Object value) {
    boolean result = false;
    try {
        ValueOperations<Serializable, Object> operations = redisTemplate.opsForValue();
        operations.set(key, value);
        result = true;
    } catch (Exception e) {
        logger.error("set error: key {}, value {}",key,value,e);
    }
    return result;
}
Copy the code

Any exceptions that occur are processed and fed back to the caller.

Let’s say we want to delete the value of a certain type of Key.

public void removePattern(final String pattern) {
    Set<Serializable> keys = redisTemplate.keys(pattern);
    if (keys.size() > 0)
        redisTemplate.delete(keys);
}
Copy the code

Redis Pattern is used to match a batch of qualified caches, and then delete them in batches.

There are other encapsulation methods, such as checking whether a Key exists before deleting it. These simple business judgments should be encapsulated in RedisService, providing the simplest API calls.

@Autowired
private RedisService redisService;

@Test
public void testString() throws Exception {
    redisService.set("neo", "ityouknow");
    Assert.assertEquals("ityouknow", redisService.get("neo"));
}
Copy the code

It is more elegant and simple to inject RedisService into Redis while other services are using it and call the corresponding method to manipulate Redis.

Finally, the enclosed source code for RedisService:

import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.*; import org.springframework.stereotype.Service; import java.io.Serializable; import java.util.List; import java.util.Set; import java.util.concurrent.TimeUnit; /** * This class encapsulates common Redis operations. Public class RedisService {private Logger} Java.io.Serializable interface */ @service Public class RedisService {private Logger logger = LoggerFactory.getLogger(RedisService.class); @Autowired private RedisTemplate redisTemplate; /** * set value * @param key * @param value * @return */ public boolean set(final String key, Object value) { boolean result = false; try { ValueOperations<Serializable, Object> operations = redisTemplate.opsForValue(); operations.set(key, value); result = true; } catch (Exception e) { logger.error("set error: key {}, value {}",key,value,e); } return result; } /** * set value with expireTime * expireTime unit of second * @param key * @param value * @param expireTime * @return */ public boolean set(final String key, Object value, Long expireTime) { boolean result = false; try { ValueOperations<Serializable, Object> operations = redisTemplate.opsForValue(); operations.set(key, value); redisTemplate.expire(key, expireTime, TimeUnit.SECONDS); result = true; } catch (Exception e) { logger.error("set error: key {}, value {},expireTime {}",key,value,expireTime,e); } return result; } /** * @param key * @return */ public boolean exists(final String key) { return redisTemplate.hasKey(key); } /** * @param key * @return */ public Object get(final String key) { Object result = null; ValueOperations<Serializable, Object> operations = redisTemplate.opsForValue(); result = operations.get(key); return result; } /** * remove single key * @param key */ public void remove(final String key) { if (exists(key)) { redisTemplate.delete(key); } } /** * batch delete * @param keys */ public void remove(final String... keys) { for (String key : keys) { remove(key); } } /** * batch delete with pattern * @param pattern */ public void removePattern(final String pattern) { Set<Serializable> keys = redisTemplate.keys(pattern); if (keys.size() > 0) redisTemplate.delete(keys); } /** * hash set * @param key * @param hashKey * @param value */ public void hashSet(String key, Object hashKey, Object value){ HashOperations<String, Object, Object> hash = redisTemplate.opsForHash(); hash.put(key,hashKey,value); } /** * hash get * @param key * @param hashKey * @return */ public Object hashGet(String key, Object hashKey){ HashOperations<String, Object, Object> hash = redisTemplate.opsForHash(); return hash.get(key,hashKey); } /** * list push * @param k * @param v */ public void push(String k,Object v){ ListOperations<String, Object> list = redisTemplate.opsForList(); list.rightPush(k,v); } /** * list range * @param k * @param l * @param l1 * @return */ public List<Object> range(String k, long l, long l1){ ListOperations<String, Object> list = redisTemplate.opsForList(); return list.range(k,l,l1); } /** * set add * @param key * @param value */ public void setAdd(String key,Object value){ SetOperations<String, Object> set = redisTemplate.opsForSet(); set.add(key,value); } /** * set get * @param key * @return */ public Set<Object> setMembers(String key){ SetOperations<String, Object> set = redisTemplate.opsForSet(); return set.members(key); } /** * ordered set add * @param key * @param value * @param scoure */ public void zAdd(String key,Object value,double scoure){ ZSetOperations<String, Object> zset = redisTemplate.opsForZSet(); zset.add(key,value,scoure); } /** * rangeByScore * @param key * @param scoure * @param scoure1 * @return */ public Set<Object> rangeByScore(String key,double scoure,double scoure1){ ZSetOperations<String, Object> zset = redisTemplate.opsForZSet(); return zset.rangeByScore(key, scoure, scoure1); }Copy the code