关于Redis的Transaction

关于Redis的Transaction,查了很多资料,现在能实现Transaction的方法有3种:


1,使用SessionCallback

这个方法是很多资料上都推荐的,也是现在最可靠的一种。

实现方法:(例子来源于:http://docs.spring.io/spring-data/redis/docs/1.7.1.RELEASE/reference/html/#tx,进行了修改)

//execute a transaction
List<Object> txResults = redisTemplate.execute(new SessionCallback<List<Object>>() {
  public List<Object> execute(RedisOperations operations) throws DataAccessException {

    operations.multi();

    operations.opsForValue().set("test_long", "1");

    if(true){
        throw new RuntimeException();
    }

    operations.opsForValue().increment("test_long", 1);

   // This will contain the results of all ops in the transaction
   return operations.exec();
  }
});
System.out.println("Number of items added to set: " + txResults.get(0));

上面的例子中,在一个事务中做了两个操作,两个操作之间有一个抛异常的代码,执行完上面的代码后,查看Redis的数据,你会发现正如我们期望的,一个操作也没有执行。


2,自己实施bindConnection・unbindConnection

如果你查看RedisTemplate的public <T> T execute(SessionCallback<T> session)方法的源码,你会发现在这个方法中使用了RedisConnectionUtils.bindConnection和RedisConnectionUtils.unbindConnection,所以我们要做的就是自己调用这两个方法来实现这个过程。

@Repository
public class SampleRepository {
    @Autowired
    private RedisTemplate<String, Integer> redisTemplate;

    public Object updateWithCas3(String key, IntUnaryOperator func) {

        RedisConnectionUtils.bindConnection(redisTemplate.getConnectionFactory());
        try {
            redisTemplate.watch(key);

            BoundValueOperations<String, Integer> valueOps = redisTemplate.boundValueOps(key);
            Integer value = valueOps.get();

            redisTemplate.multi();

            valueOps.set(func.applyAsInt(value));

            return redisTemplate.exec();

        } finally {

            RedisConnectionUtils.unbindConnection(redisTemplate.getConnectionFactory());
        }
    }
}


3,使用enableTransactionSupport

这个方法写到最后,是因为有一些原因。下面一篇文章写了其中一个原因。

http://www.cnblogs.com/qijiang/p/5626461.html(“Spring Data Redis 中的事务陷阱”这部分)

还有一个原因是,像上面第1种方法那样的情况发生时,无法回滚第1个操作。

下面是一个使用这种结构的例子:

@Repository
public class SampleRepository {
    @Autowired
    private RedisTemplate<String, Integer> redisTemplate;

    public Object updateWithCas1(String key, IntUnaryOperator func) {
        try {
            redisTemplate.watch(key);

            BoundValueOperations<String, Integer> valueOps = redisTemplate.boundValueOps(key);

            Integer value = valueOps.get();

            redisTemplate.multi();
            valueOps.set(func.applyAsInt(value));
            return redisTemplate.exec();
        } catch (Exception e) {
            // InvalidDataAccessApiUsageException: ERR EXEC without MULTI
            System.out.println(e);
        }

        return null;
    }
}

在例子其它的地方还有一段代码,就是template.setEnableTransactionSupport(true)。

这种使用方法,用在像上面的例子中还是好用的,就是数据被别人更改后,不会提交到Redis中。

如果上面的exec()执行成功的话,返回是一个空的List,如果不成功的话,返回null,这点要注意一下。



最说一下官方关于SpringDataRedis的Transaction的说明。

说明很简单主要有几点:

1,Redis提供了multi, exec, and discard 这些命令来做事务控制,但RedisTemplate不保证在Transaction中的所有操作都来自同一个Connection(不太明白为什么会这样。。。)。提供了SessionCallback来完成在同一个Connection中,完成多个操作的方法。

2,下面有一段说明,不是很理解。

Transaction Support is disabled by default and has to be explicitly enabled for each RedisTemplate in use by setting setEnableTransactionSupport(true). This will force binding the RedisConnection in use to the current Thread triggering MULTI. If the transaction finishes without errors, EXEC is called, otherwise DISCARD. Once in MULTI, RedisConnection would queue write operations, all readonly operations, such as KEYS are piped to a fresh (non thread bound) RedisConnection.

看了一下例子:

/** Usage Constrainsts **/

// executed on thread bound connection
template.opsForValue().set("foo", "bar");

// read operation executed on a free (not tx-aware)
connection template.keys("*");

// returns null as values set within transaction are not visible
template.opsForValue().get("foo");

第1个例子,意思大概明白了,不知道到底执行进来是什么意思。

第2个例子,是说执行的read操作的时候,不是在Transaction里执行的。

第3个例子,通过实践知道,在redisTemplate.multi()方法后,用上面的方法opsForValue.get读取数据时,返回null。如果想返回自己想要的值,这条语句就必须在redisTemplate.multi()方法前执行。




下面是参考的一些资料:

日语的一个关于Transaction的总结,这里大部分都引用这个文章:http://fits.hatenablog.com/entry/2015/08/27/205539

上面提到的关于事务陷阱的文章:http://www.cnblogs.com/qijiang/p/5626461.html

也是一个关于事务陷阱的文章,有一些源码的说明,看的不是很透彻:http://jimgreat.iteye.com/blog/1596058

官方关于SpringDataRedis的Transaction的说明:http://docs.spring.io/spring-data/redis/docs/1.7.1.RELEASE/reference/html/#tx

一个有Pipeline使用例子的文章,还没看:http://shift-alt-ctrl.iteye.com/blog/1887473

猜你喜欢

转载自blog.csdn.net/zhangyunpengchang/article/details/78550048