Reids事务
1.在Redis中,也许存在多个客户端同时向Redis系统发送命令的并非的可能性,因此同一份数据可能被不同线程所操控,这样就出现了并发下的数据一致问题。
事物是一个被隔离的操作,事务中的方法都会被Redis按顺序执行,事务在执行过程中不会被其他客户端发生的命令所打断。例如,在Redis中开启事务是multi命令,执行事务是exec命令,开启到执行命令之间,Redis采取的是进入队列的形式,直到exec命令出现,才会一次性发送队列里的命令去执行,而在执行这些命令的时候其他客户端就不能再插入任何命令
2.事务的具体操作
//使用Redis事务
@Test
public void TestRedisTransaction()
{
RedisTemplate redisTemplate = applicationContext.getBean(RedisTemplate.class);
/*edisTemplate直接调用opfor..来操作redis数据库,每执行一条命令是要重新拿一个连接,因此很耗资源,
让一个连接直接执行多条语句的方法就是使用SessionCallback*/
SessionCallback<Object> callback = new SessionCallback<Object>() {
@Override
public Object execute(RedisOperations operations) throws DataAccessException {
String value1=null;
operations.multi();
operations.opsForValue().set("name", "qinyi");
value1 = (String) operations.opsForValue().get("name");
operations.opsForValue().get("gender");
operations.opsForValue().get("age");
System.out.println("命令只是进入了队列,而没有被执行,所以此时value1应为空:"+value1);
operations.opsForValue().set("gender", "male");
operations.opsForValue().set("age", "19");
List<Object> list = operations.exec();
value1 = (String) operations.opsForValue().get("name");
System.out.println("执行了exec()就不为空:"+value1);
System.out.println("list:"+list);
return list;
}
};
//执行Redis命令
List<Object> value = (List<Object>) redisTemplate.execute(callback);
System.out.println("excute方法的的返回值:"+value);
}
运行结果:
可以看到没有exec()命令之前get方法返回值一律为空,最后使用redisTemplate.execute执行我们再SessionCallBack的业务逻辑,否则将不会执行SessionCallBack中的内容;
事务回滚
如上代码
@Test
public void TestRedisTransaction()
{
RedisTemplate redisTemplate = applicationContext.getBean(RedisTemplate.class);
/*edisTemplate直接调用opfor..来操作redis数据库,每执行一条命令是要重新拿一个连接,因此很耗资源,
让一个连接直接执行多条语句的方法就是使用SessionCallback*/
SessionCallback<Object> callback = new SessionCallback<Object>() {
@Override
public Object execute(RedisOperations operations) throws DataAccessException {
String value1=null;
operations.multi();
operations.opsForValue().set("name", "qinyi");
value1 = (String) operations.opsForValue().get("name");
operations.opsForValue().get("gender");
operations.opsForValue().get("age");
System.out.println("命令只是进入了队列,而没有被执行,所以此时value1应为空:"+value1);
operations.opsForValue().set("gender", "male");
operations.opsForValue().set("age", "19");
**operations.discard(); //事务回滚 当事务回滚后还进行exec命令,会报异常**
List<Object> list = operations.exec();
value1 = (String) operations.opsForValue().get("name");
System.out.println("执行了exec()就不为空:"+value1);
System.out.println("list:"+list);
return list;
}
};
//执行Redis命令
List<Object> value = (List<Object>) redisTemplate.execute(callback);
System.out.println("excute方法的的返回值:"+value);
}
对于Redis而言,其回滚能力也和数据库不太一样。需要特别注意两个地方:
1.Redis事务遇到命令格式正确而数据类型不符合时
例如:
@Test
public void TestRedisRollback() {
RedisTemplate redisTemplate = applicationContext.getBean(RedisTemplate.class);
SessionCallback<Object> callback = new SessionCallback<Object>() {
@Override
public Object execute(RedisOperations operations) throws DataAccessException {
operations.multi();
//这里我设置了String类型的字符串
operations.opsForValue().set("number","asd1");
operations.opsForValue().set("哈哈","sdsd");
//对字符串执行+1操作
operations.opsForValue().increment("number", 1);
return operations.exec();
}
};
System.out.println(redisTemplate.execute(callback));
}
虽然控制台抛出了异常,但是其之前之后的命令依然set了进去,没有回滚。
2.Redis对于命令格式错误却是另外一种情况
如图我们开启缓存 同样像执行增加操作,但是故意将命令输入错误 然后再提交,而出现这种命令格式错误,redis前后都直接回滚了,得到的值都为空