3. Redis基础回顾:常用数据结构、命令、连接方式、错误码分类

在契约测试中,对Redis错误码的断言依赖于对Redis底层通信机制的准确理解。本节系统梳理Redis的核心数据结构、操作命令、连接模式及错误码体系,为后续构建搜索服务的契约测试用例奠定基础。

3.1 常用数据结构与适用场景

Redis提供多种数据结构,搜索服务中高频使用的包括以下五种:

数据结构 内部编码 典型搜索场景 时间复杂度
String int / embstr / raw 缓存搜索结果摘要、存储API限流计数器 O(1)
Hash ziplist / hashtable 存储文档元数据(标题、摘要、得分) O(1) 单字段
List quicklist 消息队列、搜索日志写入管道 O(1) 头尾操作
Set intset / hashtable 去重索引、标签集合运算 O(1) 成员判断
Sorted Set ziplist / skiplist 搜索热词排名、分页排序 O(log N) 插入/查询
契约测试关注点: 不同数据结构在序列化/反序列化时可能产生不同的错误码。例如,对Hash类型执行String命令会返回 WRONGTYPE 错误,契约测试中需针对此类操作断言特定错误码。

3.2 核心命令分类与错误码映射

Redis命令按功能分为五类,每类命令在契约测试中对应不同的错误码断言策略:

  • 键管理命令: DEL、EXISTS、EXPIRE、TTL — 错误码多为 WRONGTYPE(类型不匹配)或 NOSCRIPT(脚本不存在)。
  • 字符串命令: GET、SET、INCR、MGET — 常见错误码 WRONGTYPE、OOM(内存不足)。
  • 哈希命令: HGET、HSET、HGETALL — 错误码 WRONGTYPE、NOSCRIPT。
  • 集合命令: SADD、SMEMBERS、SINTER — 错误码 WRONGTYPE、BUSYGROUP(流消费组冲突)。
  • 有序集合命令: ZADD、ZRANGE、ZREVRANK — 错误码 WRONGTYPE、MIN/MAX 参数格式错误。
# 契约测试中典型的错误码断言示例(伪代码)
# 对Hash类型执行List命令,期望返回WRONGTYPE
> LPUSH doc:123 "value"
(error) WRONGTYPE Operation against a key holding the wrong kind of value

# 内存超限时写入失败
> SET cache:key "large_value"
(error) OOM command not allowed when used memory > 'maxmemory'

3.3 连接方式与通信协议

Redis支持三种连接方式,每种方式在契约测试中影响错误码的传输格式:

连接方式 协议 端口 错误码传输特点
单机TCP RESP2 / RESP3 6379 错误码以 -ERR 前缀行内返回
Sentinel高可用 RESP2 + 哨兵协议 26379 主从切换时返回 READONLY 或 MASTERDOWN
Cluster集群 RESP2 + 集群协议 6379+ 槽位错误 MOVED / ASK 重定向
契约测试关键点: 在Cluster模式下,MOVED 和 ASK 错误码是正常重定向信号,而非异常。契约测试需区分“业务错误码”与“协议重定向码”,避免误判。

3.4 错误码分类体系

Redis错误码按来源分为六大类,契约测试中需针对每类设计不同的断言逻辑:

  1. 语法错误(-ERR): 命令格式错误、参数数量不对。例如 ERR wrong number of arguments for 'get' command。
  2. 类型错误(-WRONGTYPE): 对错误的数据结构执行操作。搜索服务中常见于误用Hash/List命令。
  3. 权限/认证错误(-NOAUTH / -NOPERM): 未认证或ACL权限不足。契约测试需模拟认证失败场景。
  4. 资源限制错误(-OOM / -LOADING): 内存超限或持久化加载中。搜索服务高并发时易触发。
  5. 集群重定向错误(-MOVED / -ASK): 槽位迁移或重分配。契约测试需验证客户端重试逻辑。
  6. 主从错误(-READONLY / -MASTERDOWN): 从节点只读或主节点宕机。搜索服务读多写少场景需覆盖。
常见陷阱: 契约测试中容易将 MOVED 错误码当作异常处理,导致测试误报。正确做法是:在Cluster模式下,MOVED 应触发客户端自动重定向,契约测试应断言重定向后的正确响应,而非直接断言错误。

3.5 Redis通信协议与错误码传输机制

下图展示了RESP2协议下错误码的传输链路,以及契约测试中如何捕获并断言错误码:

Redis错误码传输与契约测试断言链路 Redis错误码传输链路与契约测试断言 搜索服务客户端 连接池 / 集群管理器 RESP2 / RESP3 协议 序列化 / 反序列化 Redis 服务端 命令执行 & 错误生成 错误码分类 -ERR / -WRONGTYPE -MOVED / -ASK -OOM / -READONLY 契约测试断言层 错误码匹配 / 重定向验证 / 超时断言 说明: 契约测试在断言层捕获Redis返回的错误码,区分协议级错误(MOVED/ASK)与业务级错误(WRONGTYPE/OOM)。 对于集群重定向错误,契约测试应验证客户端自动重试逻辑,而非直接断言错误。

3.6 契约测试中的错误码断言策略

基于上述分类,在搜索服务的契约测试中,建议采用以下断言策略:

  • 精确匹配: 对 WRONGTYPE、OOM 等确定性错误码,使用字符串精确匹配。
  • 前缀匹配: 对 ERR 类错误(如参数错误),使用前缀匹配 -ERR 并验证错误描述中的关键词。
  • 重定向忽略: 在Cluster模式下,契约测试应忽略 MOVED 和 ASK 错误码,转而验证客户端是否成功重试并返回正确结果。
  • 超时与连接错误: 网络层面的超时错误(如 ETIMEDOUT)不属于Redis错误码,契约测试需单独设计超时断言逻辑。
// 契约测试中错误码断言示例(Java + Jedis)
@Test
void testWrongTypeError() {
    try {
        jedis.lpush("hashKey", "value");  // 对Hash类型执行List命令
        fail("Expected WRONGTYPE error");
    } catch (JedisDataException e) {
        assertTrue(e.getMessage().startsWith("WRONGTYPE"));
        assertEquals("Operation against a key holding the wrong kind of value",
                     e.getMessage().substring(10));
    }
}

@Test
void testClusterRedirect() {
    // 契约测试不直接断言MOVED,而是验证重试后的结果
    String result = clusterJedis.get("key_on_another_slot");
    assertNotNull(result);
    assertEquals("expected_value", result);
}
最佳实践: 在契约测试中,建议将Redis错误码断言封装为独立的断言工具类,支持正则匹配、前缀匹配和精确匹配三种模式。这样当Redis版本升级导致错误描述微调时,只需修改工具类,无需逐个调整测试用例。