百万商品大数据下的类目树优化实战经验分享



大家好,我是小米,今年 31 岁,一个喜欢写代码、讲故事、分享技术的程序员。最近我们商城要做一个大升级:在商品列表的左侧,加一个 商品类目树。

说起来,可能你觉得类目树很常见,淘宝、京东、拼多多都有,点开左侧的"手机数码"、"服饰鞋包"、"家用电器",右边商品就会动态刷新。但真正自己去做的时候,才发现这背后其实是个大坑:

  • 商品数据量大,几百万级别;
  • 类目树要展示每个节点下的商品数量,还要能实时变化;
  • 用户在筛选条件下(比如品牌、价格区间)点击类目,商品数也要立刻跟着刷新。

一句话:既要快,又要准,还要实时!

于是,我们决定用 Spring + Elasticsearch 的聚合 来搞定。接下来,我就带大家走一遍我们的踩坑之旅和最终的技术方案。

最初的想法:MySQL + group by

一开始,需求刚提出来,我们的直觉是:

既然要统计类目下的商品数,那用 MySQL group by 不就行了?

比如下面这条 SQL:

乍一看没问题,但当我们实际跑到线上时,MySQL 就直接跪了:

  • 我们的商品表有几百万条数据,每次查询要扫全表;
  • 用户还可能加上品牌、价格区间、关键字等条件;
  • 一旦有多个并发请求,数据库直接爆炸。

于是,方案一票否决。

为什么是 Elasticsearch

我们换个思路。既然要做实时统计和聚合,那 Elasticsearch 正好是擅长的领域:

  • 倒排索引:查询速度快;
  • 聚合(Aggregation) :专门干统计分析的活;
  • 水平扩展:数据量大也能 hold 住。

所以,最终我们决定用 Spring Boot + Elasticsearch 来做。

类目树的数据结构

在讲技术实现之前,先聊聊类目树的数据长什么样。

比如我们的商城类目结构:

  • 手机数码
    • 手机通讯
      • 智能手机
      • 老年机
    • 笔记本电脑
  • 家用电器
    • 冰箱
    • 洗衣机

每个节点都要展示:

  • 类目名称
  • 商品数量(动态计算)

所以,我们需要在 Elasticsearch 里把商品的类目信息存进去。一般存法是:

这里的 category_path 非常关键,它表示商品所在的完整类目路径。这样我们才能按父级类目统计商品数。

Elasticsearch 的聚合查询

接下来就是重头戏:如何统计类目下的商品数。

我们用的是 Terms Aggregation。比如,统计一级类目的商品数,可以这样写:

这个查询的意思是:按 category_path 聚合,统计每个类目下的商品数量。

如果要结合搜索条件,比如用户搜了"iPhone",就可以直接加上:

这样,返回结果里就是:

  • 各个类目下 iPhone 的数量;
  • 用户再点选类目时,右侧商品列表和左侧数量能保持一致。

Spring Boot 集成 Elasticsearch

聊完了原理,我们来点代码。我们用的是 Spring Data Elasticsearch。

1. 配置依赖

2. 定义实体类

3. 编写聚合查询

这样,我们就能实时拿到每个类目下的商品数了。

性能优化

当然,实际做的时候我们也遇到不少问题。

1. 类目层级太深

如果类目有三级甚至四级,直接聚合会很慢。我们用了 子聚合(Sub Aggregation) 来做。

这样可以逐级展开,性能会好很多。

2. 热点类目查询太频繁

比如"手机数码"点击太多,我们加了一层 缓存:

  • 短期内同样的查询直接走 Redis;
  • 后台有定时任务刷新数据。

3. 分片 & 副本优化

Elasticsearch 的分片数我们按数据量调优,确保查询分布均匀,避免单点热点。

上线后的效果

类目树功能上线后,用户体验大大提升:

  • 点开"手机数码",能秒级展示数量;
  • 搜索条件变化时,左侧数量实时刷新;
  • 后端 QPS 从原来的几百压到几千,Elasticsearch 扛住了。

而且,最让我自豪的是,之前大家觉得"类目树就是个 UI 样式",现在都觉得背后是门学问,哈哈。

总结

这次的类目树项目,我学到几点:

  • 需求再小,背后都有大学问。一个小小的类目树,其实牵扯到搜索、聚合、性能优化。
  • 选对技术栈很重要。如果我们还死磕 MySQL,估计项目早黄了。
  • 实战是最好的学习。写博客讲出来,也算是对自己知识的一次梳理。

如果你们的项目里,也有类似的 "实时统计"、"大数据聚合" 需求,Elasticsearch 一定要学会。

END

以上就是小米今天的分享。希望能帮到你们,如果你们在做类目树或者 Elasticsearch 聚合时遇到坑,欢迎留言交流!

我是小米,一个喜欢分享技术的31岁程序员。如果你喜欢我的文章,欢迎关注我的微信公众号"软件求生",获取更多技术干货!

相关推荐
如意猴8 小时前
【C++】007 C/C++ 内存管理机制、malloc与new的区别及模板初阶
java·c++·算法
可乐鸡翅yeah_8 小时前
HLS 分片过期清理,直播旧 TS 分片磁盘爆满问题处理
java·后端·spring·m3u8·m3u8在线·音视频在线播放
张彦峰ZYF9 小时前
加了锁不等于扛得住争用:synchronized、显式锁与 CAS 的语义分层、争用代价账本与选型判据
后端·同步设施·内置锁的四种状态与单向升级·可重入的由来与四条硬边界·显式锁的状态字段与等待队列·读写锁与邮戳锁·一次同步的开销账本
专业程序开发源9 小时前
SSM校园拍摄交流服务平台36936-计算机课程设计、毕业设计
java·spring boot·后端·python·elasticsearch·php·课程设计
Gopher_HBo9 小时前
zap采样器与性能优化内幕
后端
变量探索SEQVEC9 小时前
我埋了 8 个假文件,看谁会上钩:12 天 502 次扫描实录
后端
用户094248568039 小时前
第23章:OpenJDK逃逸分析、标量替换与锁优化
java·jvm
llqbzllll9 小时前
HashMap 的核心结构:从一次 put 看到扩容、桶迁移与树化边界
后端
wuminyu9 小时前
LockStack在虚拟线程Mount和Unmount拷贝过程剖析
java·linux·c语言·jvm·c++
晚安code9 小时前
设计模式入门:吃透 SOLID 原则与迪米特法则,再学 5 个高频模式
后端·设计模式