这两年有幸参与了商品列表系统的两次黑五挑战,QPS从最开始的1k都顶不住,到1w,再到2w,加上支持大量的AB实验,排序千人千面,功能越来越复杂,系统越来越庞大,可能这样的机会都不会常有,在此简单记录一下做系统优化的实践心得。

要素

仔细想想,做优化其实并没有很多门道,个人总结起来就三个要素:

  1. 业务
  2. 工具

业务

业务就是你的任务,是优化的目标,业务的复杂性和独特性决定了你的问题只有自己去解决,网上没有答案,即使挖个大神来也不能立马帮到你。

业务不仅仅包括公司对外提供的服务,还包括项目内部的一切细枝末节,比如代码的逻辑、服务器的部署、后台的配置、数据的流转,甚至不同团队间的分工合作等等。

我们要做的其实就是优化这些业务,业务中的任何一个点都可以是优化的方向。

工具

我们能用到的一切手段都是工具,有直接应对线上流量的服务器、代码、JVM、DB,也有间接可以帮助我们做优化的辅助工具,监控(Prometheus/Grafana)、日志(ELK)、压测(Goreplay)、诊断工具(Arthas)等等。

只了解业务的人面对问题束手无策,只钻研技术的人能解决的问题往往被技术限制了天花板。

我们需要的是精通业务,善用工具的人,能从监控数据中发现蛛丝马迹,能从复杂的业务关系中抽丝剥茧,能利用手上一切工具,发挥它们最大的作用。

步骤

抛开搭建环境、压测、回归测试等工作,优化步骤最精简的话只有两步:

  1. 找系统瓶颈
  2. 优化,突破瓶颈

找系统瓶颈

找瓶颈就需要有依据,依据就是监控指标。

监控指标的全面对于一个庞大的系统来说至关重要,不仅包括接口的耗时、JVM的状态、机器的负载等一些常见的指标,更需要细致到收集缓存的命中率、不同逻辑分支的占比、每张表的读写频率等具体业务相关的指标。

如果缺少了业务指标的监控,定位问题很可能定位不到根源,能优化的空间也会受到技术的限制。

优化

找到瓶颈之后,看起来问题很快就能解决了,JVM顶不住就调heap,调GC,DB顶不住就加副本,优化sql,毕竟我们学习的时候学的就是这些,面试的时候考的也是这些。

然而真正漫长而痛苦的优化过程中,这些直接了当的解决方式往往不会带来多少提升,因为简单的方法可能在我们当初开发功能时都已经做过。

通常想要成倍的提高吞吐量,我们需要做更多看似侧面的工作,解决根源上的问题

几个例子

当我们吞吐量在1k不到时,JVM顶不住,这时候不管是升机器配置,还是JVM调优都没有明显效果。因为我们有很多慢接口,即使只占请求总量的1%不到,在大流量下也是拖垮服务的重要因素。我们采用了使用ES的一些高级特性,同时将数据准备成便于查询的结构(见ElasticSearch在高并发复杂查询业务场景的应用)等一系列措施,消灭了慢查询接口。

当慢接口处理完,吞吐量提升到了一定程度,JVM还是状况不佳,我们就考虑改善缓存。原本使用的JVM内缓存,尝试了调整缓存参数,使用集中式缓存Redis等方案后都没效果。最终通过Nginx层的缓存和一致性哈希大幅减轻了压力。

当接口都很快,DB(Elasticsearch)开始顶不住,我们尝试了加机器、扩副本、调堆内存大小等手段,最终还是通过将商品详情的查询移出ES,由单独的服务通过查Redis来提供,减轻了ES大半的压力。

。。。

以上的手段未必适合其它项目,但系统的优化就是这样,从来就没有标准答案。

我们能做的就只有深入业务,利用好每一种工具,然后充满信心地迎接下一个挑战。