This commit is contained in:
louzefeng
2024-07-11 05:50:32 +00:00
parent bf99793fd0
commit d3828a7aee
6071 changed files with 0 additions and 0 deletions

View File

@@ -0,0 +1,141 @@
<audio id="audio" title="01 | 程序员为什么要关心代码性能?" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/cd/08/cdfe1a5ef37d32cf2d66403a97353808.mp3"></audio>
你好,我是庄振运。
感谢你加入这个专栏的学习,我也非常高兴能有机会和你一起探索这个领域。
我在计算机和互联网行业已经研究和工作近 20 年了,一直从事性能优化和容量管理相关的工作。从今天起,我就和你分享我这些年的经验和感悟。
提起计算机和互联网多数人首先想到的职业是程序员。中国有多少程序员呢很多人估计有600万左右。全球的人数就更多了肯定超过2000万。
我虽然也在互联网领域,也做过几年写程序的工作,但是现在的工作,严格意义上不算程序员,而是性能工程师。不过我和很多程序员朋友一起工作过,也讨论过。谈到性能优化和系统容量管理的时候,一开始他们经常会问我一个问题,就是程序员为什么需要了解性能和容量这些东西?通俗点说,这个问题就是:我就是一介程序员,性能和系统容量听起来很重要,但与我何干?
这个问题问得很好。我可以和你肯定地说,程序员应该关心,也必须关心代码性能和系统容量。今天这一讲,我们先说说程序员为什么需要关心性能。
## 怎么定义“性能”和 “性能好”?
说起代码性能,首先我们需要弄清楚什么样的代码算是性能好?怎么样算是性能不好?
代码性能表现在很多方面和指标比较常见的几个指标有吞吐量Throughput、服务延迟Service latency、扩展性Scalability和资源使用效率Resource Utilization
- 吞吐量:单位时间处理请求的数量。
- 服务延迟:客户请求的处理时间。
- 扩展性:系统在高压的情况下能不能正常处理请求。
- 资源使用效率单位请求处理所需要的资源量比如CPU内存等
必须说明的是这几个指标之外根据场景还可以有其他性能指标比如可靠性Reliability。可靠性注重的是在极端情况下能不能持续处理正常的服务请求。不过我们这个专栏的讨论主要围绕前四个更常见的目标。
性能好的代码,可以用四个字来概括:“多快好省”。
<img src="https://static001.geekbang.org/resource/image/95/e6/95a0a14caf49b53ee4859fe892596fe6.jpg" alt="">
看到这四个字,你可能想起了咱们国家当年制定的大跃进总路线,那就是:“鼓足干劲、力争上游、多快好省地建设社会主义”。没错,高性能代码的要求和这个“社会主义建设总路线”相当一致。这里的“多”,就是吞吐量大;“快”,就是服务延迟低;“好”,就是扩展性好;“省”,就是资源使用量低(也即是资源使用效率高)。
用这样的四个指标来衡量,那么性能不好的代码的表现就是:吞吐量小、延迟大、扩展性差、资源使用高(资源使用效率低)。
## 程序员为什么要关心代码性能?
对程序员来讲,写出的代码就是他的产品、他的生命线、他的形象和价值。代码性能不好,就是质量差,不靠谱。轻者影响程序员的声誉,重者影响他的工作。
对一个公司来讲,产品质量差,公司或许会倒闭。对程序员所在的互联网公司而言,如果公司的业务依赖于程序员写的代码,那么代码性能差,关键时刻掉链子,比如双十一促销的时候,公司的业务性能就会经常出问题,进而会影响公司的运营和营收,这可是天大的事情。
因此,如果一个程序员写出性能很差的代码,无异于耍流氓,并且相关程序员的工作也很难保住。
反过来讲,如果写出的代码性能很高,那代码的作者必定是我们大家认可的“靠谱”程序员,少不了“人见人爱”——客户喜欢,同事喜欢,领导也喜欢。
## 不同级别的程序员都需要关心性能
还有些朋友或许认为:代码性能是**某些人或者其他人**应该负责的;我就负责把代码写出来,优化的事,他们负责。这里的“某些人和其他人”可以是指软件测试人员、运维人员、技术专家,或者是性能工程师。
这种想法也是不对的。我下面就用几个案例来举例说明,代码性能是各个级别的程序员都应该关心和负责的。事实上,程序员从学校出来开始,一步步地在职业上攀升,每一步都应该和性能结伴而行。
我用一张图来表示一个成功程序员的技术职业轨迹(注意里面的职位和年限仅供参考)。
<img src="https://static001.geekbang.org/resource/image/0b/de/0b74f5861099d26e00ec76007913b6de.jpg" alt="">
学生刚刚从学校毕业加入互联网公司一般是入门级程序员。工作1到3年后就成为普通的程序员。工作三五年后可以算是资深程序员。工作6到10年后可以成长为技术专家。10年以上可能成为高级专家或者架构师。
### 举例1刚入门的程序员
小李刚刚大学毕业,进入一个互联网公司。
领导给他的任务是写一个小模块,其中有一个需求是统计两个日期之间有几个正常工作日(也就是多少是周一到周五)。小李采取的是简单暴力法,就是用一个循环,循环的起始和截至日期就是给定的两个日期。在循环里面,对每一个日期判定一次,确定是工作日还是休息日,然后把工作日累加起来。
这样的代码显然性能不高,生产环境里面跑起来很快就会出问题。比如,如果两个日期差距很大,这个模块可能就需要很长时间才能处理完。
如果小李注重代码性能他完全可以用更高效的方法比如快速判定给定的两个日期间有多少个星期然后乘以5因为每个星期有5个工作日。然后对头尾的星期进行特殊处理。这样的代码跑起来快多了。我可以想象小李在优化完代码后或许会吟诵两句“何当金络脑快走踏清秋”来形容新代码的性能。
### 举例2普通的程序员
小王做程序员2年了在公司里已经可以独立负责一个模块了。有一天他需要把一个二维整数数组进行重新赋值于是他写出了下面的二重循环
<img src="https://static001.geekbang.org/resource/image/35/a9/3549fc43c8199499d31b2bf5432f23a9.jpg" alt="">
如果小王了解计算机内存和缓存的知识以及大小,他或许会写出下面的循环。虽然只有两个字母的差别,性能却提升了很多倍。
<img src="https://static001.geekbang.org/resource/image/20/d9/2055088aa3fc67f23d0274435c1e48d9.jpg" alt="">
原因是什么呢?
因为计算机通常都会有数量不大的缓存。数组在内存里是连续存放的,所以,如果访问数组元素的时候能够按照顺序来,缓存可以起到极大的加速作用。
小王一开始的二重循环,恰恰没有有效地使用缓存,反而对数组元素类似随机访问。第二个版本就改正了这个错误,优化了性能。
### 举例3资深的程序员
小赵工作4年了已经算是资深的C++程序员负责一个程序的开发和设计。他的一个程序需要使用一个Map的数据结构。他开始使用的是STD库的标准实现`unordered_map`。但是他发现,在数据量大的时候,键值的插入操作需要的时间很长。虽然做了各种代码优化,但性能总是不尽人意。
其实如果他了解C++有些库有更高效的Map实现比如`google::dense_hash_map`,他或许可以酌情采用,从而大幅度提升性能。
很多的测试结果显示,`google::dense_hash_map`的性能可以比`std::unordered_map`快好几倍。下图(图片来自[https://tessil.github.io/](https://tessil.github.io/) )正是同一种测试环境下,两种实现的处理时间比较,我们可以清楚地看出性能的差距。
<img src="https://static001.geekbang.org/resource/image/12/4e/124cbafaf8f452f8399c2c85a1a6534e.png" alt="">
### 举例4技术专家
小刘工作8年了在公司里已经算是不大不小的技术专家了。
有一天他看到一份项目计划其中有一段引起了他的兴趣。这份计划是为了提高服务器的CPU使用效率提出把应用程序的线程池增大建议程序线程池的主线程数目应该和服务器的逻辑CPU的数目相等。当然这里的逻辑CPU就是我们通常说的虚拟内核数。
小刘这几年对硬件和操作系统钻研良多他立刻指出这样部署不妥他建议降低主线程池大小到逻辑CPU的一半。技术讨论过程中小刘给大家仔细讲解了原因大家最后认可了他的建议小刘也获得了大家的青睐。
小刘之所以这样建议是因为他知道服务器的逻辑CPU不是物理CPU。在超线程技术Hyper Threading的情况下服务器的吞吐量不是严格按照逻辑CPU的使用率来提升的因为两个逻辑CPU其实共享很多物理资源。
比如下面这张图就表示了在一台有个逻辑CPU的服务器上如果部署超过4个线程得到的性能提升非常有限甚至可能会带来其他不好的后果。这里具体的提升率和效果取决于线程和应用程序的特性。图片来自[http://blog.stuffedcow.net](http://blog.stuffedcow.net)<br>
<img src="https://static001.geekbang.org/resource/image/fd/45/fd4b4e1797eed83f9967daaf0ea15745.png" alt="">
### 举例5:高级专家(架构师)
老周是公司里的架构师和高级专家。他最近对公司的一个重要业务进行了性能优化,用很小的代码改动,就给公司节省了几百万美元的运营成本(这是我身边发生的一个真实案例,除了名字不一样)。
这个业务的性能瓶颈是CPU。因为业务量大这个业务部署了1万台以上的服务器占用了很大一部分数据中心的容量。
老周仔细研究了业务的逻辑并且进行了性能测试和分析。他发现代码的执行过程卡在了CPU取指令的速度上因为内存和缓存的物理特性CPU花了很大一部分时间在等待指令获取从而造成了CPU浪费。
他经过考虑,决定进行**指令级别的提前获取优化**。具体来讲就是用GCC的`__builtin_prefetch`指令来预先提取关键指令从而降低缓存的缺失比例也就提高了CPU的使用效率。
下图是GCC关于这个指令的官方文档。
<img src="https://static001.geekbang.org/resource/image/95/36/95c984c03bc60ccb3f56867e0c4bcb36.png" alt="">
经过这样的优化一台服务器可以处理比以前多50%的请求从而节省了相应比例的服务器和容量。从公司成本角度来看这一优化节省了3千台以上的服务器价值几百万美元老周被CEO开会表扬也是自然的事情了。
有趣的是,整个的代码改进只需要几行代码的改动,真真切切是“一字万金”。
## 总结
重要的事情需要多说几遍每个IT从业人员尤其是程序员都需要关心代码性能。
如果不了解性能的知识,也许能写出可运行但性能不好的代码。但一个真正对工作、对公司和对自己负责的程序员一定会发现,性能不好的代码无异于耍流氓,不经用还隐患无穷,万万要不得。
换句话说,对程序员来说,生活不仅是眼前的代码,还有效率和性能的优化。唐代诗人孟郊在考中进士后写了一首《登科后》,其中有两句:“春风得意马蹄疾,一日看尽长安花。”
我们谁不希望写出来的代码也运行飞快,自己能春风得意呢?!
## 思考题
无论你工作几年了,也无论是现在具体做什么工作,你能举出一两个,因为代码性能不好并导致严重后果的例子吗?是什么样的性能问题呢?
换个角度来说,如果写代码的程序员一开始就考虑到各种性能问题,并且提前在代码里面解决,写出的代码跑得飞快,而且很稳定。这样靠谱的程序员你会不会给他点赞?
欢迎留言和我分享你的观点,也欢迎你把今天的内容分享给身边的朋友,和他一起讨论。

View File

@@ -0,0 +1,122 @@
<audio id="audio" title="02 | 程序员也要关心整个系统和公司成本吗?" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/b6/ed/b65ad4079f3ba8a6cf442777c7d8a0ed.mp3"></audio>
你好,我是庄振运。
上一讲我们谈了,作为一个程序员,你所负责的软件模块的性能是很重要的。如果写的程序性能不好,轻则通不过开发过程中的性能测试这一关,严重的话,还会为以后的业务生产环境埋下很多地雷和炸弹,随时会踩响和爆炸,从而影响公司的业务和运营。
代码性能的重要性,不仅仅局限于程序员所直接负责的软件模块,它对其他相关软件模块、模块所在的应用程序、单机系统的设计、互联网服务的质量、公司的运营成本,甚至对我们共同生活的地球都很重要。
这一讲,我们就来说说这几个方面。为了方便说清这几方面的关系,我画了下面这张图。
<img src="https://static001.geekbang.org/resource/image/10/3c/1076d5164429beb91bfb4e790f8d2a3c.jpg" alt="">
我来简单解释一下这张图:
- 首先红色模块是我们负责的模块标示0它和其他模块一起构成了整个应用程序标示1
- 这个应用程序运行在服务器和OS上面构成了一个单机系统标示2
- 几个单机系统一起组成一个互联网服务标示3来面向客户
- 这个服务和其他服务一起需要公司的硬件容量支持从而占用公司的商业成本标示4
- 最后,别忘了,我们共同生活在这个可爱的绿色星球上。
## 应用程序的性能标示1
<img src="https://static001.geekbang.org/resource/image/10/3c/1076d5164429beb91bfb4e790f8d2a3c.jpg" alt="">
我们先从标示0和1开始也就是模块和应用程序。
我们每个人负责的代码模块,一般都不是孤立存在的,都要和其他模块交互。模块之间是唇齿相依的。如果一个模块性能不好,一定会在某种情况下影响到其他模块,甚至是整个程序的性能和服务质量。唇亡齿寒的道理我们都懂,所以每个软件模块的性能都需要严格把关。
具体到我们自己的模块来说,或许在开发、测试的环境中,这个模块看起来运行正常,但等到了生产环境,一旦流量上去,性能不好的模块很快就会被曝光。尤其是在流量很高的时候,如果性能不佳,公司的运维同事一定会疲于奔命,更甚者会导致公司业务受损,这时性质就很严重了。
很多性能问题都会被根因分析。如果根因分析定位到是某人所负责的模块拖了大家的后腿,写这个模块的程序员不仅会被其他程序员鄙视,还可能会被老板找去“喝茶”。
回到性能问题上。性能问题可以表现在很多指标上比如吞吐量Throughput服务延迟Latency)可靠性Reliability)延展性Scalability等等。根据模块所在的应用程序的性质其中的一个或者几个指标会相对比较重要些。举个简单的例子如果一个应用程序所在的互联网服务是面向终端客户的比如微信用户那么客户的服务延迟一定是极为重要的性能指标。
具体来说假如端到端的服务延迟有最大的延迟允许比如不能超过2秒钟那么这个服务所需要的应用程序或者微服务一般也都会有自己的最大延迟预算。假设这个端到端服务需要三个微服务或应用程序来串联那么每个应用程序都会分到一定的延迟预算比如最大1秒。
同理我们所负责的模块也会根据程序的逻辑设计分到相应的延迟预算比如300毫秒如下图红色模块所示。
<img src="https://static001.geekbang.org/resource/image/38/8a/38553b84376fa3add7bcbc44cebec78a.jpg" alt="">
这种情况下如果我们的模块在流量适度变大时处理时间超过300毫秒的预算那这个模块的延展性显然就不够了很可能会导致整个端到端服务的延迟超标。
## 单机系统的性能标示2
讨论完了模块和程序,我们再看看单机系统。
<img src="https://static001.geekbang.org/resource/image/10/3c/1076d5164429beb91bfb4e790f8d2a3c.jpg" alt="">
我们的模块所在的应用程序(或者微服务),是运行在服务器的硬件和操作系统上面的。对这台服务器而言,这是个单机系统,包括软件和硬件的整个垂直全栈。现在的系统都非常复杂,软硬件之间的交互也复杂而微妙,并且随着各个构件的升级而经常变化。
单机系统的软硬件构件包括操作系统、程序库、存储系统、CPU、内存、还有网络等等。这些构件都会或多或少地影响上层程序的性能。
我举一个简单的例子,这个例子在后面的文章中还会仔细介绍。在我以前的公司,曾经有一个应用程序需要不断地把数据同步写到底层存储系统。这个应用程序后来被发现有性能问题,我们花了很多时间去做分析,后来终于找到原因。
这个性能问题的表现是:数据同步写入的延迟有时候会非常高。原因是**底层的存储系统同时服务好几个应用程序**。
由于底层存储系统有各方面的限制,当多个应用程序同时使用这个存储系统时,每个应用程序延迟方面的性能并不能得到保证,因此导致了某个应用程序的读写被严重推迟,并最终导致了后者的性能问题。
更进一步到设计层面来讲,从我们负责的模块和应用程序角度来看,**对下层的软硬件构件越是了解,就越有可能设计出性能优越的模块和应用程序**。
比如很多数据存储方面的服务和应用程序在设计时需要仔细考虑各种存储系统的技术趋势和性能特征。这些性能特征包括存储速度、价格、容量大小、易失性等。比如传统的DRAM内存就是一种存储它速度很快但是价格贵、容量小并且所存数据不能长期保存一断电数据就会丢失。
最近几年一种新的非易失性内存NVMNon-volatile Memory的出现打破了这一传统数据可以长期保持但是速度稍微慢一些。同样的传统的硬盘存储容量大、价格低但是速度最慢。最近几年固态硬盘SSD 的大量采用,在很多新设计的在线系统中已经作为标准配置,几乎取代了传统硬盘。
我们程序员作为自己模块甚至整个应用程序的设计者,如果能充分考虑这些硬件的性能特征和技术趋势,就可以设计出性能好、高效率的软件。
下面这张表格我列举了四种存储硬件分别是传统DRAM内存、NVM内存、硬盘和固态硬盘并且比较了它们的五个指标。
<img src="https://static001.geekbang.org/resource/image/50/23/5061b7cf9e91d10f584da89dab2a0323.jpg" alt="">
## 互联网服务的性能标示3
<img src="https://static001.geekbang.org/resource/image/10/3c/1076d5164429beb91bfb4e790f8d2a3c.jpg" alt="">
现代的互联网服务往往需要很多模块交互,并且客户流量会很大。我们所在的应用程序和系统经常只是整个互联网大服务的一部分,会有上游服务对我们产生请求,我们也会对下游服务发送请求。
比如下面的图示,我们所在的服务模块用红色标识,上游服务模块用绿色标识,下游服务模块用黄色标识。
<img src="https://static001.geekbang.org/resource/image/3e/18/3e13c191e41aa0a206fa194c72027518.jpg" alt="">
从公司运营的角度来看,整个互联网大服务的性能才是我们每个程序员真正关心和负责的。我们每人都需要从这个大局出发来考虑和分析问题,来设计自己的模块以及各种交互机制。否则,可能会出现我们的模块本身看起来设计得不错,但却对上下游模块造成不好的影响,进而影响整个大服务的性能。我来举一个真实的案例。
这个案例是从一次生产环境下的服务问题中发现的。某个下游模块出现延展性问题,服务的延迟变大,上游模块发出的请求排了很长的队。这个时候上游模块已经感觉到下游的性能问题,因为对下游请求的处理延迟已经大幅度增加了。
此时上游模块本应该怎么做呢?
它应该降低对下游模块的请求速度,从而减轻下游模块的负担。但是案例中的上游模块设计没有考虑到这一点。不但没有降低请求速度,反而发送了更多的请求,以求得更快的回答。这样无异于火上浇油,最后导致下游模块彻底挂掉,引发了整个服务的瘫痪。
后来我们学到的教训就是,串联的服务模块中,上游模块必须摒弃这样雪上加霜的服务异常尝试,应该采用**指数退避机制**Exponential Backoff ),通过快速地降低请求速度来帮助下游模块恢复(上游模块对下游资源进行重试请求的时间间隔,要随着失败次数的增加而指数加长)。
## 公司的成本标示4
<img src="https://static001.geekbang.org/resource/image/10/3c/1076d5164429beb91bfb4e790f8d2a3c.jpg" alt="">
我们所负责的互联网服务的性能直接影响公司的成本。
一个高性能的服务在服务同等数量的客户时需要的成本会比较小。具体来说如果我们的服务是计算密集型那么就应该尽量优化算法和数据结构等方面来降低CPU的使用量这样就可以用尽量少的服务器来完成同样的需求从而降低公司的成本。
现如今是大数据时代,公司在服务器和数据中心以及网络等容量方面的支出是很可观的。尤其是大的公司比如脸书,腾讯等,公司有很多的数据中心和几百万台的服务器。如果公司的每个服务都做到高性能,替公司节省的运营成本是非常巨大的。
同时,面向互联网服务的容量规划和效率管理也很重要。如果能科学地管理容量,准确地预测未来需求,并逐步提升容量的效率,就能把公司这方面的成本管理和节省好,从而不至于浪费资金在不必要的多余容量上。
## 我们共同的绿色地球
最后,让我们跳出“我们的公司”这样的小格局,放眼全球,甚至我们人类的大格局。我们只有一个共同的地球,我们有责任让她保持绿色。
现在的时代感谢互联网的发展和大数据时代的来临全球各公司的数据中心已经在消耗大量的能源。从咱们国家来看2018年国内的数据中心用掉的电量比整个上海市用电量还大占全国用电量的2.3%。全球来看也类似数据中心在2018年消耗了全球3%以上的电量。这个耗电量已经是差不多整个英国全国用电量的两倍。更严重的是,这样的用电还在飞速增长,差不多每三年或四年就翻一倍!
所以,我们每个人,其实都负有责任来降低能源消耗。虽然生活中有多种方式可以降低能源消耗,我们的日常工作其实也是重要的一环。如果每个人能把负责的代码优化一下,服务高效一些,我们就是在拯救我们共同的地球,让她永葆绿色!
## 总结
对代码和程序的性能优化,以及对系统容量的效率提升,和我们共同关心爱护的东西息息相关。从代码模块,到整个系统,到互联网服务,到公司运营,再到我们的社会,都依赖于我们每个人的责任和贡献。
你和我或许是一介普通工程师和程序员,但人们常说“位卑未敢忘忧国”。我们虽然没必要拔高到忧国忧民的高度,但是也要认真做好我们的份内份外的事情。
## 思考题
- 回顾你最近接触过的软件模块或者正在写的代码,有没有和其他的系统模块有交互关系,它们之间是如何交互的?
- 如果你的模块性能不好(不管是响应时间很慢,还是发出过多请求),有没有可能对其他系统模块造成影响?这个影响会不会造成整个系统和服务的严重后果甚至瘫痪?
欢迎你在留言区分享自己的思考,与我和其他同学一起讨论,也欢迎你把文章分享给自己的朋友。

View File

@@ -0,0 +1,142 @@
<audio id="audio" title="03 | 导读:专栏是怎么设计的?需要哪些知识?" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/71/d4/71d37661d4e106c3dcb2f18dbd6f28d4.mp3"></audio>
你好,我是庄振运。
在前面两讲中,我们看到了性能优化和容量效率提升的重要性,如果程序员在这方面的技能和知识有欠缺,只知道写代码,那么写出来的代码很可能效率低、性能差。在代码性能差的情况下,如果你再被老板或者同事威逼利诱去做性能优化,那么就成了赶鸭子上架的苦差事了,你只能感叹:“问君能有几多愁,恰似写完代码去调优”。
玩笑开完,我们还是从正能量的角度去看一看吧。
这就要从一个典故说起了美国福特汽车公司当年要排除一台大型发动机的故障请了很多专家但都束手无策。最后请来了著名的电机专家斯坦门茨Charles Proteus Steinmetz。斯坦门茨仔细检查了机器后用粉笔在机器外壳的某处画了一道线然后说“把做记号处的电机线匝减少16圈。”难题居然就迎刃而解了。
斯坦门茨索要了1万美元作为报酬很多人觉得实在是太多了。因为当时福特公司最著名的薪酬口号就是“日薪5美元”也就是说一个工人每年能赚1千美元已经是很高薪了。但是斯坦门茨回答道“用粉笔画一条线顶多值1美元但是知道在哪里画线值9999美元。”当公司总裁福特先生得知后十分欣赏斯坦门茨并很痛快地给了1万美元的酬金。
我们做性能工作也是如此,虽然性能优化的方法和最终解决方案或许看起来很简单直白,但是要知道**在哪里做优化**和**做什么样的优化**,却需要很多的测试和分析的工作经验。
每个程序员和运维人员都应该尽量多地了解性能优化和容量效率提升的知识,学习这方面的技能,因为在自己的职业的不同阶段都可以从这些技能获得收益。
那么,性能优化和容量效率提升需要什么样的知识和技能呢? 这一讲我就和你聊一聊。
## 总体要求
性能和容量管理工作需要的知识面比较广, 而且最好具有其他相关方面的能力,比如要有一定的**和人打交道**的软技能,从而和其他员工以及其他团队进行有效地沟通和合作。
大体上讲,性能相关的工作有三个方面的特点,我分别说明一下。
第一个特点是**知识面要广,并且软硬结合**。
计算机方面的知识可以大体上分为两大类:软件相关和硬件相关。很多程序员对软件相关的知识了解多些,但对硬件方面了解不多。但是,因为很多性能问题会牵扯到硬件,所以基本要求就是“能软能硬”,两方面的知识都要足够。
第二个特点是“**理论联系实际**”。
一方面需要理论根底深厚,因为有时候性能问题是某些不明显的原因导致的,所以需要对各种协议,对软件和操作系统,对硬件和网络都要非常熟悉。同时,性能分析的过程需要做些实验来使真正的问题暴露出来,也就是需要进行验证,所以对动手能力的要求也比较高。
第三个特点是**不但要会性能测试,还要会性能分析和性能优化**。
我画了张图,希望能帮助你理清思路。通俗点说吧,这些方面对我们的要求就是:**写得了代码,查得出异常;理得清问题,做得了测量;找得到病根,开得出药方**。
<img src="https://static001.geekbang.org/resource/image/57/c6/579b9872d17b084e28a9e0c63613c6c6.jpg" alt="">
## 广阔的知识面
我们现在先从知识方面讲起,看看性能和容量工作需要什么样的知识(参考上面的图)。
首先是**软件**方面,其实内容很多。图里面仅仅表示了几个方面,你可以参照我们上学时开的各种计算机方面的课程,尤其是操作系统、数据结构和算法、编译原理以及各类协议。这方面也包括很多数学和统计方面的知识。
**硬件**方面包括服务器本身、存储系统、各类网络、数据中心等等。这方面的重点是服务器本身的部件比如CPU、内存等等。
至于**实践经验**,主要是关于性能方面的。比如很多系统的命令来观察系统资源的使用率,各种调试和测试工具,以及如何进行性能分析和性能优化的实践。
**软技能**方面,我会在这个专栏的最后两讲详细进行介绍,这里大概讲一下。做性能优化和容量管理工作经常需要和其他的团队和人员打交道。这些团队包括开发团队、数据中心团队、运维团队等等,需要和他们紧密而愉快地合作。同时,性能分析和优化经常需要把数据和分析展示给别人和领导看,这就需要你拥有一定的演讲能力。
## 性能工程师的工作很像“医生”的工作
性能问题通常是复杂的,性能工程师的工作就很像“医生”的工作,需要多方面的知识才能确诊(这个观点我在开篇词里有介绍,这里还是有必要再详细说说)。
虽然性能问题有时候明显的是表现在某个组件的问题,比如网络拥塞,但很多情况下并不容易确定是哪里出了问题。所以,就需要做很多种不同的测试和数据分析。
另外,很多程序和服务内部有很多模块,外部也牵扯到其他子系统。即使每个子系统分开来测试时都性能不错,但是在某些特殊情况下,会发生级联和连锁故障。所以,除了需要了解每个子系统内部的原理,还必须弄清楚不同模块和子系统之间的协作交互关系。
具体来讲,软件硬件各个子系统都可能因为各种情况下的交互而产生性能问题。尤其是垂直的软件硬件栈(比如程序、操作系统、硬件等),这样的跨层交互,更会产生各种复杂的性能特性,这方面的内容后面会详细展开。
我经常把性能工程师比作医生。
医生遇到有健康问题的病人会做“望闻问切”利用B超、X光等做各种分析。根据各种数据和病人的表象医生会做出诊断确定是什么病。然后会开药方或者给予相应的治疗。病人吃药和接受治疗后会再次进行复检来确定病情是已痊愈是稍有缓解还是加重了
性能工程师对待计算机和互联网的性能问题也是如此,会首先观察各种参数,甚至进行主动的场景测试。根据性能测试的结果,可以做出分析,并最终确定性能问题的根因。这之后可以进行相应的性能优化来消除对应的性能问题。采取优化后,还需要进行重新测试来确定问题真正得到解决,亦或是另有他因,从而需要重新分析。
我用下图来类比这两种场景。
<img src="https://static001.geekbang.org/resource/image/7b/7d/7bf7a098b73b828116b827c3c3be077d.png" alt="">
第一个场景是医生诊断病情和治疗病人。第二个场景是性能工程师分析问题并且优化性能。我们可以直观地看出两种场景中的每一步的相似之处。
## 这个专栏的组织架构
我是怎样组织这个专栏的呢?首先重申,写这个专栏的初心是向你介绍性能和容量管理工程这一工作,并分享我近二十年来的学习和工作经验。
需要说明的是,这一领域和工作牵涉的内容其实非常广泛,包括所有的计算机互联网方面的知识。所以在我们这个专栏有限的时间内,面面俱到是不太现实的。出于这样的考虑,在这个专栏里我的侧重点及讲述方式和其它相关专栏和课程不太一样。
我在专栏文章中尽量做到深浅结合,既要让你能了解这一工作的重要性和大体情况,也要有足够的干货,希望能让你有恍然大悟的感觉。
计算机科学在国外大学里面一般不在工程学院下面开设而是归类于艺术“Art”设置在艺术和科学学院下面。不管这种分类的渊源如何我确实觉得计算机科学和性能调优真的是一门艺术它需要知识需要经验也需要天分。既然是艺术就和其它艺术形式比如文学有其相通之处。
我个人喜好诗词古文,对生活中和学习中的很多感悟,我经常会跳出一层去体会。很多时候我发现不同领域的东西,它们的感觉和道理是完全相似或相通的。修辞学上有一种手法叫“通感”,就是不同的感觉方式的类似体验(比如听觉和视觉的相通等),也是这个道理。
所以,我在分享知识和经验的时候,有时会忍不住加上几句唐诗宋词古文,目的有好几个,第一是不希望一直干巴巴地讲课,希望加点调味料,提高你的兴趣;第二是理工科的技术和文科的文艺的确经常有异曲同工之妙,希望帮助你体会;第三呢,顺便帮你复习一下诗词,增加学习乐趣。
先一起来看看我们要学习的内容大纲。
### 开篇:代码性能和系统容量
考虑到很多朋友对这方面的工作不是特别了解,所以我开始在**开篇**这一部分用两讲来做一个宏观介绍让你了解性能问题为什么对每一个IT人员都重要尤其是对程序员。我分成“代码性能”和“系统性能及公司成本”来分别说明。
### 性能定律和数理基础
性能工程离不开理论基础。我接下来会用几讲来介绍最基础但也是最重要的数理基础和几大定律。这些数理基础包括一些基本的统计知识,以及对数据的分析和展示的方法。我还会把一些重要的性能相关的数字总结出来,让你参考和记忆。对待这些数字,你应该像对待九九乘法表一样,每个都铭记在心,因为工作中时时要用到。
### 性能测试
性能工程离不开测试。性能测试是一切性能工作的基础和开端。
很多公司的性能工程师其实多数时间是花在性能测试上,包括进行测试的设计和分析测试结果。虽然测试工作看起来简单直白,可是真正做好性能测试并不容易,这里有相当多需要注意的地方。我会梳理其中一些经验和指导原则讲解给你。
当然,测试的工具也很重要,一个好的测试工具绝对让你事半功倍,所以我也会介绍常用的好工具。
### 性能分析
我们需要对性能测试得到的数据进行仔细的分析和研究,只有这样才能发现真正的问题和找到性能问题的根本原因。而**性能分析**就是关键的一步。我会首先介绍进行性能数据分析的原则然后抓住几个重点领域包括CPU、内存、存储和网络来分别介绍常见的性能问题让你以后碰到这方面的问题时心里有数。
### 性能优化
性能分析的目的是找到性能问题的根因,然后进行**性能优化**来解决问题。性能优化做得好,必须有相关方面的知识和实践经验。我会给你介绍性能优化的六大原则和十大常用策略,并分几个领域用生产中的案例做具体的展示。
### 性能工程的进阶实践
我还特意准备了几讲稍微进阶的内容和实践案例。这些内容是我过去在几个大公司的亲身实践,每一讲都是针对某个具体场景的生产实战经验。
### 容量规划和服务管理
对公司,尤其是大公司来说,容量的规划管理和效率提升是很重要的,因为这直接关系着公司的运营成本。我注意到这方面的参考资料比较少,所以特意来和你介绍这一领域的知识。我会分成几部分来分享,包括服务器的部署、数据中心、容量规划、容量的效率提升以及服务需求的控制等等。
一个公司要成功运营,成本和对应的容量是总有限的,所以需要量入为出,对服务的需求进行适当的管理,尽量精打细算。管理的实践需要考虑很多因素,一方面尽量节省容量,另一方面也不能妨碍公司业务的扩展。如何把握这个度,我会讲讲经验。
### 专栏总结:性能和容量工程的工作特点
最后两讲是介绍性能和容量工程师这个职业,包括这一工作的特点和职业前景。
随着大数据和互联网的飞速发展,我坚信这方面的工作越来越重要。尤其是大公司,都会专门招聘这方面的人才组成特殊的性能优化和容量管理团队。所以,针对有志于从事这一行业的朋友,我会分享这方面的面试经验。
## 总结
得益于这种工作的特点,性能优化和容量效率需要了解的知识和技能比较广泛。人们常说:“读书破万卷,下笔如有神。”读书写文章是这个道理,做性能优化工作也是如此。只有不断学习计算机各方面的相关知识,博览群书,才能在解决性能问题时得心应手。
我们在前三讲里面一起探讨了为什么我们需要关心代码性能,系统性能和容量效率;并且了解了这方面的工作需要什么样的知识和技能。从下一讲开始,我们就一起学习这些知识和技能。
## 思考题
- 你碰到过或者听说过什么领域的性能问题吗?
- 对这个性能问题做根因分析和解决需要哪方面的知识和经验?
- 彻底解决这个问题需要和其他人和团队合作吗?
- 如果需要和其他人和团队合作,再假设你是带头人,你会怎么去推动呢?
欢迎你在留言区分享自己的思考,与我和其他同学一起讨论,也欢迎你把文章分享给自己的朋友。