Redis不支持SQL式GROUP BY或JOIN,仅适用于预聚合、分片缓存、实时计数、状态快照四类场景;需业务提前拆解逻辑、结果可收敛、更新可原子化,并重视key生命周期管理。

Redis 本身不支持 SQL 式的 GROUP BY 或嵌套 JOIN,所以“复杂业务数据聚合”不能靠 Redis 自身计算完成。它真正能做的,是预聚合、分片缓存、实时计数、状态快照这四类事——前提是业务逻辑能提前拆解、结果可收敛、更新可原子化。
下面直接说怎么做、为什么这么选、以及最容易翻车的地方。
用 Hash + Pipeline 批量写入多维指标,避免 N 次网络往返
比如统计「每个商品在每个城市的日销量 + 库存余量 + 用户评分均值」,不要为每个字段单独 SET 三次,也不要循环调用 redisTemplate.opsForHash().put()。
- 把同一商品的所有维度塞进一个
Hash,key 设为sale:stat:20260713:sku_123,field 是city_shanghai、stock、rating_avg等 - 用
redisTemplate.executePipelined()批量提交,一次网络请求完成全部写入 - 注意:Pipeline 不保证原子性,但能压降延迟;若需强一致性,改用 Lua 脚本封装整个写入逻辑
用 Sorted Set 实现带权重的实时排行榜,别硬算 TOP-N
例如「按点击量+收藏量+分享量加权排序的热榜」,每次用户行为都触发重算会拖垮服务。
- 给每个 item 分配一个 score:比如
score = clicks * 10 + favorites * 5 + shares * 20,用ZADD更新 - 前端查榜直接
ZREVRANGE sale:hot:20260713 0 49 WITHSCORES,毫秒级返回 - 陷阱:score 必须是 double 类型,整数溢出或精度丢失会导致排序错乱;建议统一放大 100 倍后转 long 再存为 double
- 别在应用层用
ZRANGE拿全量再 Java 排序——Redis 的ZRANK/ZSCORE已足够支撑分页和单条查询
用 String + 过期时间做轻量级聚合快照,替代定时任务扫库
比如「每小时汇总订单金额,用于监控看板」,不用每小时拉 MySQL 聚合一次再写回 Redis。
- 订单创建时,用
INCRBYFLOAT order:hourly:2026071319 129.99累加(key 按小时滚动) - 同时设
EXPIRE order:hourly:2026071319 3600,过期自动清理,不占永久空间 - 如果需要保留历史,用
RENAME+EXPIRE组合:先RENAME order:hourly:2026071319 order:hourly:2026071319:archived,再设长过期 - 注意:
INCRBYFLOAT在 Redis 2.6+ 才支持,低版本只能用 Lua 脚本模拟
聚合结果跨服务共享时,必须统一序列化协议
当多个 Spring Boot 服务(比如订单服务、营销服务)都要读写同一个聚合 key,RedisTemplate 默认的 JDK 序列化会互相看不懂。
- 强制所有服务使用
StringRedisTemplate处理字符串型聚合值,或统一配置Jackson2JsonRedisSerializer - 避免混用
RedisTemplate<string object></string>和RedisTemplate<string string></string>,哪怕 value 看似是字符串,JDK 序列化也会包一层java.lang.Stringheader - 检查
redis-cli直连后GET出来的值是否可 human-readable;不可读,说明序列化不一致
聚合逻辑越靠近数据写入点,就越可控。把「什么时候聚合」「聚合哪些字段」「怎么处理冲突」这些决策留在业务代码里,而不是指望 Redis 替你做复杂计算。真正容易被忽略的是:聚合 key 的生命周期管理——没人删的 key 会堆积成内存黑洞,尤其是用时间戳拼接的 key,必须配套清理策略。


















