redis事务不回滚是因为其本质是命令队列的原子性提交,无acid一致性保障,exec逐条执行且无预检机制,单条命令内存淘汰失败只影响自身,已执行命令不可逆。

Redis事务中写入触发内存淘汰,命令会失败
当 EXEC 执行时,其中任意一条写命令(如 SET、HSET)导致内存使用 ≥ maxmemory,且当前策略不是 noeviction,Redis 会尝试按配置策略淘汰 key;但若淘汰后仍无法腾出足够空间容纳该命令所需内存,该命令直接返回错误(如 (error) OOM command not allowed when used memory > 'maxmemory'.),整个事务**不会回滚**,已成功执行的命令依然生效。
为什么事务不因内存淘汰失败而整体回滚
Redis 事务本质是命令队列的原子性提交,不提供 ACID 中的“一致性”或“隔离性”保障。它不维护事务级锁、也不做预检内存——每条命令在 EXEC 阶段才真正执行并校验资源。因此:
Redis 缓存和数据结构管理技能。通过自然语言操作 Redis,支持 String、Hash、List、Set、ZSet、Stream 等数据结构操作。当用户提到 Redis、缓存、消息队列、会话存储时使用此技能。
-
EXEC是逐条执行,不是全有或全无的快照式提交 - 内存淘汰发生在单条命令执行路径中(
processCommand→freeMemoryIfNeeded()),失败只影响当前命令 - Redis 没有事务回滚机制,
DISCARD只能放弃未提交的队列,已EXEC的部分不可逆
不同淘汰策略对事务行为的影响差异
策略本身不改变事务语义,但会影响“是否能成功写入”的概率和可预测性:
-
volatile-lru/allkeys-lru:可能淘汰近期未访问的 key,若事务依赖这些 key 的存在(比如后续命令要读它们),会导致逻辑异常,但 Redis 不校验这种依赖 -
volatile-ttl:倾向淘汰快过期的 key,对长 TTL 缓存更友好,但事务中写入的新 key 若带短 TTL,反而可能被自己刚写入就淘汰(尤其高并发下 TTL 碰撞) -
noeviction:事务中首个超限写命令立即报错,其余命令不再执行(因为EXEC遇错即停),行为最确定 -
allkeys-random:淘汰完全不可预测,可能误删关键业务 key,应避免在事务密集场景使用
真实调试中容易忽略的关键点
内存淘汰是否触发,取决于命令执行瞬间的内存状态,而非 MULTI 开始时。这意味着:
- 事务块内命令之间若有其他客户端并发写入,可能提前耗尽内存,导致后面命令在
EXEC时直接失败 -
INFO memory中的used_memory_peak和mem_fragmentation_ratio比当前used_memory更能反映压力趋势 - 用
MEMORY USAGE <key></key>估算单个 key 内存开销,比盲目调大maxmemory更有效 -
CONFIG SET maxmemory-policy切换策略后,不会立刻清理,但下次事务写入就按新规则走——这点常被当成“热切换无感”,实际可能突然暴露数据保留逻辑缺陷










