我们知道Redis 是支持弱事务的, Redis 中的事务只能保证命令在同一个连接中执行,当发生异常后并不会进行事务回滚. SDR 对这种弱事务也提供了支持。 除了支持redis-cli 客户端的multi, exec, discard 命令之外, SDR 还支持使用声明式注解@Transaction 注解类强制开启事务, 但笔者并未使用过,如果有兴趣,可以自行尝试。
1. SDR 事务
1.1 SDR 事务特点
- 事务中所有命令, 在同一连接中执行
- 事务中的所有命令,在执行exec方法时,才开始依次执行
- 事务中所有命令都会执行,纵使中间有命令报错,也不会终止后面的命令执行
- 事务中发生异常时,已执行的命令不会发生回滚。
1.2 相关API
方法签名 | 方法描述 |
---|---|
void multi() | 开启事务 |
List exec() | 执行事务, 返回每条命令的返回结果 |
void discard() | 丢弃事务 |
1.3 @Transaction 强制开启事务
SDR 中默认RedisTemplate是关闭事务,可以配置自定义RedisTemplate时,设置强制开启事务 setEnableTransactionSupport(true). 此方法会强制当前RedisTemplate 绑定的RedisConnection在当前线程中强制开启事务, 当事务结束时,若没有异常则自动触发exec方法提交事务,否则自动触发discard方法丢弃事务.
1.4 事务和pipeline 的区别
虽然事务和pipeline看起来很相似,都可以批量执行命令,但是还是有本质性的不同的。
- 相同点:
- pipeline 和 事务都是在同一连接中执行的, 减少了多次TCP连接创建销毁的实际,提高了性能
- pipeline 和 事务当执行命令过程出现异常时均不会终止后面的命令运行
- 不同点:事务是当执行exec方法时, 统一开始执行命令; 当执行ops的相关命令时,并非真正的执行. 而pipeline则时,执行ops 命令时就真正开始执行命令.
2. API 测试
2.1 提交事务exec
- 执行测试用例前,先清空redis数据库或删除redis中key为 A,B,C 的key-value
- 从控制台输出可以看出, 虽然执行第二天命令失败了,但是并没有抛出异常,也没有终止剩余命令执行
@Test
public void test_exec(){
List<Object> txResults = stringRedisTemplate.execute(new SessionCallback<List<Object>>() {
@Override
public List<Object> execute(RedisOperations operations) throws DataAccessException {
// 开启事务
operations.multi();
ValueOperations ops = operations.opsForValue();
ops.set("A", "aaaa");
ops.setIfPresent("B","aaa");
ops.set("C", "cccc");
// 执行事务
return operations.exec();
}
});
// 遍历返回结果
for (Object txResult : txResults) {
System.out.println(txResult);
}
/* 控制台输出
true
false
true
*/
}
2.2 取消事务discard
- discard 会取消事务中的命令,并丢弃
- 测试之后,发现redis中并没有A, B, C三个key, 这说明虽然调用了ops.set方法, 但是命令并没有执行.
@Test
public void test_discard(){
List<Object> txResults = stringRedisTemplate.execute(new SessionCallback<List<Object>>() {
@Override
public List<Object> execute(RedisOperations operations) throws DataAccessException {
// 开启事务
operations.multi();
ValueOperations ops = operations.opsForValue();
ops.set("A", "aaaa");
ops.set("B","aaa");
ops.set("C", "cccc");
// 取消事务
operations.discard();
return null;
}
});
System.out.println("txResults:" + txResults);
}