大家好,我是walker
一个从文科自学转行的程序员~
爱好编程,偶尔写写编程文章和生活
欢迎关注公众号【I am Walker】,回复“电子书”,就可以获得200多本编程相关电子书哈~
我的gitee:https://gitee.com/shen-chuhao/walker.git 里面很多技术案例!
事务处理
redis事务提供了一种“将多个命令打包, 然后一次性、按顺序地执行”的机制, 并且事务在执行的期间不会主动中断 —— 服务器在执行完事务中的所有命令之后, 才会继续处理其他客户端的其他命令。
Redis中的事务是可以视为一个队列,即我们可以通过MULTI开始一个事务,这相当于我们声明了一个命令队列。
接下来,我们向Redis中提交的每条命令,都会被排入这个命令队列。
当我们输入EXEC命令时,将触发当前事务,这相当于我们从命令队列中取出命令并执行,所以Redis中一个事务从开始到执行会经历 开始事务 、 命令入队 和 执行事务 三个阶段。
# 开启事务
127.0.0.1:6379> multi
OK
127.0.0.1:6379> set name "walker"
QUEUED #会将执行命令加入队列中
127.0.0.1:6379> sadd list 1 2 3 4 5
QUEUED
127.0.0.1:6379> get name # 此时获取结果是获取不到的,只会显示QUEUED字段
QUEUED
# 执行队列命令
127.0.0.1:6379> exec
1) OK
2) (integer) 5
3) "walker"
- 事务是由一系列操作组成的单个逻辑工作执行单元。特别地,因为在Redis中命令是存储在一个队列中,所以,事务中的所有命令都会按顺序执行,并且在执行事务的过程中不会被客户端发送的其它命令中断。
- 事务是一个原子操作,事物中的命令只有两种执行结果,即全部执行或者全部不执行。如果客户端在使用MULTI命令开启事务后因为意外而没有执行EXEC命令,则事务中的所有命令都不会执行。同理,如果客户端在使用MULTI命令开启事务后执行EXEC命令,则事务中的所有命令都会执行。
- Redis中的事务可以使用DISCARD命令来清空一个命令队列,并放弃对事务的执行。如果命令在入队时发生错误,Redis将在客户端调用EXEC命令时拒绝执行并取消事务,但是在EXEC命令执行后发生的错误,Redis将选择自动忽略。
相关指令
multi
标记一个事务块的开始。
exec
执行所有事务块的命令
discard
取消事务,放弃执行事务块内的所有命令。 |
---|
127.0.0.1:6379> multi
OK
127.0.0.1:6379> set a1 name
QUEUED
127.0.0.1:6379> set a2 test
QUEUED
127.0.0.1:6379> get a2
QUEUED
# 清除队列命令
127.0.0.1:6379> discard
OK
# 之后获取就获取不到queue队列了
127.0.0.1:6379> get a2
(nil)
127.0.0.1:6379> get a1
(nil)
watch
Redis Watch 命令用于监视一个(或多个) key ,如果在事务执行之前这个(或这些) key 被其他命令所改动,那么事务将被打断
WATCH key [key ...]
springboot事务使用
开启事务
方式一
@Autowired
private RedisTemplate redisTemplate;
/**
* 事务方式一
*/
@Test
void testTransaction(){
//开启事务支持
//记得开启事务支持,但是不知道为何,我已经开启了,结果还是出现“ ERR EXEC without MULTI”,找不到原因,可能是redis版本问题
redisTemplate.setEnableTransactionSupport(true);
//开启事务
redisTemplate.multi();
ValueOperations<String, String> stringops = redisTemplate.opsForValue();
String key="test:transaction";
stringops.set(key,"1");
ListOperations<String, String> listOps = redisTemplate.opsForList();
String key1="test:transaction2";
listOps.leftPush(key1,"1111");
System.out.println(stringops.get(key));
System.out.println(listOps.range(key1, 0, 10));
//执行事务
redisTemplate.exec();
System.out.println(stringops.get(key));
System.out.println(listOps.range(key1, 0, 10));
}
方式二:使用SessionCallback
/**
* 执行事务方式二
* 这种方式网友们比较推荐
*/
@Test
public void testTransaction2(){
redisTemplate.execute(new SessionCallback<List<Object>>(){
@Override
public List<Object> execute(RedisOperations operations) throws DataAccessException {
operations.multi();
operations.opsForValue().set("name:1","walker11");
operations.opsForValue().set("name:2","walker22");
operations.opsForValue().set("name:3","walker33");
return redisTemplate.exec();
}
});
}
使用discard取消事务
/**
* 测试取消事务
*/
@Test
public void testDiscard(){
Integer flag=1;
redisTemplate.execute(new SessionCallback<List<Object>>(){
@Override
public List<Object> execute(RedisOperations operations) throws DataAccessException {
operations.multi();
operations.opsForValue().set("name:1","walker11");
operations.opsForValue().set("name:2","walker22");
operations.opsForValue().set("name:3","walker33");
if(flag.equals(1)){
//取消事务,经测试,在使用discard之后,不能再次执行exec(),否则会出现 ERR EXEC without MULTI错误
redisTemplate.discard();
return null;
}
return redisTemplate.exec();
}
});
}
更多推荐