本章节将介绍 Spring Data Redis 对 Redis 事务的支持。
事务是数据库中一个单独的执行单行(Unit),事务就是由一种数据操纵语言DML语句组成,这一组DML要么全不成功要么全部失败,是一个整体。
事务应该具有4个属性(ACID 特性):
原子性(atomicity):一个事务是一个不可分割的工作单位,事务中包括的操作要么都做,要么都不做。
一致性(consistency):事务必须是使数据库从一个一致性状态变到另一个一致性状态。一致性与原子性是密切相关的。
隔离性(isolation):一个事务的执行不能被其他事务干扰。即一个事务内部的操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事务之间不能互相干扰。
持久性(durability):持久性也称永久性(permanence),指一个事务一旦提交,它对数据库中数据的改变就应该是永久性的。接下来的其他操作或故障不应该对其有任何影响。
Redis 事务可以一次执行多个命令,并且带有以下两个重要的保证:
事务是一个单独的隔离操作:事务中的所有命令都会序列化、按顺序地执行。事务在执行的过程中,不会被其他客户端发送来的命令请求所打断。
事务是一个原子操作:事务中的命令要么全部被执行,要么全部都不执行。
批量操作在发送 EXEC 命令前被放入队列缓存。当收到 EXEC 命令后进入事务执行,事务中任意命令执行失败,其余的命令依然被执行。在事务执行过程,其他客户端提交的命令请求不会插入到事务执行命令序列中。
一个事务从开始到执行会经历以下三个阶段:
(1)开始事务
(2)命令入队
(3)执行事务
以下是一个事务的例子, 它先以 MULTI 命令开始一个事务, 然后将多个命令入队到事务中,最后由 EXEC 命令触发事务, 一并执行事务中的所有命令:
D:\server\redis-x64-5.0.14.1>redis-cli 127.0.0.1:6379> multi OK 127.0.0.1:6379> set username "tom" QUEUED 127.0.0.1:6379> set password "aaaaa" QUEUED 127.0.0.1:6379> get username QUEUED 127.0.0.1:6379> get password QUEUED 127.0.0.1:6379> exec 1) OK 2) OK 3) "tom" 4) "aaaaa" 127.0.0.1:6379>
上面事务中,先分别添加 username 和 password 键,然后再获取它们的值。
Redis 通过 multi、exec 和 discard 命令为事务提供支持。这些操作 RedisTemplate 提供了支持。然而,RedisTemplate 并不能保证用相同的连接运行事务中的所有操作(可能事务中的语句被多个连接执行)。
Spring Data Redis 提供了 SessionCallback 接口,用于需要使用同一连接执行多个操作时,例如使用 Redis 事务时。以下示例使用 multi 方法:
// 执行一个事务 List<Object> txResults = redisTemplate.execute(new SessionCallback<List<Object>>() { public List<Object> execute(RedisOperations operations) throws DataAccessException { operations.multi(); operations.opsForSet().add("key", "value1"); // 这将包含事务中所有操作的结果 return operations.exec(); } }); System.out.println("Number of items added to set: " + txResults.get(0));
RedisTemplate 使用其 value、hash key 和 hash value 的序列化器,在返回之前对 exec 的所有结果进行反序列化。exec 还有一个额外的方法,它允许您为事务结果传递一个定制的序列化程序。方法定义如下:
List<Object> exec() 执行用 multi() 启动的事务中所有排队的命令。
List<Object> exec(RedisSerializer<?> valueSerializer) 执行一个事务,使用提供的 RedisSerializer 来反序列化任何byte[] 或 byte[] 集合结果。
提示:
从1.1版开始,RedisConnection 和 RedisTemplate 的 exec 方法进行了重要更改。以前,这些方法直接从连接器返回事务的结果。这意味着数据类型通常与 RedisConnection 方法返回的数据类型不同。例如,zAdd 返回一个布尔值,指示元素是否已添加到排序集。大多数连接器将此值作为 long 值返回,Spring Data Redis 执行转换。另一个常见的区别是,大多数连接器都会为诸如 set 之类的操作返回状态回复(通常是字符串,OK)。这些回复通常被 Spring Data Redis 丢弃。
在1.1之前,不会对 exec 的结果执行这些转换。此外,结果没有在 RedisTemplate 中反序列化,因此它们通常包括原始字节数组。如果此更改破坏了应用程序,请在 RedisConnectionFactory 上将 convertPipelineAndTxResults 设置为 false 以禁用此行为。