Redis series (III) integration of redis transactions and spring boot


It is more or less used in NoSQL development, and it is also a necessary knowledge point for interview. I’ve asked every interview in recent days. However, I feel that the answer is not good, and there are still many knowledge points that need to be sorted out. Here, I’ll comb through several redis notes, and then add the above test questions.

Redis series:

  1. Redis series (I) introduction to redis
  2. Redis series (II) 8 data types of redis
  3. Redis series (III) integration of redis transactions and spring boot
  4. Redis series (IV) redis profile and persistence
  5. Redis series (V) publish subscribe mode, master-slave replication and sentinel mode
  6. Redis series (VI) redis’s cache penetration, cache breakdown and cache avalanche
  7. Redis series (VII) redis interview questions
  8. Redis Command Reference

1. Business

Essence of redis transaction: a collection of commands! All commands in a transaction will be serialized and executed in sequence during the execution of the transaction.

Execute a set of commands at one time, sequentially and exclusively.

Redis transactions do not have the concept of isolation level.

All commands are not executed directly in a transaction. They are executed only when the execution command is initiated (exec).

Redis ensures atomicity for a single command, but transactions do not guarantee atomicity.

Redis transaction command:

  • Open transaction: multi
  • Order to join the team
  • Execute transaction: Exec
  • Undo transaction: discard

1. Normal execution of transactions> multi 		#  Open transaction
OK> set k1 v1
QUEUED> set k2 v2
QUEUED> get k2
QUEUED> set k3 v3
QUEUED> exec 			#  Execute transaction
1) OK
2) OK
3) "v2"
4) OK>

2. Abandon transaction> multi
OK> set m1 n1
QUEUED> set m2 n2
QUEUED> DISCARD 		#  Abandon transaction
OK> get m1 		#  None of the commands in the transaction queue will be executed

Compiled exception: if the command has an error, all commands in the transaction will not be executed> multi
OK> set k1 v1
QUEUED> set k2 v2
QUEUED> setget k3 v3 		#  Wrong command
(error) ERR unknown command `setget`, with args beginning with: `k3`, `v3`,> set k4 v4
QUEUED> exec 		#  Error in executing transaction
(error) EXECABORT Transaction discarded because of previous errors.> get k4 		#  All commands will not be executed

Runtime exception: if an error is reported in the execution result of a command in the transaction, other commands can be executed normally, and the error command throws an exception> set k1 "v1"
OK> multi
OK> incr k1 		#  Will fail
QUEUED> set k2 v2
QUEUED> set k3 v3
QUEUED> get k3
QUEUED> exec
1) (error) ERR value is not an integer or out of range 		#  The first command failed to execute, and the rest were executed normally
2) OK
3) OK
4) "v3"> get k2

3. Monitor watch (frequently asked in interview)

Pessimistic lock: very pessimistic. I think there will be problems whenever and lock everything. Affect efficiency, the actual situation will generally use optimistic lock.

Optimistic lock: very optimistic. I think there will be no problem at any time, so I don’t lock it. When updating data, judge whether the monitored data has been modified during this period, that is, obtain version.

First, understand the role of watch in redis transactions. The watch command can monitor one or more keys. Once one of the keys is modified (or deleted), subsequent transactions will not be executed. The monitoring continues until the exec command (the commands in the transaction are executed after exec, so the key value of watch monitoring can be modified after the multi command). Suppose we monitor multiple keys before the transaction is executed through the watch command. If the value of any key changes after the watch command, the transaction executed by the exec command will be abandoned and a null multi bulk response will be returned to notify the caller of the transaction execution failure.

Therefore, it should be noted that after the watch monitoring key, operate these keys, otherwise the watch may not work.

Redis monitoring test

Normal test:> set money 100		
OK> set out 0
OK> watch money 		#  Monitor money objects
OK> multi 		#  When the transaction ends normally and money does not change during execution, the execution will be successful
OK> DECRBY money 20
QUEUED> exec
1) (integer) 80
2) (integer) 20>

Test multiple threads to modify the value, and use watch as redis’s optimistic lock operation.> set money 100
OK> set out 10
OK> watch money 	#  Monitor money
OK> multi
OK> DECRBY money 10
QUEUED> exec 		#  Before execution, modify the value of money in another thread B. the following is the execution failure.

B thread:

[[email protected] bin]# redis-cli -p 6379> set money 30

If the modification fails, just get the latest value.> UNWATCH 		#  Transaction execution failed, unlock first
OK> WATCH money 		#  Get the latest value and monitor again. It is equivalent to select version in MySQL
OK> multi
OK> DECRBY money 1
QUEUED> exec 		#  During execution, the monitored values will be compared. If there is a change, the execution will fail.
1) (integer) 29
2) (integer) 11>


Use java to operate redis. Jedis is a Java link development tool officially recommended by redis and a middleware for Java to operate redis.

1. Import dependency



2. Test: start the local Windows version of redis

package cn.itzhouq;

import redis.clients.jedis.Jedis;

public class TestPing {
    public static void main(String[] args) {
        Jedis jedis = new Jedis("localhost", 6379, 10);
        System.out.println(; // PONG


package cn.itzhouq;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.Transaction;

 *Test transaction
public class TestTX {
    public static void main(String[] args) {
        Jedis jedis = new Jedis("localhost", 6379);
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("hello", "world");
        jsonObject.put("name", "xxx");

        //Open transaction
        Transaction multi = jedis.multi();
        String result = jsonObject.toJSONString();

        try {
            multi.set("user1", result);
            multi.set("user2", result);
            int i = 1 / 0; //  Simulation anomaly
            multi.exec(); //  Execute transaction
        } catch (Exception e) {
            multi.discard(); //  Abandon transaction
        } finally {
            System.out.println(jedis.get("user1")); //  During normal execution, {"name": "XXX", "hello": "world"} // null
            jedis.close(); //  Close link

3. Spring boot integration

Spring data is also a project as famous as spring boot.

Note: after spring boot 2. X, the original jedis is replaced by lettuce.

Jedis: direct connection and multi-threaded operation are not safe. If you want to avoid insecurity, use jedis pool, which is more like bio mode.

Lettuce: with netty, instances can be shared among multiple threads. There is no thread insecurity. Thread links can be reduced, more like NiO mode.

#All configuration classes of spring boot have an automatic configuration class redistemplate
#Automatic configuration classes will bind a properties configuration file. RedisProperties

Read the source code:


@Import({LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class})
public class RedisAutoConfiguration {
    public RedisAutoConfiguration() {

        name = {"redisTemplate"}
    )// we can define a redistemplate to replace the default one.
    public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
        //The default redistemplate does not have too many settings, and redis objects need to be serialized.
        //The two generics are both object types. We need to forcibly replace them with 
        RedisTemplate template = new RedisTemplate();
        return template;

    @Conditionalonmissingbean // since string type is the most commonly used in redis, a bean is proposed separately
    public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
        StringRedisTemplate template = new StringRedisTemplate();
        return template;


1. Import dependency:


2. Configure connection

#Configure redis

3. Testing

package cn.itzhouq;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

public class Redis02SpringbootApplicationTests {

    private RedisTemplate redisTemplate;

    public void contextLoads() {
        //In addition to basic operations, our common methods can be operated directly through redistemplate, such as transaction and crud
        redisTemplate.opsForValue().set("name", "xiaoming");
        System.out.println(redisTemplate.opsForValue().get("name")); // xiaoming

Take a look at the source code: redistemplate.class

//Serialization configuration
private RedisSerializer keySerializer = null;
private RedisSerializer valueSerializer = null;
private RedisSerializer hashKeySerializer = null;
private RedisSerializer hashValueSerializer = null;
private RedisSerializer stringSerializer = RedisSerializer.string();
public void afterPropertiesSet() {
    boolean defaultUsed = false;
    if (this.defaultSerializer == null) {
        //JDK serialization is used by default, which will make the string escape
        this.defaultSerializer = new JdkSerializationRedisSerializer(this.classLoader != null ? this.classLoader : this.getClass().getClassLoader());

    // ...

We use JSON serialization, so we need to customize the configuration class

1. Serialization

Write an entity class user to test serialization.

package cn.itzhouq.pojo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.stereotype.Component;

public class User {
    private String name;
    private int age;

Test serialization:

public void test() throws JsonProcessingException {
    User user = new User("xiaoming", 3);
    redisTemplate.opsForValue().set("user", user);

Throw exception:

Caused by: java.lang.IllegalArgumentException: DefaultSerializer requires a Serializable payload but received an object of type [cn.itzhouq.pojo.User]
	at org.springframework.core.serializer.DefaultSerializer.serialize(
	... 35 more

DefaultSerializer requires a SerializableThe default serialization requires the entity class to implement the serialization interface. So modify user:

public class User implements Serializable {
    private String name;
    private int age;


User(name=xiaoming, age=3)

The results are normal, but the console is still escaped.> keys *
1) "\xac\xed\x00\x05t\x00\x04user">

Serialization using Jackson:

public void test() throws JsonProcessingException {
    //In general, JSON is used to transfer objects in development
    User user = new User("xiaoming", 3);
    String jsonUser = new ObjectMapper().writeValueAsString(user);
    redisTemplate.opsForValue().set("user", jsonUser);
    System.out.println(redisTemplate.opsForValue().get("user")); // {"name":"xiaoming","age":3}

Whether the user implements the serializable interface or not, the console results are displayed normally, but the view in the client is still escaped.

If you don’t want to use JDK serialization, you can write redistemplate yourself.

2. Customize redistemplate

package cn.itzhouq.config;

import 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;

 *Write your own redistemplate
public class RedisConfig {

    public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory) {
        //For the convenience of development, it is generally used 
        RedisTemplate template = new RedisTemplate();

        //Serialization configuration
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);

        //Serialization of string
        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
        //The key is serialized by string
        //The key of hash is also serialized by string
        //Value is serialized by Jackson
        //The value serialization method of hash is Jackson

        return template;

Injection and testing:

private RedisTemplate redisTemplate;

public void test() throws JsonProcessingException {
    //In general, JSON is used to transfer objects in development
    User user = new User("xiaoming", 3);
    String jsonUser = new ObjectMapper().writeValueAsString(user);
    redisTemplate.opsForValue().set("user", jsonUser);
    System.out.println(redisTemplate.opsForValue().get("user")); // {"name":"xiaoming","age":3}

View in client:> keys *
1) "user">

At this time, the object is not escaped.

The next note will introduce redis’s configuration file and persistence.

Recommended Today

A detailed explanation of the differences between Perl and strawberry Perl and ActivePerl

Perl is the abbreviation of practical extraction and report language “practical report extraction language”. Application of activestateperl and strawberry PERL on Windows platformcompiler。 Perl   The relationship between the latter two is that C language and Linux system have their own GCC. The biggest difference between activestate Perl and strawberry Perl is that strawberry Perl […]