使用Spring Boot實現Redis事務 | Vinsguru

banq發表於2020-12-01

大多數redis命令可以歸類到get/set下。預設情況下,所有這些命令都是原子的。但是,當我們需要順序執行一組命令時,則不能保證它是原子的。Redis通過multi,exec和discard命令提供了對事務的支援.

我們首先告訴redis我們將通過呼叫multi命令來執行一組操作。然後我們照常執行操作(A,B和C),如下圖所示。完成後,如果情況良好,我們將呼叫exec(),或者將其丟棄以忽略更改。

使用Spring Boot實現Redis事務 | Vinsguru

 

我們將考慮一個簡單的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));

    }
}

原始碼可在此處獲得

 

相關文章