1.应用场景
应用于程序性能优化, 了解熟悉性能优化的本质. |
2.学习/操作
1. 文档
2. 整理输出2.1 背景
2.2 应用之策 - 优化思路
01 明确优化目的
02 定标准,做到什么程度
03 找到瓶颈点
04 着手优化最后才是着手优化。 做优化的时候需要避免两个常见的误区。 第一,不要过度追求应用的单机性能,如果单机表现良好,还应该从整体的角度去思考。 第二,不要过度追求单一维度上的极致优化,比如过度追求 CPU 的性能而忽略了内存方面的瓶颈。 正确的思路一般符合下面两个方向。 第一,空间换性能。 一个节点顶不住就多复制一个节点出来,独一份的数据导致资源竞争得厉害,就多复制一份数据出来。 第二,距离换性能。 数据从服务端经过层层处理返回到客户端觉得慢的话,那么能不能直接保存在客户端,或者至少是离客户端尽可能近的地方。 好了,思路清楚了,具体在做的时候我建议你根据下面小标题的顺序进行。 不管是主动地性能优化,还是被动地排查性能问题都一样。 2.3 应用之策 - 优化思路实现01 应用程序层面不管你愿不愿意承认,现实中的大部分性能问题皆是应用程序自身部分的代码导致的。 我们总是不太愿意承认自己的错误,我见过太多程序员总是习惯性的将问题先归结于硬件问题,网络问题等等,然后最终排查下来的根源往往还是在coding的应用程序上。 所以,我们更应该先从应用程序本身入手进行分析。而且,应用程序所处的位置更「上游」,可操作性更强,让我们可以有更多的手段进行优化。 01 缓存 首先,最常见的便是「缓存」,这是用空间换性能的经典。 数据必然是存储在非易失性的数据库中的,但是一些会被高频访问的数据,将它从数据库中复制一份,存储在易失性的内存上做缓存,可以大大提高被访问的性能。这个道理大家都懂,就不多说了。 但是值得提醒的一点是,缓存数据的数据结构设计很重要,没有一种数据结构是万能的。需要更多的权衡,因为数据结构设计的越简单、单一,缓存数据的二次运算就越多;反之,所有都存储「结果数据」的话,需要冗余的数据量又过大(缓存数据更新还麻烦)。 还得提醒一点,如果缓存的数据量不小,还得考虑增加一个缓存淘汰算法,否则缓存命中率不堪入目,白白浪费大量内存资源。 之前的《分布式系统系列》中有几篇缓存相关的聊了很多细节,可以在文末跳过去查阅。 02 异步 举个现实生活中的例子,如果你在手机上点了一杯奶茶,去店里拿的时候发现前面还有20个号,你会在这干等半小时么? 我想大部分人都不会吧,宁愿去别的地方溜溜。 异步就是通过避免“干等着”来提升性能的手段。 做异步主要是以下两种方式,
03 多线程&分布式 这两点都是「分治」思想的体现。一个快递员送1000个包裹比较慢,那么让10个快递员同时各送100个自然就快了。 但是切勿分的太狠,毕竟,多起一个线程相当于多一个放养的娃,放出去太多的话,管理成本很高,可能反而会更慢。这就是线程切换的成本,分布式系统中也存在类似的管理成本。 不过,一个小建议送给你。不到迫不得已,能通过「单机多线程」应付的,就不要引入分布式了。因为,网络这个东西实在太不靠谱了,你得为它做大量的额外工作。 04 延后运算 这个和缓存的思路相反,将一些运算尽可能的延后到用的时候。 适用的场景也和缓存相反,适用于一些低频的、运算耗时的数据上。 延迟加载、插件化等等就是该思想的体现。 05 批量,合并 如果你需要在短时间内频繁的传递多个数据给同一个目的地,那么尽量考虑将他们打包到一起,一次性传输,特别是涉及到I/O的场景。 如果手头的系统还是一个单点系统,这招的性价比就非常高。在避开分布式系统的复杂性的前提下,获得性能提升。 数据库的bulk操作,前端的sprite图,都是该思想的体现。 应用程序层面的其它优化方式还有很多。 比如,用长链接代替频繁打开关闭的短链接、压缩、重用等等。这些相对比较简单和好理解,就不多说了。 应用程序层面的事情做到位了之后,我们再来考虑组件层面的优化。 02 组件层面组件是指那些非业务性的东西,比如一些中间件、数据库、运行时的环境(JVM、WebServer)等。 数据库的调优,总的来说分为以下三部分:
其它的一些,比如JVM的调优最主要的就是对「GC」相关的配置调优。 Web Server的调优主要是针对「连接」相关的调优。 这些细节就不赘述了,资料多到看不过来。 03 系统层面系统层面的一些调优工作,涉及到运维工程师的一些工作,我不是很擅长就不误人子弟了。 但是我们可以借助系统层面的一些技术指标来观测并判断我们的程序是否正常。 比如,CPU、线程、网络、磁盘、内存。 01 CPU 判断CPU是否正常,大多数情况下关注这三个指标就够了,CPU利用率、CPU平均负载、CPU上下文切换。CPU利用率大家基本上都知道,就不多说了,那就说说后面两个。 关注CPU平均负载的时候,特别需要注意趋势的变化。如果 1 分钟/5 分钟/15 分钟的三个值相差不大,那说明系统负载很平稳,则不用关注,如果这三个值逐渐降低,说明负载在渐渐升高,需要排查具体的原因。 CPU上下文切换。上下文切换的次数越多,就意味着更多的CPU时间消耗在寄存器、内核栈以及虚拟内存等数据的保存和恢复上,真正进行你所期望的运算工作的时间就越少,系统的整体性能自然就会下降。导致这个情况的原因主要有两点,
02 线程 线程方面除了关注线程数之外,还需要关注一下处于「挂起」状态的线程数量有多少。 挂起状态的线程数过多,意味着程序里锁竞争激烈,需要考虑通过其它的方案来缩小锁的粒度、级别,甚至是避免用锁。 03 网络 通常在硬件层面内网带宽会远大于外网的带宽,所以,外网带宽被吃满的情况更加常见,特别是多图、多流媒体类型的可对外访问系统。关于流量大小相关的问题一般大家都能想到,就不多说了。 但是,Z哥提醒你要特别关注端口的使用和每个端口上的连接状态情况。 比较常见的问题是,连接用完有没有及时释放,导致端口被占满,后续新的网络请求无法建立连接通道。 (可以通过netstat、ss获取网络相关的信息。) 04 磁盘 除非是规模非常大的系统,否则一般情况下,从磁盘的指标上看不出啥问题。 平时看的时候,除了看看利用率、吞吐量和请求数量之外,有两个容易被忽略的点可以多关注下。 第一点,如果I/O利用率很高,但是吞吐量很小,则意味着存在较多的磁盘随机读写,最好把随机读写优化成顺序读写。(可以通过 strace 或者 blktrace 观察 I/O 是否连续判断是否为顺序的读写行为) 其次,如果I/O等待队列的长度比较大,则该磁盘存在 I/O 性能问题。 一般来说,如果队列长度持续超过2就可以这么认为。 05 内存 关注内存的时候除了内存消耗之外,有一个Swap换入和换出的内存大小需要特别注意一下。-- Mac上使用也可以看一下,如果换入换出很大,系统就表现卡顿 因为Swap需要读写磁盘,所以性能不是很高。 如果GC的时候遍历到的对象恰巧被Swap 出去了,便会有磁盘I/O产生,性能自然会下降。所以这个指标不应该太高。 大多数内存问题,都和对象常驻内存不及时释放有关,有很多工具可以观察对象的内存分配情况。 如,jmap、VisualVM、heap dump等。 如果你的程序部署在linux系统上的话,不能错过Brendan Gregg的大神整理的精华。 下面就引用一张图,给大家感受一下,具体可以去 Linux Performance 自行查阅更多相关的内容。 ▲图片来自于brendangregg.com 最后,虽然性能优化是一件大家都知道的好事,但是再好的事做起来都有成本。 所以,如非必要,不要过早、过度进行性能优化哦。 好了,总结一下。这篇呢,Z哥和你聊了一下非常让程序员们头疼的程序性能问题。 想要避免受这个问题困扰的前提是事前做好性能优化工作。 做性能优化不能走一步算一步。 事先需要做三件事「明确优化目的」、「定标准」、「找到瓶颈点」。 具体做优化的时候建议 从应用程序层面开始,再到组件层面,最后才是系统层面,从上往下,层层深入。 顺带分享了每个层面的常用一些方法和思路。 希望对你有所启发。 在一个大系统中,数据就像水,整个系统就像是一个漏斗,漏斗的每一层代表每个子程序。 上层的子程序对性能的损耗越低,能流下去的水就越多,直到最后一层「数据库」处,也可以理解为是存储。 所以,赶紧行动起来,开启保卫数据库之战吧。 后续补充 ... |
3.问题/补充
TBD |
4.参考
性能优化 = 改改代码? 做「容量预估」可没有true和false Linux性能分析图 |
后续补充
...