Redis Notes - Transactions and Integration springboot

Posted by jej1216 on Thu, 03 Feb 2022 20:14:07 +0100

affair

Redis's single command guarantees atomicity, but the redis transaction does not guarantee atomicity

Redis Transaction nature: A collection of commands.


----------------- queue set set set implement -------------------

Each command in a transaction is serialized and executed sequentially. No interference from other commands is allowed.
Disposable
 Sequential
 Exclusiveness
Redis Transactions do not have the concept of isolation level
Redis A single command guarantees atomicity, but a transaction does not guarantee atomicity!

Redis Transaction Operation Procedure

  • Open Transaction (multi)
  • Command Entry
  • Execute Transaction (exec)

Therefore, commands in a transaction are not executed when joined, and execution (Exec) is not started until a commit is made.

Normal execution
127.0.0.1:6379> multi # Open Transaction
OK
127.0.0.1:6379> set k1 v1 # Command Entry
QUEUED
127.0.0.1:6379> set k2 v2 # ..
QUEUED
127.0.0.1:6379> get k1
QUEUED
127.0.0.1:6379> set k3 v3
QUEUED
127.0.0.1:6379> keys *
QUEUED
127.0.0.1:6379> exec # Transaction Execution
1) OK
2) OK
3) "v1"
4) OK
5) 1) "k3"
   2) "k2"
   3) "k1"
discurd transaction
127.0.0.1:6379> multi
OK
127.0.0.1:6379> set k1 v1
QUEUED
127.0.0.1:6379> set k2 v2
QUEUED
127.0.0.1:6379> DISCARD # Abandon Transaction
OK
127.0.0.1:6379> EXEC 
(error) ERR EXEC without MULTI # Transaction not currently open
127.0.0.1:6379> get k1 # Command not executed in abandoned transaction
(nil)
Transaction error

Code syntax error (compile-time exception) All commands are not executed

127.0.0.1:6379> multi
OK
127.0.0.1:6379> set k1 v1
QUEUED
127.0.0.1:6379> set k2 v2
QUEUED
127.0.0.1:6379> error k1 # This is a grammar error command
(error) ERR unknown command `error`, with args beginning with: `k1`, # Errors will be reported but will not affect subsequent command enlistment 
127.0.0.1:6379> get k2
QUEUED
127.0.0.1:6379> EXEC
(error) EXECABORT Transaction discarded because of previous errors. # Execution error
127.0.0.1:6379> get k1 
(nil) # Other commands were not executed

Code logic errors (runtime exceptions) ** Other commands can execute ** > > > > > so transaction atomicity is not guaranteed

127.0.0.1:6379> multi
OK
127.0.0.1:6379> set k1 v1
QUEUED
127.0.0.1:6379> set k2 v2
QUEUED
127.0.0.1:6379> INCR k1 # This command has a logical error (incrementing strings)
QUEUED
127.0.0.1:6379> get k2
QUEUED
127.0.0.1:6379> exec
1) OK
2) OK
3) (error) ERR value is not an integer or out of range # Runtime error
4) "v2"   # Other commands execute normally
 Although one of the commands reported an error, the following commands still executed successfully.
So Redis A single instruction guarantees atomicity, but Redis Transactions do not guarantee atomicity.
Monitor

Pessimistic lock:

  • Pessimistic, think there's always a problem, lock everything

Optimistic lock:

  • Very optimistic, think there will be no problem at any time, so there will be no lock! When updating data, check to see if anyone has modified the data during this period
  • Get version
  • Compare version s when updating

Monitoring specified data with watch key is equivalent to optimistic lock locking.

Normal execution

127.0.0.1:6379> set money 100 # Set balance: 100
OK
127.0.0.1:6379> set use 0 # Expense usage: 0
OK
127.0.0.1:6379> watch money # Monitor money (lock up)
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> DECRBY money 20
QUEUED
127.0.0.1:6379> INCRBY use 20
QUEUED
127.0.0.1:6379> exec # The monitor value was not modified in the middle, and the transaction executed normally
1) (integer) 80
2) (integer) 20

Testing multithreaded modification values, using watch es can act as optimistic lock operations for redis (equivalent to getversion)

Let's start another client to simulate queuing threads.

Thread 1:

127.0.0.1:6379> watch money # money Lock
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> DECRBY money 20
QUEUED
127.0.0.1:6379> INCRBY use 20
QUEUED
127.0.0.1:6379>     # The transaction was not executed at this time

Simulate thread queuing, thread 2:

127.0.0.1:6379> INCRBY money 500 # Modified money monitored in thread one
(integer) 600

Back to thread 1, execute the transaction:

127.0.0.1:6379> EXEC # Another thread modified our value before execution, which will cause the transaction to fail
(nil) # No result, transaction execution failed
127.0.0.1:6379> get money # Thread 2 modification takes effect

"600"

127.0.0.1:6379> get use # Thread 1 transaction failed, value not modified

"0"

Unlock to get the latest value and then lock the transaction.

unwatch to unlock.

Note: Locks are automatically released after each exec submission, whether successful or not

Jedis

Using Java to operate Redis, Jedis is the officially recommended Java connection redis client for Redis.

1. Import Dependency

<!--Import jredis Package for-->
<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
    <version>3.2.0</version>
</dependency>
<!--fastjson-->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.70</version>
</dependency>

2. Coding Test

Connect to database
Operational commands
Disconnect

Code Samples

public class TestPing {
    public static void main(String[] args) {
        Jedis jedis = new Jedis("192.168.xx.xxx", 6379);
        String response = jedis.ping();
        System.out.println(response); // PONG
    }
}

Output PONG

Common API s

string,list,set,hash,zset

All the api commands, the ones we learned above, have not changed!

public class TestTX {
    public static void main(String[] args) {
        Jedis jedis = new Jedis("127.0.0.1", 6379);
        jedis.flushDB();
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("hello","world");
        jsonObject.put("name","xiaoli");
        // Open Transaction
        Transaction multi = jedis.multi();
        String result = jsonObject.toJSONString();
        // jedis.watch(result)
        try {
            multi.set("user1",result);
            multi.set("user2",result);
            int i = 1/0 ; // Code throws an exception transaction, execution failed!
            multi.exec(); // Perform business!
        } catch (Exception e) {
            multi.discard(); // Abandon Transaction
            e.printStackTrace();
        } finally {
            System.out.println(jedis.get("user1"));
            System.out.println(jedis.get("user2"));
            jedis.close(); // Close Connection
        }
    }
}

SpringBoot Integration

SpringBoot operational data: spring-data jpa jdbc mongodb redis!

SpringData is also a project with the same name as SpringBoot!

Description: In SpringBoot2. After x, the original jedis was replaced with lettuce?

Jedis: Direct connection, multi-threaded operation, is not safe, if you want to avoid unsafe, use jedis pool connection pool! More like BIO mode

Letuce: With netty, instances can be shared among multiple threads without thread insecurity! Reduced thread data, more like NIO mode

Source analysis:

@Bean
@ConditionalOnMissingBean(name = "redisTemplate") 
// We can define a redisTemplate to replace this default!
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
// The default ReedisTemplate does not have too many settings, redis objects all need to be serialized!
// Both generics are Object, Object's type, so we need to cast <String, Object>
    RedisTemplate<Object, Object> template = new RedisTemplate<>();
    template.setConnectionFactory(redisConnectionFactory);
    return template;
}
@Bean
@ConditionalOnMissingBean // Since String is the most commonly used type in redis, a bean has been proposed separately!
public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
    StringRedisTemplate template = new StringRedisTemplate();
    template.setConnectionFactory(redisConnectionFactory);
    return template;
}

Integration Testing

1. Import Dependency

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

Springboot 2. After x, the original Jedis was replaced by lettuce.

Jedis: Direct connections, multi-threaded operations, are not safe. If you want to avoid insecurity, use jedis pool to connect to the pool! More like BIO mode

Letuce: With netty, instances can be shared across multiple threads without thread insecurity! Reduced thread data, more like NIO mode

When learning the principles of SpringBoot auto-configuration, integrating and configuring a component must have an auto-configuration class, xxxAutoConfiguration, and in spring. The fully qualified name of this class must also be found in factories. Redis is no exception.

So there must also be a RedisProperties class

There are two classes in the @ConditionalOnClass annotation that do not exist by default, so Jedis is not valid

Then look at Lettuce:

Perfect effect.

Now let's go back to RedisAutoConfiguratio

There are only two simple Bean s

  • RedisTemplate
  • StringRedisTemplate

When you see xxTemplate, you can compare RestTemplat with SqlSessionTemplate to indirectly manipulate components using these Templates. Neither of them is exceptional. Used to manipulate String data types in Redis and Redis, respectively.

There is also a conditional note on RedisTemplate that says we can customize it

Having said that, we need to know how to write a configuration file and connect to Redis, so we need to read RedisProperties

These are some basic configuration properties.

There are also connection pool-related configurations. Be aware that the connection pool for Lettuce is always used.

2. Write a configuration file

# Configure redis
spring.redis.host=XXXX
spring.redis.port=6379

3. Use RedisTemplate

@SpringBootTest
class Redis02SpringbootApplicationTests {

@Autowired
private RedisTemplate redisTemplate;

@Test
void contextLoads() {

    // redisTemplate operates on different data types, the api is the same as our instructions
    // opsForValue operation string similar to String
    // opsForList acts like List
    // opsForSet
    // opsForHash
    // opsForZSet
    // opsForGeo
    // opsForHyperLog

    // In addition to basic operations, common methods we use can operate directly through redisTemplate, such as transactions and basic CRUD s

    // Get Connection Object
    //RedisConnection connection = redisTemplate.getConnectionFactory().getConnection();
    //connection.flushDb();
    //connection.flushAll();

    redisTemplate.opsForValue().set("mykey","zhangsan");
    System.out.println(redisTemplate.opsForValue().get("mykey");
}
}

4. Test results

When we returned to Redis to look at the data, we were surprised to find that it was all garbled, but the program could output normally. At this time, it is related to the serialization of storage objects. The objects transmitted in the network also need serialization. Otherwise, they are all garbled.

The serialization configuration inside RedisTemplate is like this

The default serializer is to use JDK serializer

We can then customize RedisTemplate to modify it.

RedisSerializer provides a variety of serialization schemes:

Let's write our own RedisTemplete

package com.li.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;
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 {
    // This is a fixed template that I have written for you. Everyone in the enterprise can use it directly!
    // I defined a RedisTemplate myself
    @Bean
    @SuppressWarnings("all")
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
        // We have developed for ourselves the convenience of using <String, Object>
        RedisTemplate<String, Object> template = new RedisTemplate<String,
                Object>();
        template.setConnectionFactory(factory);

        // Json Serialization Configuration
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new
                Jackson2JsonRedisSerializer(Object.class);
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(om);
        // Serialization of String s
        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();

        // key uses String serialization
        template.setKeySerializer(stringRedisSerializer);
        // hash's key also uses String's serialization
        template.setHashKeySerializer(stringRedisSerializer);
        // value serialization using jackson
        template.setValueSerializer(jackson2JsonRedisSerializer);
        // hash's value serialization uses jackson
        template.setHashValueSerializer(jackson2JsonRedisSerializer);
        template.afterPropertiesSet();

        return template;
    }
    
}

All redis operations, in fact, are very simple for java developers, but it is more important to understand the idea of redis and the use and use of each data structure scenario!

Topics: Database Redis Spring Boot