bigywater.tech
常见报错 · 12

Redis WRONGTYPE:SET 会悄悄覆盖任何类型的 key,事务和 Lua 里出错都不回滚

“WRONGTYPE Operation against a key holding the wrong kind of value” 的意思很直白:命令的类型和 key 的实际类型不符。但真正麻烦的是怎么知道谁把别的类型写进了这个 key、事务和脚本里它会造成什么后果。这篇在本机把矩阵、覆盖、事务和排查都跑了一遍。

3 个
user:* 命名空间里,SCAN … TYPE string 找到的本该是 hash 的 key 数(共 50 个 key)
w1 留下
Lua 脚本在 WRONGTYPE 处中止:出错前的 SET w1 保留,出错后的 SET w2 没有执行
(nil) / OK
SET lst oops NX 因为 key 已存在返回 nil;不带 NX 的 SET 返回 OK 并把 list 悄悄改成 string

错误从哪来

实测 · 本机执行输出
  报错原文: LPUSH str x -> (error) WRONGTYPE Operation against a key holding the wrong kind of value
  命令            string      list      hash       set      zset    stream
  GET               ok WRONGTYPE WRONGTYPE WRONGTYPE WRONGTYPE WRONGTYPE
  STRLEN            ok WRONGTYPE WRONGTYPE WRONGTYPE WRONGTYPE WRONGTYPE
  APPEND            ok WRONGTYPE WRONGTYPE WRONGTYPE WRONGTYPE WRONGTYPE
  LLEN       WRONGTYPE        ok WRONGTYPE WRONGTYPE WRONGTYPE WRONGTYPE
  LPUSH      WRONGTYPE        ok WRONGTYPE WRONGTYPE WRONGTYPE WRONGTYPE
  HGET       WRONGTYPE WRONGTYPE        ok WRONGTYPE WRONGTYPE WRONGTYPE
  HSET       WRONGTYPE WRONGTYPE        ok WRONGTYPE WRONGTYPE WRONGTYPE
  SMEMBERS   WRONGTYPE WRONGTYPE WRONGTYPE        ok WRONGTYPE WRONGTYPE
  SADD       WRONGTYPE WRONGTYPE WRONGTYPE        ok WRONGTYPE WRONGTYPE
  ZRANGE     WRONGTYPE WRONGTYPE WRONGTYPE WRONGTYPE        ok WRONGTYPE
  ZADD       WRONGTYPE WRONGTYPE WRONGTYPE WRONGTYPE        ok WRONGTYPE
  XLEN       WRONGTYPE WRONGTYPE WRONGTYPE WRONGTYPE WRONGTYPE        ok
  XADD       WRONGTYPE WRONGTYPE WRONGTYPE WRONGTYPE WRONGTYPE        ok
  对照:INCR 用在字符串但内容不是整数 -> (error) ERR value is not an integer or out of range(不是 WRONGTYPE)

每种类型有自己的一组命令,用错了就是 WRONGTYPE。INCR 对非整数字符串是另一个报错 ERR。

不看类型的命令

实测 · 本机执行输出
  命令                  string      list      hash       set      zset    stream
  TYPE                    ok        ok        ok        ok        ok        ok
  EXISTS                  ok        ok        ok        ok        ok        ok
  TTL                     ok        ok        ok        ok        ok        ok
  EXPIRE                  ok        ok        ok        ok        ok        ok
  OBJECT ENCODING         ok        ok        ok        ok        ok        ok
  DUMP                    ok        ok        ok        ok        ok        ok
  COPY                    ok        ok        ok        ok        ok        ok
  RENAME                  ok        ok        ok        ok        ok        ok
  DEL                     ok        ok        ok        ok        ok        ok
  TYPE 的返回: [('str', 'string'), ('lst', 'list'), ('hsh', 'hash'), ('st', 'set'), ('zs', 'zset'), ('stm', 'stream')]

TYPE、EXISTS、DEL、RENAME、COPY 等对所有类型都能用,遇到 WRONGTYPE 先 TYPE 一下。

静默覆盖

实测 · 本机执行输出
  覆盖前: TYPE lst = list,LLEN lst = 3
  SET lst oops NX -> (nil)(key 已存在,没有写入);TYPE lst = list
  SET lst oops -> "OK"
  覆盖后: TYPE lst = string,LLEN lst -> (error) WRONGTYPE Operation against a key holding the wrong kind of value
  对照:RENAME 也会悄悄覆盖目标 key: RENAME str lst -> "OK",TYPE lst = string
  COPY 默认不覆盖: COPY str lst -> (integer) 0;加 REPLACE -> (integer) 1

SET 和 RENAME 不看原来的类型直接覆盖,数据悄悄丢失;SET NX 和 COPY(不带 REPLACE)不会覆盖。

事务与脚本

实测 · 本机执行输出
  MULTI 之后 SET a 1 -> "QUEUED"
  LPUSH str x(str 是字符串)-> "QUEUED"(入队时没有报错)
  SET b 2 -> "QUEUED"
  EXEC -> ['"OK"', 'WRONGTYPE…', '"OK"']
  之后 GET a = 1,GET b = 2(第 1、3 条已经生效)
  EVAL(SET w1;对字符串 LPUSH;SET w2)-> (error) WRONGTYPE Operation against a key holding the wrong kind of value script: ab9c978fb905e5925b4ea7ced1db6aa06edbe387, on @user_script:1.
  之后 GET w1 = x,GET w2 = None(出错之前的 w1 留下了,出错之后的 w2 没有执行)
  用 redis.pcall 捕获 -> "caught: WRONGTYPE Operation against a key holding the wrong kind of value"

EXEC 中某条命令 WRONGTYPE 时其它命令照常执行;Lua 里 redis.call 抛错会中止脚本,但此前的写入不回滚,redis.pcall 可以捕获。

排查

实测 · 本机执行输出
  user:* 共 50 个 key(50 个本来该是 hash):SCAN TYPE string 找到 3 个,hash 找到 47 个
  混进来的字符串 key: ['user:7', 'user:23', 'user:41']

SCAN 的 TYPE 选项可以按类型分批找出命名空间里类型不一致的 key。

分步演示

类型不匹配
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / MySQL 26.10.0 与 Redis 8.10.2(Homebrew,每个实验自己起实例,端口 9150 以上,只监听 127.0.0.1,不碰本机已有的实例)/ Python 3.15.0 + PyMySQL(按各篇注明用到哪些及版本);报错文本都是真实复现时原样捕获的;计时用 time.perf_counter,数字每次运行会有小幅抖动。本篇用一个自己起的 Redis 8.10.2 实例(端口 8130),纯标准库的 RESP 客户端;每格矩阵结果前都重新 FLUSHALL 并写入一批各类型的 key
    • 没有验证:Redis Cluster 下 RENAME / COPY 跨槽时的报错;模块类型(RedisJSON、RediSearch)的 WRONGTYPE 表现;Redis 7.0 之前版本的 SCAN TYPE 支持(该选项自 6.0 引入,本篇只在 8.10.2 上跑);客户端库(redis-py、Jedis、go-redis)把 WRONGTYPE 映射成什么异常;SORT、SORT_RO、OBJECT FREQ 等更多命令的类型要求
    京ICP备2026067502号-1京公网安备11010502063487号