前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【2018手Q春节红包系列】春节排行榜性能优化小记

【2018手Q春节红包系列】春节排行榜性能优化小记

原创
作者头像
叫你不戴帽子
修改2018-08-19 10:26:53
1.1K1
修改2018-08-19 10:26:53
举报

优化后QPS如下图:

春节初一~初三期间,红包活动页右上角会有春节排行榜的入口。

排行榜页面前期预估峰值:15w/s,页面逻辑展示如下:

此次优化前后,排行榜server单机QPS对比如下(备注:以下数据,均假设从CKV中拿到数据,忽略掉oidb相关逻辑):

测试条件:CPU占比不超过85%,超时时间不超过900ms

优化前单机QPS

优化后单机QPS

获取排行榜首页数据(实时拉取好友步数并进行排序)

2200

5200 ( +3000)

获取排行榜分页数据(从Redis快照中直接获取数据)

3000

5500 ( +2500)

用户点赞

12000

12000

发送C2C消息

12000

12000

针对春节排行榜运动侧准备了120台V8机器。以排行榜首页为例,优化前,系统QPS(极限值) = 2200*120 = 26.4w/s,优化后,系统QPS(极限值) = 5200*120 = 62.4w/s提升136.4%。

以CPU占比75%平稳运行而言,系统QPS = 4700*120 = 56.4w/s,此时平均每个请求从发包到收包耗时约40ms

所有接口中,由于排行榜首页,做的逻辑更多更复杂,因此,不出意外,首页QPS是最低的。另外,春节期间排行榜所有请求中,首页的请求应该是最多的,毕竟,用户进入页面首先就会请求首页数据。

综上所述,优化排行榜首页性能成为了整个系统的关键所在。这次优化,也是根据这条思路进行的,下面简要介绍下优化的思路和过程。

一、排行榜逻辑架构图

考虑到公司安全规范,架构图隐去。

要点如下:

1. 存储:主要采用CKV,高性能Kv存储。

2. 外部接口:能异步就异步(除oidb查会员标记位外)

3. 框架:SPP微线程,相关网络操作均采用异步。

4. 备注:SSO寻址走hash一致性寻址,server本地采用Redis做快照,防止排名错乱的问题。

二、压测数据

工欲善其事,必先利其器。这里不得不提到test.server.com这个压测利器,文中所有数据和结果均来自test.server.com提供的压测客户端。有兴趣的同学参见此文

优化的步骤无外乎:

A. 压测得到当前Server性能(CPU不超过80%,延迟不超过900ms)

B. Perf查看CPU消耗点在哪里(因为排行榜这里是CPU Bound型)

C. 针对B的结果相应优化,再重复进行步骤A。

1. 原始压测数据(未做优化前):压测到2200/s时,CPU占比已经飙升至80%。

此时,Perf出来的火焰图如下(图为搜索JSON匹配后的结果):

分析后得知,Server中的Json和map的相关操作吃了很大一部分CPU,约占用30%左右。

2. 优化map操作,将代码中所有涉及到map的逻辑全部替换为hash_map和vector。思路:map底层采用RB_Tree的方式实现,查找复杂度为OLog(n);hash_map底层采用Hash_table实现,查找复杂度为O(1)。

优化前QPS

优化后QPS

优化措施

2200

3200 (+1000)

hash_map和vector替换map操作

备注:C99里面的hash_map,不是标准库,是gcc实现的:__gnu_cxx::hash_map。这里性能提升明显,主要是获取步数数据、用户关系链数据及相互匹配时,查找操作较多。

优化后Perf图如下(搜索关键字<map>如下图,CPU占比11%左右):

右图中,之所以map匹配CPU占比超过11%,因为Json相关的操作中大量的使用了map,而Json还没有进行优化。搜索hash_map,CPU占比只有5%左右

3. 优化Json操作。思路:将Json库替换为简单字符串拼接(这里由于是和前端交互,限定了只能使用json交互,因此优化的思路还是从组装json方面考虑,而不是替换为二进制协议,例如pb等)。

优化前QPS

优化后QPS

优化措施

3200

4000 ( +800)

json组装,改为自己拼接字符串

优化后Perf图如右下(搜索关键字<ssdasn::>后如下图):

右图中,可以看到,优化完成后,占用CPU最多的已经变为ssdasn::的相关操作,这些操作是CKV存储的编解码封装,也就是说,后续的性能优化已经和业务无关了。

此时CPU使用如下,占比约80%左右:

4. 减少日志流水操作。思路:忽略正常处理的请求,只打印出错请求处理日志。

优化前QPS

优化后QPS

优化措施

4000

4500 ( +500)

减少日志打印,只打印出错日志

优化前后perf图对比如下:

左图为优化前,日志流水操作占比: 2.08%;右图为优化后,正常情况下日志流水操作占比0%(只有出错才打)。留出了更多的CPU时间给业务逻辑,优化后QPS如下:

5. gcc编译增加O2选项。思路:利用编译器自身的编译优化选项,从编译执行的底层尝试优化性能(业务无关)

优化前QPS

优化后QPS

优化措施

4500

5200 ( +700)

使用gcc O2优化选项

优化后QPS如下图:

此时,业务server等待队列耗时如下,可以看到,请求基本都在server收到包后1ms内开始被处理,不会堆积:

CPU占比如下,约占比85%左右:

三、走过的弯路

在初步压测出单机QPS=2200后,由于缺乏经验,没有从火焰图去分析CPU到底消耗在哪里。因此只能尝试各种经验上的方法进行优化,例如:

1. 去掉同步快照操作(伤害用户体验)

2. 去掉关系链CKV同步操作(更换成本高)

3. 调整批量获取CKV的数量(经验值)

4. 调大进程数

5. 关闭系统日志

6. 关闭排序(排行榜基础功能)

等...

这些效果都不明显,提升作用有效,最多的时候,有损体验的前提下,QPS才提升到2500左右。

后面在查阅相关资料后,系统化的使用perf、火焰图等工具进行分析,抓到性能瓶颈后,有的放矢,才能在后面的优化过程中,有效的提升系统QPS。

这次优化,从接触学习压测工具开始,到昨天优化告一段落,断断续续持续了有3、4天左右。感谢团长、双太、drog、温总在优化过程中提供的帮助和建议!!!


 参考资料

gcc 编译优化选项O2相关知识,可以参考这篇文章GCC中-O1 -O2 -O3 优化的原理

另外,O2选项优化打开时,注意另外一个选项:-fno-strict-aliasing,如果没有配合使用的话,程序可能产生未定义的行为,大意是说O2选项默认认为不同类型的指针不能指向同一片内存区,如果业务代码中,有强制类型转换,需要注意下这里。具体参考:gcc 编译参数 -fno-strict-aliasing

官方说明在此:Options That Control Optimization

我的理解:O2性能优化选项打开前后代码语义必然相同,最主要的性能优化点,可能还是:未打开前,Gcc编译生成的代码是独立的,每一行代码都可以打断,方便调试;打开后,Gcc编译生成的代码是相关的,并根据一些相关性进行了优化,当然这时候,调试的难度就很大了。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
云数据库 Redis
腾讯云数据库 Redis(TencentDB for Redis)是腾讯云打造的兼容 Redis 协议的缓存和存储服务。丰富的数据结构能帮助您完成不同类型的业务场景开发。支持主从热备,提供自动容灾切换、数据备份、故障迁移、实例监控、在线扩容、数据回档等全套的数据库服务。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档