使用Spring Boot实现Redis事务 | Vinsguru


大多数redis命令可以归类到get/set下。默认情况下,所有这些命令都是原子的。但是,当我们需要顺序执行一组命令时,则不能保证它是原子的。Redis通过multi,exec和discard命令提供了对事务的支持.
我们首先告诉redis我们将通过调用multi命令来运行一组操作。然后我们照常执行操作(A,B和C),如下图所示。完成后,如果情况良好,我们将调用exec(),或者将其丢弃以忽略更改。

 
我们将考虑一个简单的Bank应用程序,其中Redis是主要数据库。我们有一组帐户。用户可以将资金从一个帐户转移到另一个帐户。
让我们看看如何使用Spring Boot将资金转账作为Redis交易来实现。
让我们创建一个简单的Account类,如下所示。

@Data
@AllArgsConstructor(staticName = "of")
public class Account implements Serializable {

    private int userId;
    private int balance;

}

 
Redis事务– SessionCallBack:
Spring Data Redis提供了SessionCallBack接口,当我们需要作为一个事务执行多个操作时,需要实现该接口。
  • MoneyTransfer是SessionCallBack的实现,其中包含用于汇款的业务逻辑。
  • 它将收到帐户ID和要转帐的金额。

@AllArgsConstructor(staticName = "of")
public class MoneyTransfer implements SessionCallback<List<Object>> {

    public static final String ACCOUNT =
"account";
    private final int fromAccountId;
    private final int toAccountId;
    private final int amount;

    @Override
    public <K, V> List<Object> execute(RedisOperations<K, V> redisOperations) throws DataAccessException {
        var operations = (RedisTemplate<Object, Object>) redisOperations;
        var hashOperations = operations.opsForHash();
        var fromAccount = (Account) hashOperations.get(ACCOUNT, fromAccountId);
        var toAccount = (Account) hashOperations.get(ACCOUNT, toAccountId);
        if(Objects.nonNull(fromAccount) && Objects.nonNull(toAccount) && fromAccount.getBalance() >= amount){
            try{
                operations.multi();
                fromAccount.setBalance(fromAccount.getBalance() - amount);
                toAccount.setBalance(toAccount.getBalance() + amount);
                hashOperations.put(ACCOUNT, fromAccountId, fromAccount);
                hashOperations.put(ACCOUNT, toAccountId, toAccount);
                return operations.exec();
            }catch (Exception e){
                operations.discard();
            }
        }
        return Collections.emptyList();
    }
}

 
测试
我们可以在Redis中添加一些帐户并测试Redis事务。

@SpringBootApplication
public class RedisTransactionApplication implements CommandLineRunner {

    public static void main(String[] args) {
        SpringApplication.run(RedisTransactionApplication.class, args);
    }

    @Autowired
    private RedisTemplate<Object, Object> redisTemplate;

    @Override
    public void run(String... args) throws Exception {

        // initialize some accounts
        this.redisTemplate.opsForHash().put(MoneyTransfer.ACCOUNT, 1, Account.of(1, 100));
        this.redisTemplate.opsForHash().put(MoneyTransfer.ACCOUNT, 2, Account.of(2, 20));

       
// do the transaction
        this.redisTemplate.execute(MoneyTransfer.of(1, 2, 30));

       
// print the result
        System.out.println(this.redisTemplate.opsForHash().get(MoneyTransfer.ACCOUNT, 1));
        System.out.println(this.redisTemplate.opsForHash().get(MoneyTransfer.ACCOUNT, 2));

    }
}

源代码可在此处获得