背景
IM中,拉取会话列表的接口,聚合了人的头像,群的头像,最新一条消息,免打扰等各种配置,在某些特殊场景下,最新一条消息体会非常大,导致在拉500个会话列表的时候,返回数据量达到了10MB+
优化思路
jaeger查看整体耗时
找到大的耗时模式,逐个优化
具体思路
1.检查sql索引,一般sql执行时间在50ms左右,超过100ms就可能有问题了
2.查看机器负载,如果机器cpu,内存,磁盘,带宽某一个到达瓶颈了,就不是代码能够解决的
3.在机器资源充足的情况下,细化到代码层面,可以增加日志,打印出某个方法的执行时间,甚至,打印出某一行的执行时间,来看具体耗时在哪
4.如果确实数据量太大了,可以考虑传输pb序列化后的结果,对于key多的场景,pb能比json节省30%带宽
5.pb序列化以后还不满足,可以考虑用gzip做压缩
6.如果压缩还不行,就要从设计方面考虑了,一次返回这么多数据是否合理,是否所有返回的数据都是必要的
7.合理的分页
8.不互相依赖的操作,并行处理
9.数据量大的耗时操作,分批并行处理
经验
1.会话场景,返回的会话信息,用pb序列化以后,节省了30%的带宽
2.会话列表内的最新一条消息,如果很长,客户端本身也展示不完整,所以,可以对消息做一个摘要,没必要返回很长的消息
3.查询群成员数量的sql,count没有走覆盖索引,回表以后耗时700ms,修改索引以后,耗时70ms,性能提升10倍,合理的设计索引是mysql很重要的一部分
3.经过实际测试,一个线程创建1000个对象和10个线程,每个线程创建100个对象耗时差距不大【在内存不吃紧的情况下,创建对象和申请内存不是耗时操作】
4.打日志的时候,sprintf,%+v这种操作,如果结构体本身的数据量很大,建议重点排查,而且,日志打印应该做成异步的,可以利用线程池来做
错误的猜想
1.会话列表一页500个会话,聚合模块耗时400ms,一开始怀疑循环创建新的会话结构体并赋值的过程导致耗时很长,后面打了日志以后,发现耗时不到1ms,经过排查发现,有一行日志把整个响应给打出来了,包括会话信息,消息体本身,而且是同步打印的,这一行日志打印耗时300ms
2.pb的序列化,相比于json,只能压缩key的空间,所以,实际的压缩消息,取决于具体的业务场景,key小的业务场景压缩效果就不明显,比如消息。
教训
不要盲目猜想,打印关键步骤的耗时日志,可以快速的定位瓶颈代码,在机器负载不高的时候,看pprof意义不大,pprof本身是降低程序损耗的,在机器资源够的情况下,要降低接口耗时,还是得打印耗时日志逐步分析