0%

redis事务

Redis 事务详解(基于 6.0.10 版本)

Redis 事务是一组命令的集合,通过序列化执行机制保证命令的原子性执行(非传统 ACID 原子性),适用于需要批量执行命令且不被中断的场景。本文将深入解析 Redis 事务的特性、命令流程及实际应用。

事务的核心特性

Redis 事务基于单线程架构设计,与传统数据库事务(如 MySQL)有显著区别:

  1. 串行化执行
    事务中的所有命令会按顺序排队,在 EXEC 执行期间,Redis 不会插入其他客户端的命令,保证事务的独占性。
  2. 无隔离级别
    事务提交前,所有命令仅入队不执行,因此:
    • 事务内的查询无法看到事务内其他命令的修改(未执行)。
    • 事务外的查询也无法看到事务内的未执行命令,不存在 “脏读”“不可重复读” 等问题。
  3. 不保证原子性
    • 若事务内存在语法错误(如命令拼写错误),EXEC 会拒绝执行所有命令。
    • 若事务内存在运行时错误(如对字符串执行 INCR),Redis 会继续执行其他命令,仅返回该命令的错误,不支持回滚。

事务命令详解

命令 作用
MULTI 标记事务开始,后续命令进入队列(返回 QUEUED)。
EXEC 执行队列中的所有命令,返回结果列表(若事务被中断,返回 nil)。
DISCARD 取消事务,清空队列(需在 EXEC 前执行)。
WATCH key [key...] 监视键,若事务执行前键被修改,事务会被中断(乐观锁机制)。
UNWATCH 取消所有监视的键(EXECDISCARD 后会自动执行)。

事务执行流程

1. 正常执行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 开始事务
127.0.0.1:6379> MULTI
OK

# 命令入队
127.0.0.1:6379> SET a 1
QUEUED
127.0.0.1:6379> INCR a
QUEUED

# 执行事务
127.0.0.1:6379> EXEC
1) OK
2) (integer) 2 # 最终 a 的值为 2

2. 语法错误导致事务失败

1
2
3
4
5
6
7
8
9
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> SET b 2
QUEUED
127.0.0.1:6379> INCR # 语法错误(缺少键)
(error) ERR wrong number of arguments for 'incr' command

127.0.0.1:6379> EXEC # 整个事务被拒绝
(error) EXECABORT Transaction discarded because of previous errors.

3. 运行时错误不影响其他命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
127.0.0.1:6379> SET c "abc"  # 字符串值
OK

127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> INCR c # 运行时错误(对字符串自增)
QUEUED
127.0.0.1:6379> SET d 4
QUEUED

127.0.0.1:6379> EXEC
1) (error) ERR value is not an integer or out of range # 错误命令
2) OK # 其他命令正常执行
127.0.0.1:6379> GET d # d 成功设置
"4"

WATCH 命令与乐观锁

WATCH 用于实现乐观锁,防止事务期间关键数据被修改:

基本用法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 初始化余额
127.0.0.1:6379> SET balance 100
OK

# 监视 balance
127.0.0.1:6379> WATCH balance
OK

# 其他客户端修改 balance(模拟并发)
# 另一个终端:127.0.0.1:6379> SET balance 200

# 开始事务
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> DECRBY balance 50 # 入队
QUEUED

# 执行事务(因 balance 被修改,事务中断)
127.0.0.1:6379> EXEC
(nil)

127.0.0.1:6379> GET balance # 余额仍为 200
"200"

注意事项

  • WATCH 必须在 MULTI 前执行,否则无效。
  • 事务执行后(无论成功与否),所有 WATCH 会自动取消。
  • 若需重试事务,需重新执行 WATCH 和事务命令。

事务与管道(Pipeline)的结合

事务与管道结合可减少网络往返次数,提升批量操作性能:

Java 示例(使用 Spring Data Redis)

1
2
3
4
5
6
7
8
9
10
11
12
13
List<Object> results = stringRedisTemplate.executePipelined(
new SessionCallback<Object>() {
@Override
public Object execute(RedisOperations operations) throws DataAccessException {
operations.multi(); // 开启事务
operations.opsForValue().set("k1", "v1");
operations.opsForValue().increment("k2");
operations.exec(); // 执行事务
return null;
}
}
);
// 结果:[OK, 1]
  • 管道:批量发送命令,减少网络 IO 次数。
  • 事务:保证命令串行执行,不被其他请求中断。

实际应用建议

  1. 错误处理
    执行 EXEC 后需遍历结果,对失败命令手动处理(如重试或补偿)。
  2. 替代方案
    • 若需强原子性,可使用 Lua 脚本(Redis 保证脚本执行的原子性)。
    • 分布式场景下,结合 Redisson 等框架实现分布式锁 + 事务。
  3. 性能优化
    事务命令不宜过多(避免阻塞单线程),建议拆分大事务为多个小事务

欢迎关注我的其它发布渠道

表情 | 预览
快来做第一个评论的人吧~
Powered By Valine
v1.3.10