Redis 4.x系列(十四):Spring Boot 在使用 Reids 管道和事务

  在 Java 中使用 Redis,基于 Spring Boot 框架。 关于 Spring Boot 集成 Redis,可阅读Spring Boot 2实践系列(十二):Spring Data Redis 集成详解和使用

  本篇主要演示 Spring Boot 提供的 RedisTemplate 对 Redis 功能特性的使用,主要是管道事务的操作。各种数据类型的操作比较简单。

Redis 管道技术

Redis 管道技术:简单理解就是将一组命令打包一次发送给服务器执行,而不是每条命令发送和返回。
详细可查看Redis 4.x系列(八):Redis Pipeline(管道技术)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
/**
* @name: SendByPipeLine
* @desc: 使用管道技术批量写入数据
**/
@RunWith(SpringRunner.class)
@SpringBootTest
public class SendByPipeLine {

@Autowired
private RedisTemplate<Object, Object> redisTemplate;

/**
* 使用管道技术
*/
@Test
public void saveByPipeLine(){

RedisCallback<Object> redisCallback = new RedisCallback<Object>() {

//doInRedis中的redis操作不会立刻执行
@Override
public Object doInRedis(RedisConnection connection) throws DataAccessException {
connection.openPipeline();

//执行业务,打包多条命令
for (int i = 0; i < 10000; i++) {
String key = "key:" + i;
String value = "value" + i;
connection.set(key.getBytes(), value.getBytes());
// connection.get(key.getBytes());
}

//所有redis操作会在connection.closePipeline()之后一并提交到redis并执行,这是pipeline方式的优势
// List<Object> resultList = connection.closePipeline();
return null;
}
};

//所有操作的执行结果为executePipelined()的返回值
List<Object> resultList = redisTemplate.executePipelined(redisCallback, redisTemplate.getStringSerializer());
System.out.println(JSON.toJSONString(resultList));
}
}

Redis 事务和乐观锁

在 Redis 开启事务主要是对要执行的命令入队检查,如果检测到错误就清空队列并退出事务; 若是在执行exec()时出行执行错误, Redis 不会停止执行命令, 队列中的所有其它正确的命令仍会被执行成功。详细可查看Redis 4.x系列(九):Redis Transaction(事务)

在 Spring Boot 中使用 RedisTemplate 来执行事务,在重写注册 RedisTemplate Bean 时开启事务支持:template.setEnableTransactionSupport(true);或获取连接来执行事务。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
import com.alibaba.fastjson.JSON;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.SessionCallback;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class DelayQueueApplicationTests {

private static final Logger logger = LogManager.getLogger(DelayQueueApplicationTests.class);

@Autowired
private RedisTemplate<String, Object> redisTemplate;
@Autowired
private RedisConnectionFactory redisConnectionFactory;

@Test
public void redisTransactionTest() {

/**
* 开启事务,redisTemplate.multi() 与 redisTemplate.exec()不是在一个连接里,会报如下错识
* Error in execution; nested exception is io.lettuce.core.RedisCommandExecutionException: ERR EXEC without MULTI
* 不能使用此方式
*/
// redisTemplate.multi();
// redisTemplate.opsForValue().set("name","Tom Tom");//执行成功
// redisTemplate.opsForValue().set("filmName","Tom And Jerry");//执行成功
// List<Object> execList = redisTemplate.exec();

/**
* 使用同一个连接执行多个命令
*/
// RedisConnection connection = redisConnectionFactory.getConnection();
// //乐观锁, 在 Reids 中存在 KEY = lock_key 的键值对
// connection.watch("lock_key".getBytes());
// //开启事务
// connection.multi();
// connection.set("age".getBytes(), String.valueOf(34).getBytes());
// connection.set("filmName".getBytes(), "Tom & Jerry".getBytes());
// //乐观锁:在exec()处打断点,使用其它客户端修改watch(key)的值,此处再方放行,事务执行失败返回null
// List<Object> execList = connection.exec();
// logger.info("execList:{}", JSON.toJSONString(execResults[0]));

/**
* 或在同一个 Session 中执行, 创建 SessionCallback
*/
redisTemplate.execute(new SessionCallback<Object>() {
@Override
public <K, V> Object execute(RedisOperations<K, V> operations) throws DataAccessException {
//开启事务
operations.multi();
redisTemplate.opsForValue().set("name1", "sun");
redisTemplate.opsForValue().set("name2", "moon");

//执行事务
List<Object> exec = operations.exec();
logger.info("exec:{}", JSON.toJSONString(exec));
return null;
}
});
}

}

Redis 4.x系列(十四):Spring Boot 在使用 Reids 管道和事务

http://blog.gxitsky.com/2018/11/08/Redis-14-user-redis-in-java/

作者

光星

发布于

2018-11-08

更新于

2022-08-14

许可协议

评论