executePipelined回调必须返回null,因框架强制校验非null即抛InvalidDataAccessApiUsageException;实际结果通过方法返回的List按序获取,不可在回调中返回业务值。

executePipelined 方法必须返回 null
调用 redisTemplate.executePipelined() 时,如果回调中返回非 null 值(比如 return "done"),会触发 ClassCastException 或空指针异常,因为框架内部期望该方法返回 null 来标识“仅执行、不取结果”。实际结果是通过 pipeline 的响应列表隐式返回的。
常见错误写法:return operations.opsForValue().get(key);
这会导致类型不匹配——pipeline 返回的是 List,不是单个值。
- 正确做法:在
SessionCallback或RedisCallback中只做命令注册,末尾写return null; - 若需获取结果,必须从
executePipelined的返回值(List)中按顺序提取,注意null值可能代表命令失败或无返回(如set) - 对
StringRedisTemplate,建议用StringRedisConnection类型转换后再操作,避免字节编码问题
SessionCallback vs RedisCallback:选哪个?
SessionCallback 封装了 RedisOperations,支持高阶 API(如 opsForHash().hGetAll()),写法更贴近业务逻辑;RedisCallback 暴露原生 RedisConnection,能调用底层字节数组命令(如 lPop(key.getBytes())),性能略高但需手动处理序列化。
- 读写字符串、Hash、List 等结构化数据,优先用
SessionCallback,可直接复用redisTemplate配置的序列化器 - 需要精确控制 key/value 编码(比如兼容老系统二进制协议),或操作非 String 类型键(如含中文或特殊字符的 key),用
RedisCallback+StringRedisConnection - 不要混用:在
SessionCallback里调用connection.xxx()会绕过序列化,导致乱码或反序列化失败
批量读取 Hash 数据时的坑
用 pipeline 批量读 HASH 字段(如 hGetAll)时,不能直接传入多个 key——hGetAll 本身只接受一个 hash key。想一次读多个 hash,得用循环注册多个 hGetAll 命令,每个对应一个 key。
错误认知:“hMget 能批量读 field,所以 hGetAll 也能批量读多个 hash”——不对,hMget 是针对单个 hash 的多个 field,而 hGetAll 是针对单个 hash 的全部 field。
- 正确姿势:对每个目标 hash key,调用一次
operations.opsForHash().hGetAll(key) - 返回的
List中,每个元素是Map(未反序列化),需手动转成Map - 若 key 不存在,对应位置返回
null,不是空 Map,需判空
Pipeline 不保证原子性,也不同步事务
executePipelined 只是打包发送命令,不等效于 MULTI/EXEC。哪怕所有命令都进了 pipeline,中间某条失败(如 key 过期、内存满),其余命令照常执行,也不会回滚。
- 需要原子性保障?必须显式用
redisTemplate.execute(new SessionCallback包裹multi()和exec() - Pipeline 失败时,异常不会立即抛出,而是体现在返回
List的某个位置为Exception实例(如RedisSystemException),需遍历检查 - 高并发下,pipeline + 连接池配置不当(如
max-active过小)会导致线程阻塞在getConnection(),比单条还慢
Pipeline 的真正价值不在“能不能用”,而在“什么时候值得用”:当批量操作 QPS > 500 且网络 RT > 1ms 时,收益才明显。本地开发环境测出的 10 倍性能提升,在生产跨机房链路下可能只剩 2–3 倍——别只看 demo 里的 for 循环耗时对比。











