从程序员到架构师:大数据量、缓存、高并发、微服务、多团队协同等核心场景实战
上QQ阅读APP看书,第一时间看更新

4.1 业务场景:如何将十几秒的查询请求优化成毫秒级

这次项目针对的系统是一个电商系统。每个电商系统都有个商品详情页。一开始这个页面很简单,只包括商品的图片、介绍、规格、评价等。

刚开始,这个页面打开很快,系统运行平稳可靠。

后来,页面中加了商品推荐,即在商品详情页后面显示一些推荐商品的列表。

再后来,页面中加入了最近成交情况,即显示一下某人在什么时候下单了。

接着,页面中又加入了优惠活动,即显示这个商品都可以参加哪些优惠活动。

……

当时这个系统里面有5万多条商品数据,数据量并不大,但是每次用户浏览商品详情页时都需要几十条SQL语句,经常出现十几秒才能打开详情页的情况。

这样的用户体验当然不好。公司有个第三方监控工具,从国内各地监控系统几个关键路径的性能。其中一个关键路径是从首页到搜索再到商品详情页的时长,这个平均时长从刚开始的3.61秒逐渐变成后来的15.53秒,监控工具成为“摆设”,实际不再使用。

之前也有人提过商品详情页需要优化,不过另一个人说,打开App进入首页前广告都要播放10秒,用户还在乎这个商品详情页打开慢吗?当然,没有马上优化的原因,是后面不断有其他业务需求。

后来这个优化项目提上了日程,项目组开始考虑要怎么优化。重构数据库基本不可能,最好不要改动表结构。大家想到的方案也很通用,就是把大部分商品的详情数据缓存起来,少部分的数据通过异步加载。比如,最近的成交数据通过异步加载,即用户打开商品详情页以后,再在后台加载最近的成交数据,并显示给用户。不过这一章主要讲缓存,所以异步加载的方案就不在此展开了。

关于缓存,最简单的实现方法就是使用本地缓存,即把商品详情数据放在JVM里面。在Google Guava中有一个内存缓存模块,它把所有商品的ID与商品详情信息一对一缓存至JVM内存中,用户获取商品详情数据时,系统会根据商品ID直接从缓存中读取数据,能大大提升用户页面的访问速度。

不过,通过简单换算后发现这个方法明显不合理。先来举个例子。

一条商品数据中往往包含品牌、分类、参数、规格、服务、描述等字段,仅存储这些商品数据就要占用500KB左右的内存,再将这些数据缓存到本地的话,就要占用500KB×50000≈25GB内存。此时,假设商品服务有30个服务器节点,仅缓存商品数据就需要额外准备750GB的内存空间,这种方法显然不可取。

为此,项目组决定使用另外一个解决办法——分布式缓存,先将所有的缓存数据集中存储在同一个地方,而非重复保存到各个服务器节点中,然后所有的服务器节点都从这个地方读取数据,如图4-1所示。

• 图4-1 分布式缓存示意图

那么这个统一存储缓存数据的地方需要使用什么技术呢?这就涉及接下来要讲的缓存中间件技术选型问题了。