目录
什么是redis事务
Redis事务就是一次性、顺序性、排他性的执行一个队列中的一系列命令,当调用了EXEC命令将执行所有命令。
Redis中,单条命令是原子性执行的,但Redis事务不保证原子性,且没有回滚。事务中任意命令执行失败,其余的命令仍会被执行。
Redis事务常用方法、
multi()
:开启事务
watch(String... keys)
:监听key是否被修改,如果在执行exec()
前被修改则取消事务
unwatch()
:取消监听
exec()
:将事务需要执行的命令发送给Redis执行,并返回结果。返回结果是一个按命令执行顺序返回的list,
discard()
:取消事务
注意:Redis事务和管道一样,都是异步模式。在事务开启未执行前无法获取到查询操作的结果,必须等待执行exec()后才能获取到结果
小知识点 junit中的@Before
、@After
注解
@Before
:被@After
修饰的方法在@Test
方法执行前执行
@After
:被@After
修饰的方法在@Test
方法执行后执行
demo测试
测试时的公共方法和Jedis对象
static final Jedis jedis = RedisSimplePool.getJedis();
/**
* 清空数据库
*/
@Before
public void flushdb() {
jedis.select(0); //切换数据库
System.out.println("清空数据库");
jedis.flushDB();
}
/**
*
* @描述:返还连接
* @作者:严磊
* @时间:2020年6月17日 下午10:27:02
*/
@After
public void closeConn() {
RedisSimplePool.returnJedis(jedis);
}
1、开启事务并提交
/**
*
* @描述:测试开启事务并提交
* @作者:严磊
* @时间:2020年6月17日 下午9:10:33
*/
@Test
public void commitTest() {
// 开启事务
Transaction transaction = jedis.multi();
transaction.set("key-1", "value-1");
transaction.set("key-2", "value-2");
transaction.set("key-3", "value-3");
transaction.set("key-4", "value-4");
// 提交事务
transaction.exec();
System.out.println(jedis.keys("*"));
}
执行结果:
在transaction.exec();
处加入断点。
执行exec方法前,数据库没有数据。
执行exec方法后,数据库插入了4个key。
2、开启事务然后主动放弃事务
/**
*
* @描述:测试开启事务然后主动放弃事务
* @作者:严磊
* @时间:2020年6月17日 下午9:11:30
*/
@Test
public void discardTest() {
// 开启事务
Transaction transaction = jedis.multi();
transaction.set("key-1", "value-1");
transaction.set("key-2", "value-2");
transaction.set("key-3", "value-3");
transaction.set("key-4", "value-4");
// 放弃事务
transaction.discard();
System.out.println(jedis.keys("*"));
}
3、使用watch监听指定的键是否被修改,如果在执行exec前被修改则取消事务
/**
*
* @描述:watch 命令会标记一个或多个键,如果事务中被标记的键,在提交事务之前被修改了,那么提交事务,事务就会失败。
* @作者:严磊
* @时间:2020年6月17日 下午9:12:25
* @throws InterruptedException
*/
@Test
public void watchTest() throws InterruptedException {
//boolean resultValue = transMethod(200);
boolean resultValue = transMethod(10);
System.out.println("交易结果(事务执行结果):" + resultValue);
int balance = Integer.parseInt(jedis.get("balance"));
int debt = Integer.parseInt(jedis.get("expense"));
System.out.printf("balance: %d, expense: %d\n", balance, debt);
}
// 支付操作
public static boolean transMethod(int amtToSubtract) throws InterruptedException {
int balance; // 余额
int expense; // 已消费额
jedis.set("balance", "100");
jedis.set("expense", "0");
jedis.watch("balance", "expense");
balance = Integer.parseInt(jedis.get("balance"));
// 余额不足
if (balance < amtToSubtract) {
jedis.unwatch(); // 放弃所有被监控的键
System.out.println("余额不足");
return false;
}
Transaction transaction = jedis.multi();
// 扣钱
transaction.decrBy("balance", amtToSubtract);
Thread.sleep(15000); // 在外部修改 balance 或者 expense
transaction.incrBy("expense", amtToSubtract);
// list为空说明事务执行失败
List<Object> list = transaction.exec();
if(list==null)
{
return false;
}
else
{
list.forEach(System.out::println);
return true;
}
}
执行结果:
如果在事务执行exec方法前,对key balance
或expense
进行了修改,exec()方法会返回null。事务中的命令都不会执行。