大多数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));
} }
|
源代码可在此处获得。