实现事务的方法
- multi(开启事务)+exec(提交)+discard(放弃事务)+watch(CAS乐观锁)
- lua脚本
Redis事务的特点
- 事务中的所有方法串行执行,执行过程不会被其它client打断;
- 事务是原子的,命令全执行,或全不执行,但是,Redis的原子性和关系数据库的原子性不一致,Redis虽然能保证命令全执行,但是如果某条命令在执行时出错,Redis会忽略它,继续执行下一条命令。
事务中的错误处理
- 语法错误或系统错误:对于这种类型的错误,在client执行事务前就能发现(事务中命令成功提交会返回queued),多数client会选择discard事务。从Redis2.6.5开始,服务端会记录这样的错误,在客户端exec时报错并自动丢弃该事务;
- 执行时报错:服务端会忽略该条语句的错误,直接执行事务中的下一跳语句。也就是说,执行时错误没有回滚的操作,官方的说法是这样有利于保持Redis简单高效。
Jedis代码演示
private void doWithMulti(Jedis jedis) {
Transaction transaction = jedis.multi();
transaction.incr("num.trans");
transaction.set("val.trans", "inTrans");
transaction.sismember("wilson.friends", "tom");
System.out.println("Transaction result:"+transaction.exec());
jedis.del(new String[]{"num.trans","val.trans"});
}
private void doWithMultiAndCAS(Jedis jedis) {
List<Object> res = null;
do {
jedis.watch("wilson.friends");
Boolean isFriend=jedis.sismember("wilson.friends", "katty");
if (!isFriend) {
Transaction transaction = jedis.multi();
transaction.incr("num.trans");
transaction.set("val.trans", "inTrans");
res = transaction.exec();
System.out.println("Transaction result:"+res);
}
} while (null == res);
}
private void doWithPipeline(Jedis jedis) {
Pipeline line = jedis.pipelined();
line.multi();
line.incr("num.trans");
line.set("val.trans", "inTrans");
line.sadd("wilson.friends", "gary");
Response<String> numRes = line.get("num.trans");
line.exec();
line.sync();
System.out.println("num.trans val:"+numRes.get());
}