在电商平台中,用户访问商品店铺时,常常会面临复杂的商品列表展示需求,例如:按照价格、销量、好评度、上架时间等维度排序,还需要支持分页查询。这类查询在面对高并发访问时,数据库负载大、响应慢的问题就会暴露出来。为了解决这些痛点,本文将分享一种基于 Redis 的优化方案,借助 ZSet(有序集合)实现高性能分页排序查询,极大缓解数据库压力。
以京东商城为例,我们来逐步还原整个解决方案。
一、业务场景与性能瓶颈
在电商系统中,每个店铺都有一个商品列表页面,用户在浏览商品时常常会进行如下操作:
- 按照上架时间排序(从近到远或从远到近)
- 按照好评度排序
- 按照价格排序(升序或降序)
- 按照销量排序
- 分页查看第 N 页数据(例如每页 10 条)
这些操作表面上是简单的前端分页与排序,实际上后端需要频繁执行复杂的 SQL,如 ORDER BY + LIMIT,而且还得支持多维度排序。传统的数据库方案会遇到明显瓶颈:
- 排序字段不同,SQL 需要动态拼接或建立多个复合索引,维护复杂;
- LIMIT OFFSET 分页会导致大量数据扫描;
- 高并发下数据库容易成为性能瓶颈。
为此,引入 Redis 进行缓存与索引优化,成为一个务实且高效的方案。
二、整体解决方案概述
该方案的核心目标是:把复杂的分页排序逻辑迁移到 Redis 中执行,并减少对数据库的频繁访问。
主要分为两层:
整个过程通过 RPC 组件(或 Redis Proxy)实现分布式集群读写分离、负载均衡与数据并行提取,极大地提升了性能。
三、Redis 数据结构设计
1. 使用 ZSet 构建多维索引
在 Redis 中使用 ZSet(Sorted Set) 实现商品 ID 的排序索引:
- 每个 ZSet 表示一个“店铺+排序维度”的组合。
- ZSet 的 score 字段表示某个维度的排序值,如价格、好评率、上架时间等。
- ZSet 的 value 是商品的唯一 ID。
例如,店铺编号为 8182,则构建如下 ZSet 索引:
8182_utime | 按上架时间升序 |
8182_eva | 按好评度升序 |
8182_price | 按价格升序 |
8182_sales | 按销量升序 |
8182_price_desc | 按价格降序(使用 ZREVRANGE) |
通过预先计算好的 score 值,商品列表数据可以按需排序,Redis 自动维护排序顺序,查询效率极高。
2. score 值计算策略
Redis 的 ZSet 会按照 score 从小到大排序,因此:
- 上架时间:时间越近,score 越小 → 放前面
- 好评率:值越高,score 越小(或反转处理)
- 价格:原始价格作为 score 即可
- 销量:销量大,score 小或降序展示用 ZREVRANGE
重要提醒:score 值要归一化为数值类型(long、double 等),避免使用 timestamp 字符串或复杂格式。
四、分页查询过程
示例场景:
用户访问商家 8189 商品页面,选择“按价格升序”,请求第 2 页(每页 10 条),则执行如下命令:
ZRANGE 8189_price 10 19
含义:
- 获取 8189_price 这个 ZSet 中第 10~19 位的商品 ID(注意 Redis 是左闭右闭)。
- 不涉及字段排序,不需要数据库干预。
如果是价格降序,则使用:
ZREVRANGE 8189_price 10 19
五、商品数据的批量提取:MGET
分页查询得到的是商品 ID 列表,接下来要获取商品的详情数据。
组织商品详情 Key
Redis 中商品详情数据以字符串形式存储,Key 命名规范为:
p_p_<商品ID>
例如:p_p_10123
假设我们得到了如下商品 ID:
10123, 10125, 10130
则构建如下 Redis 批量查询命令:
MGET p_p_10123 p_p_10125 p_p_10130
这个操作由 RPC 层代理并并行调度给多个分片 Redis 节点完成:
- 支持并发请求,分摊到多个 Redis 实例
- 所有操作基于内存,极高性能
- 返回结果自动聚合还原顺序
商品详情通常为 JSON 字符串,前端拿到结果后再反序列化展示。
六、关于 Redis 优化的关键问题解析
1. 会不会出现 Big Key(大 Key)问题?
一般不会:
- 单个店铺商品量有限,通常几百件
- 即使是旗舰店,大多数用户只浏览前几百件商品
- 可设置最大上限,如前 1000 条商品做排序,其余忽略或另做分段存储
如确实遇到万级商品,建议使用分页截断 + 多个 ZSet 分段存储策略解决。
2. Redis 的内存占用情况如何?
- 每个 ZSet 的 score 和 ID 是整数,占用非常小(通常 < 8 字节)
- 商品 JSON 数据量不应过大,建议只缓存必要字段(如标题、图片、价格、评分等)
3. 内存不足怎么办?
策略如下:
- 索引数据(ZSet) 放在 Redis 0 号库,设置永不过期
- 商品详情数据(JSON) 放在 Redis 1 号库,设置 TTL = 1 天
- 清除缓存时,只清理 1 号库数据,保障索引稳定性
- 程序中根据业务需要配置多数据源访问策略
4. 是否需要提前在 MySQL 中排序?
不需要。Redis 的 ZSet 就是天然的排序结构,只要提前算好 score 即可,不需要额外在 MySQL 中做 ORDER BY 操作。
七、Redis 数据同步方案
为了保持 Redis 与数据库一致性,需要定期或实时同步更新:
方案一:定时任务同步
- 每分钟扫描最近新增/修改/删除的商品
- 更新对应的 ZSet 和 JSON 数据
方案二:使用 Flink CDC 实时同步
- 监听 MySQL 的 Binlog 日志
- 实时将变更推送到 Redis,适用于高一致性、高实时场景
八、RCP 组件是否存在单点问题?
是的,RPC 作为 Redis 访问代理如果是单点架构,会存在性能瓶颈与可用性问题。
解决方案:
- 使用 F5 负载均衡或云厂商 SLB(如阿里云 SLB、华为云 ELB)对多个 RPC 实例做负载均衡
- 客户端统一通过 F5 地址访问,后端多个 RPC 实例并发处理请求
- 高可用设计保障系统整体稳定性与可扩展性
总结
本方案通过 Redis 的 ZSet 索引与 MGET 批量读取机制,完美解决了电商平台中商品列表分页排序场景下的性能瓶颈问题。核心优势如下:
- 高效的多维排序能力(升序、降序)
- 支持高并发下的分页查询
- 数据从 Redis 读取,全程内存操作,性能极高
- 有效降低数据库压力
- 灵活的缓存结构设计,保障索引稳定性
这类优化思路不仅适用于电商平台,还可以推广到内容推荐、搜索结果排序等场景,是 Redis 应用中的典范案例。
评论前必须登录!
注册