既然看到这了,送我一个「赞同」吧,支持个人创做。
想更进一步和我一块儿玩耍,欢迎「搜索微信公号:跨界架构师」或者在「右侧扫描」。
内容包括:架构设计丨分布式系统丨产品丨运营丨我的深度思考。
若是第二次看到个人文章,欢迎右侧扫码订阅我哟~ 👉html
每周五11:45 按时送达。固然了,也会时不时加个餐~前端
个人第「124」篇原创敬上linux
你们好,我是Z哥。程序员
很久没写技术文章了,最近正好有进行一些思考,顺手写出来分享给你们。算法
上了必定规模的系统,特别是To C的系统,性能优化或多或少都会被逼着去作一下。不然,系统便没法支撑业务的发展,技术成了拖后腿,不是引领业务了。数据库
一旦线上出现了性能问题,就会很棘手。由于它和业务功能上的Bug不一样,后者的分析和解决思路更清晰,只要日志记录到位,沿着一条已知的业务逻辑线,很容易就能找到问题根源。缓存
而性能问题就会复杂的多,致使的因素有不少,甚至会是多种因素共同做用下的结果。好比,代码质量低下、业务发展太快、架构设计不合理等等。性能优化
并且通常状况下,性能问题处理起来比较耗时,涉及到的分析链路可能会很长,特别是本身小组以外的上下游系统,不少人不肯意干,或者说有心无力。最多采用一些临时性的补救手段,碰碰运气。好比,扩容增长机器、重启大招、……。服务器
有些临时性的补救措施,有时候不但不能解决问题,还会埋下新的隐患。微信
好比,从表象上看到某个程序由于给的资源不足致使产生性能问题。临时增长更多资源给它,可能从表面上看,问题是解决了。可是实则多是由于程序内部对资源的使用上存在不合理的地方,增长资源只是延缓问题发做的时间,并且还可能会侵占其它程序的运行资源。
为了不陷入如此的窘境,咱们应当尽可能提早进行性能优化,未雨绸缪。甚至最好是将它做为一个周期性的工做来进行。
接下去就来分享一下我对作性能优化的思路。
不少人优化优化着慢慢变成了为了优化而优化,目的丢了,或者甚至一开始就没考虑过。如此会陷入到无心义的性能黑洞中,没法自拔,只是不断追求更好看的性能指标。
优化的目的能够是加强用户体验,好比消除一些有明显卡顿的页面和操做。还能够是节省服务器带宽流量、减小服务器压力这些。不管如何,你须要有一个目的。
优化这事是永无止境的,为了不陷入到前面说的无心义的性能黑洞中,咱们最好可以根据实际的业务状况定义出一个相对客观的标准,表明优化到什么程度。
我本身惯用的标准是确保比预期高50%,若是条件容许则争取到100%。
好比,根据当下的性能指标与业务量对比,发现最大并发数可能会超过当前的2倍,那么此时优化到争取优化提高3倍,至少保证能提高2.5倍,是一个比较合理的标准。
以前专门写过一篇关于容量预估的文章《作「容量预估」可没有true和false》,能够在文末跳转过去看下,这里就不展开了。
不少人作优化的时候,逮着代码就开始改。的确,只要有必定的知识积累,很容易就能从代码中发现,写法A不如写法B这样的代码。
但其实大部分状况下,「流程上的优化远胜于语法级别的优化」。好比将每个字符串拼接改为用StringBuilder来实现,大多数状况下带来的成果其实很小,甚至在某些状况下还不如不改。
因此,咱们最好仍是可以借助一些客观数据,以得到更多的运行环境相关的信息,来找到整个“木桶”上最短的一块“板”。如整个系统的整体架构、服务器的信息等,便于定位到底性能的瓶颈点在哪。
「流程上的优化远胜于语法级别的优化」中的“流程”除了业务流程以外,还包括技术层面的流程,好比数据在网络中的流转过程。
最后才是着手优化。
作优化的时候须要避免两个常见的误区。
第一,不要过分追求应用的单机性能,若是单机表现良好,还应该从总体的角度去思考。
第二,不要过分追求单一维度上的极致优化,好比过分追求 CPU 的性能而忽略了内存方面的瓶颈。
正确的思路通常符合下面两个方向。
第一,空间换性能。一个节点顶不住就多复制一个节点出来,独一份的数据致使资源竞争得厉害,就多复制一份数据出来。
第二,距离换性能。数据从服务端通过层层处理返回到客户端以为慢的话,那么能不能直接保存在客户端,或者至少是离客户端尽量近的地方。
好了,思路清楚了,具体在作的时候我建议你根据下面小标题的顺序进行。无论是主动地性能优化,仍是被动地排查性能问题都同样。
无论你愿不肯意认可,现实中的大部分性能问题皆是应用程序自身部分的代码致使的。
咱们老是不太愿意认可本身的错误,我见过太多程序员老是习惯性的将问题先归结于硬件问题,网络问题等等,而后最终排查下来的根源每每仍是在coding的应用程序上。
因此,咱们更应该先从应用程序自己入手进行分析。并且,应用程序所处的位置更「上游」,可操做性更强,让咱们能够有更多的手段进行优化。
首先,最多见的即是「缓存」,这是用空间换性能的经典。
数据必然是存储在非易失性的数据库中的,可是一些会被高频访问的数据,将它从数据库中复制一份,存储在易失性的内存上作缓存,能够大大提升被访问的性能。这个道理你们都懂,就很少说了。
可是值得提醒的一点是,缓存数据的数据结构设计很重要,没有一种数据结构是万能的。须要更多的权衡,由于数据结构设计的越简单、单一,缓存数据的二次运算就越多;反之,全部都存储「结果数据」的话,须要冗余的数据量又过大(缓存数据更新还麻烦)。
还得提醒一点,若是缓存的数据量不小,还得考虑增长一个缓存淘汰算法,不然缓存命中率不堪入目,白白浪费大量内存资源。
以前的《分布式系统系列》中有几篇缓存相关的聊了不少细节,能够在文末跳过去查阅。
举个现实生活中的例子,若是你在手机上点了一杯奶茶,去店里拿的时候发现前面还有20个号,你会在这干等半小时么?
我想大部分人都不会吧,宁愿去别的地方溜溜。异步就是经过避免“干等着”来提高性能的手段。
作异步主要是如下两种方式,
经过线程进行异步。这主要用于涉及到I/O的地方,像磁盘I/O和网络I/O。一旦产生I/O其实就意味着背后的操做是由另一个程序在进行,此时CPU就不用空着了,让它忙别的去吧。
经过中间件异步,好比MQ。这用于更大的场景里,好比在某些流程中、上下游系统的衔接中,若是有些结果并不须要实时收到,那么经过MQ进行异步就能够大大提升性能。毕竟MQ的性能更接近NoSQL,性能天然比关系型数据库高的多。更况且,还将一些业务逻辑的预算给滞后了,当下的性能会更好。
这两点都是「分治」思想的体现。一个快递员送1000个包裹比较慢,那么让10个快递员同时各送100个天然就快了。
可是切勿分的太狠,毕竟,多起一个线程至关于多一个放养的娃,放出去太多的话,管理成本很高,可能反而会更慢。这就是线程切换的成本,分布式系统中也存在相似的管理成本。
不过,一个小建议送给你。不到无可奈何,能经过「单机多线程」应付的,就不要引入分布式了。由于,网络这个东西实在太不靠谱了,你得为它作大量的额外工做。
这个和缓存的思路相反,将一些运算尽量的延后到用的时候。适用的场景也和缓存相反,适用于一些低频的、运算耗时的数据上。
延迟加载、插件化等等就是该思想的体现。
若是你须要在短期内频繁的传递多个数据给同一个目的地,那么尽可能考虑将他们打包到一块儿,一次性传输,特别是涉及到I/O的场景。
若是手头的系统仍是一个单点系统,这招的性价比就很是高。在避开分布式系统的复杂性的前提下,得到性能提高。
数据库的bulk操做,前端的sprite图,都是该思想的体现。
应用程序层面的其它优化方式还有不少。好比,用长连接代替频繁打开关闭的短连接、压缩、重用等等。这些相对比较简单和好理解,就很少说了。
应用程序层面的事情作到位了以后,咱们再来考虑组件层面的优化。
组件是指那些非业务性的东西,好比一些中间件、数据库、运行时的环境(JVM、WebServer)等。
数据库的调优,总的来讲分为如下三部分:
SQL语句。
索引。
链接池。
其它的一些,好比JVM的调优最主要的就是对「GC」相关的配置调优。WebServer的调优主要是针对「链接」相关的调优。这些细节就不赘述了,资料多到看不过来。
系统层面的一些调优工做,涉及到运维工程师的一些工做,我不是很擅长就不误人子弟了。可是咱们能够借助系统层面的一些技术指标来观测并判断咱们的程序是否正常。好比,CPU、线程、网络、磁盘、内存。
判断CPU是否正常,大多数状况下关注这三个指标就够了,CPU利用率、CPU平均负载、CPU上下文切换。CPU利用率你们基本上都知道,就很少说了,那就说说后面两个。
关注CPU平均负载的时候,特别须要注意趋势的变化。若是 1 分钟/5 分钟/15 分钟的三个值相差不大,那说明系统负载很平稳,则不用关注,若是这三个值逐渐下降,说明负载在渐渐升高,须要排查具体的缘由。
CPU上下文切换。上下文切换的次数越多,就意味着更多的CPU时间消耗在寄存器、内核栈以及虚拟内存等数据的保存和恢复上,真正进行你所指望的运算工做的时间就越少,系统的总体性能天然就会降低。致使这个状况的缘由主要有两点,
程序内的磁盘I/O、网络I/O比较多。
程序内启动的线程过多。
线程方面除了关注线程数以外,还须要关注一下处于「挂起」状态的线程数量有多少。
挂起状态的线程数过多,意味着程序里锁竞争激烈,须要考虑经过其它的方案来缩小锁的粒度、级别,甚至是避免用锁。
一般在硬件层面内网带宽会远大于外网的带宽,因此,外网带宽被吃满的状况更加常见,特别是多图、多流媒体类型的可对外访问系统。关于流量大小相关的问题通常你们都能想到,就很少说了。
可是,Z哥提醒你要特别关注端口的使用和每一个端口上的链接状态状况。比较常见的问题是,链接用完有没有及时释放,致使端口被占满,后续新的网络请求没法创建链接通道。(能够经过netstat、ss获取网络相关的信息。)
除非是规模很是大的系统,不然通常状况下,从磁盘的指标上看不出啥问题。
平时看的时候,除了看看利用率、吞吐量和请求数量以外,有两个容易被忽略的点能够多关注下。
第一点,若是I/O利用率很高,可是吞吐量很小,则意味着存在较多的磁盘随机读写,最好把随机读写优化成顺序读写。(能够经过 strace 或者 blktrace 观察 I/O 是否连续判断是不是顺序的读写行为)
其次,若是I/O等待队列的长度比较大,则该磁盘存在 I/O 性能问题。通常来讲,若是队列长度持续超过2就能够这么认为。
关注内存的时候除了内存消耗以外,有一个Swap换入和换出的内存大小须要特别注意一下。由于Swap须要读写磁盘,因此性能不是很高。若是GC的时候遍历到的对象恰巧被Swap 出去了,便会有磁盘I/O产生,性能天然会降低。因此这个指标不该该过高。
大多数内存问题,都和对象常驻内存不及时释放有关,有不少工具能够观察对象的内存分配状况。如,jmap、VisualVM、heap dump等。
若是你的程序部署在linux系统上的话,不得不错过Brendan Gregg的大神整理的精华。下面就引用一张图,给你们感觉一下,具体能够去 http://www.brendangregg.com/linuxperf.html 自行查阅更多相关的内容。
▲图片来自于brendangregg.com
最后,虽然性能优化是一件你们都知道的好事,可是再好的事作起来都有成本。因此,如非必要,不要过早、过分进行性能优化哦。
好了,总结一下。
这篇呢,Z哥和你聊了一下很是让程序员们头疼的程序性能问题。想要避免受这个问题困扰的前提是事前作好性能优化工做。
作性能优化不能走一步算一步。事先须要作三件事「明确优化目的」、「定标准」、「找到瓶颈点」。
具体作优化的时候建议从应用程序层面开始,再到组件层面,最后才是系统层面,从上往下,层层深刻。顺带分享了每一个层面的经常使用一些方法和思路。
但愿对你有所启发。
在一个大系统中,数据就像水,整个系统就像是一个漏斗,漏斗的每一层表明每一个子程序。上层的子程序对性能的损耗越低,能流下去的水就越多,直到最后一层「数据库」处,也能够理解为是存储。
因此,赶忙行动起来,开启保卫数据库之战吧。
推荐阅读:
做者:Zachary
出处:https://zacharyfan.com/archives/1051.html
若是你喜欢这篇文章,能够点一下右下角的「推荐」。
这样能够给我一点反馈。: )
谢谢你的举手之劳。
既然看到这了,送我一个「赞同」吧,支持个人创做。
想更进一步和我一块儿玩耍,欢迎「搜索微信公号:跨界架构师」或者在「右侧扫描」。
内容包括:架构设计丨分布式系统丨产品丨运营丨我的深度思考。
原文出处:https://www.cnblogs.com/Zachary-Fan/p/howdoperformancetest.html