一、概念
在redis中没有隔离级别的概念,都是串行化执行。
事务执行过程:是把事务过程中执行的命令放入队列中(队列中的命令要么都执行,要么都不执行),输入exec命令开始执行事务
在redis中,单条命令是原子性执行的,但是事务不能保证原子性,且没有回滚。事务中任意命令执行失败,其余命令仍会被执行。
事务的三个阶段:
- 开始事务
- 执行的命令加入队列
- 执行事务或回滚
事务命令:
- watch key1 key2……:监视一个或多个key,如果事务执行之前,这些key被改动,则事务被打断。(类似乐观锁)
- multi:事务开始
- exec:执行事务(执行后无论事务成功还是失败,watch加的监控都会被取消掉)
- discard:回滚事务
- unwatch:取消监视
二、redis客户端如何使用事务
(1)执行事务
(2)回滚事务
(3)事务中异常
1、事务队列中存在命令错误(类似java编译错误),队列中所有命令都不执行
2、事务队列中存在语法性错误(类似java运行时异常),出错的命令不执行,其他命令正常执行
(4)使用watch
1、模拟watch k2 中数据未变动,正常执行的过程如下:
2、模拟watch k2 中数据发生变动,导致事务执行失败,如图:
事务中途使用其他客户端修改k2的值,如下:
三、springboot如何使用事务
有两种方式使用redis事务,手动开启或自动开启
redis命令对应的RedisTemplate的方法如下:
RedisTemplate.multi();
RedisTemplate.exec();
RedisTemplate.discard();
RedisTemplate.watch("");
RedisTemplate.unwatch()
(1)手动开启
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
@Autowired
private StringRedisTemplate stringRedisTemplate;
@Override
public void test() {
//设置事务支持
stringRedisTemplate.setEnableTransactionSupport(true);
//开启事务
stringRedisTemplate.multi();
try {
stringRedisTemplate.opsForValue().set("k","v");
stringRedisTemplate.opsForValue().set("k1","v1");
//提交事务
List<Object> exec = stringRedisTemplate.exec();
for (Object o : exec) {
System.out.println(o);
}
} catch (Exception e) {
e.printStackTrace();
//回滚事务
stringRedisTemplate.discard();
}
String k1 = stringRedisTemplate.opsForValue().get("k1");
System.out.println("k1:"+k1);
}
执行结果打印:
(2)自动开启
1、pom
<!--redis客户端-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!--redis事务依赖JDBC事务管理-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<!--嵌入式数据库h2。配置了jdbc必须要配置数据库,为了方便配置h2。如果项目有mysql的话就不需要h2数据库了-->
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
</dependency>
2、启动类注册StringRedisTemplate类,并开启事务支持。
@SpringBootApplication
public class DemoApplication {
@Bean
public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory)
throws UnknownHostException {
StringRedisTemplate template = new StringRedisTemplate();
//开启事务支持(默认是没有关闭的)
template.setEnableTransactionSupport(true);
template.setConnectionFactory(redisConnectionFactory);
return template;
}
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
3、controller
@RestController
public class DemoController {
@Autowired
private DemoService demoService;
@Autowired
private StringRedisTemplate stringRedisTemplate;
@RequestMapping("/test")
public void getValue() throws Exception {
String k = stringRedisTemplate.opsForValue().get("k1");
System.out.println("k1原值:"+k);
try {
demoService.test();
} catch (Exception e) {
System.out.println("redis事务执行失败");
}
String k1 = stringRedisTemplate.opsForValue().get("k1");
System.out.println("k1事务之后的值:"+k1);
}
}
4、service
@Service
public class DemoServiceImpl implements DemoService {
@Autowired
private StringRedisTemplate stringRedisTemplate;
@Override
@Transactional(rollbackFor = Exception.class)
public void test() throws Exception {
stringRedisTemplate.opsForValue().set("k","v");
stringRedisTemplate.opsForValue().increment("k1");
throw new Exception();
}
}
测试:模拟抛出异常情况
事务执行失败,数据没有被修改