redis事务:
redis的事务和mysql等关系型数据库的事务不太一样,redis中的事务不会回滚,只能手动收拾事务失败后的烂摊子。
在命令行中,MULTI是事务的开始命令。EXEC是事务的执行命令。并且会按顺序执行事务中的命令,不会被其他事务的命令打扰。在java操作redis中,是通过.multi()和.exec()开始和执行事务。
MULTI会将之后加入的所有命令按顺序的加入到命令队列中,调用EXEC会依次的执行这些命令。
EXEC命令的返回值是一个数组,其中的每个元素分别是原子化事务中的每个命令的返回值,返回值的顺序和命令的发出顺序是相同的。
如果是因为语法错误,如没有相应命令或者命令参数传递不匹配,则事务中的命令会全部不执行。因为这个错误命令是在被放入队列时失败。命令还没有执行。
如果是其他原因导致事务失败,那么正确的命令将被执行,错误的不会被执行,整个过程也不会回滚。
WATCH命令:
watch是对一个或几个key对于的值进行监控,如果在事务执行之前,监控的值与开始监控时的值不一样,那么有关该值的事务将不会被执行,exec的执行结果在java中会返回null,在命令行中会返回(nil)。类似于AQS机制。
DISCARD会清除所有先前在一个事务中放入队列的命令,然后恢复正常的连接状态。 如果使用了WATCH命令,那么DISCARD命令就会将当前连接监控的所有键取消监控。 这个命令的运行格式如下所示: DISCARD 这个命令的返回值是一个简单的字符串,总是OK。
UNWATCH 清除所有先前为一个事务监控的键。 如果你调用了EXEC或DISCARD命令,那么就不需要手动调用UNWATCH命令。 这个命令的运行格式如下所示: UNWATCH 这个命令的返回值是一个简单的字符串,总是OK。 时间复杂度总是O(1)。
所以,exec,discard,unwatch命令都会清除所有监视。
jedis.watch(productStockCacheKey);
//开启Redis事务
Transaction tx = jedis.multi();
//库存减一
tx.decrBy(productStockCacheKey,1);
//执行事务
List<Object> resultList = tx.exec();
if (resultList == null || resultList.isEmpty()) {
jedis.unwatch();//也可以不调用,因为exec有同样效果
//watch监控被更改过----物品抢购失败;
System.out.println("商品:"+product.getProductName()+",watch监控被更改,物品抢购失败");
throw new SecKillException(SecKillEnum.FAIL);
}