16-MySQL DBA笔记-调优基础理论和工具

第五部分 性能调优与架构篇
本篇将为读者介绍性能调优的一些背景知识和理论,然后介绍一些工具的运用,最后介绍从应用程序到操作系统、到数据库、到存储各个环节的优化。
性能调优是一个高度专业的领域,它需要一定的方法论做指导,我们需要有一定的背景知识和方法论做引导,才能提出正确的问题,
正确的问题往往意味着有解决问题的可能性,这也是我们在处理各种事务的时候最难知道的。
提出正确的问题是一种能力,也是可以训练出来的。
本篇将花大量篇幅叙述各种调优方法,并分享笔者从业多年来的一些经验和意识,目的是和大家沟通有无,启发大家更有效率、更智能地解决性能问题。
本书将主要侧重于如何解决问题,而不会深入讲解理论,现实中,大家主要还是依赖经验法则,较少用到理论。
但是,一些通用的理论,大家有必要熟悉。
作为DBA,性能调优不应该是我们的主要工作,如果对此不是特别有兴趣,那么只要清楚我们所要掌握的能解决问题的知识就可以了。
日常运维中,如何保证不出现性能问题,或在性能问题出现之前就将隐患处理掉,才是更值得看重的,对于运行良好的系统,不存在各种高难度的问题要你去处理。
由于架构和性能优化的相关性很大,因此也合并在本篇一并讲述。

第16章 基础理论和工具
本章首先讲述性能调优的一些概念和理论,然后介绍一些工具的使用,最后介绍一些性能调优的方法。
16.1 性能调优理论
16.1.1 基础概念
对于一些概念,可能不同的人有不同的解释,基本上没有一个很严格的标准答案,
笔者将在此阐释个人理解的一些基本概念,以方便大家在阅读本书时,对照概念,理解我所讲述的内容。
资源(resource):物理服务器的功能组件,一些软件资源也可以被衡量,比如线程池、进程数等。
系统的运行,需要各种资源,对于资源列表的确定,我们可以凭借对系统的了解来确定,也可以通过绘制系统的功能块图的方式来确定要衡量的资源。
常见的物理资源如下所示。
CPU、CPU核数(core)、硬件线程(hardwarethread)、虚拟线程(virtualthread)
内存 、网络接口、存储设备、存储或网络的控制器、内部高速互联
负载 (load):有多少任务正在施加给系统,也就是系统的输入,要被处理的请求。对于数据库来说,负载就包括了客户端段发送过来的命令和查询。
负载如果超过了设计能力,往往会导致性能问题。
应用程序可能会因为软件应用的配置或系统架构导致性能降低,
比如,如果一个应用程序是单线程的,那么无疑它会受制于单线程架构,因为只能利用一个核,后续的请求都必须排队,不能利用其他的核。
但性能下降也可能仅仅是因为负载太多了。负载太多将导致排队和高延时,
比如,一个多线程应用程序,你会发现所有的CPU都是忙碌的,都在处理任务,这个时候,仍然会发生排队,系统负载也会很高,这种情况很可能是施加了过高的负载。
如果在云中,你也许可以简单地增加更多的节点来处理过高的负载,在一般的生产应用中,简单地增加节点有时解决不了问题,你需要进行调优和架构迭代。
负载可以分成两种类型:CPU密集型(CPU-bound)和I/O密集型(I/O-bound)。
CPU密集型指的是那些需要大量计算的应用,它们受CPU资源所限制,也有人称为计算密集型或CPU瓶颈型。
I/O密集型指的是那些需要执行许多I/O操作的应用,例如文件服务器、数据库、交互式shell,它们期望更小的响应时间。它们受I/O子系统或网络资源所限制。
对于CPU密集型的负载,可以检查和统计那些CPU运算的代码,对于I/O密集型的负载,可以检查和统计那些执行I/O操作最多的代码。这样就可以更有针对性地进行调优。
我们可以使用系统自带的工具或应用程序自己的性能检测工具来进行统计和分析。
对于吞吐率,很显然,数据库支持的简单查询的吞吐率会比复杂查询的吞吐率大得多,其他应用服务器也是类似的,简单的操作执行得更快,
所以对于吞吐, 我们也需要定义我们的系统应处理何种负载。
利用率(utilization):利用率用于衡量提供服务的资源的忙碌程度,它是基于某一段时间间隔内,系统资源用于真正执行工作的时间的百分比。
即, 利用率=忙的时间/总计时间
利用率可以是基于时间的,比如CPU的利用率:某颗CPU的利用率或整体系统的CPU利用率。比如对于磁盘的利用率,我们可以使用iostat命令检查%util。
利用率也可以是基于容量的,它可以表示我们的磁盘、内存或网络的使用程度,比如90%的磁盘空间被使用,80%的内存被使用,80%的网络带宽被使用等。
可以用高速公路收费站的例子来进行类比。 利用率表现为当前有多少收费亭正在忙于服务。
利用率100%,就表示所有的收费亭都正在处理收费,你找不到空闲的收费亭,因此你必须排队。
那么在高峰时刻,可能许多时候都是100%的利用率,但如果给出全天的利用率数据,也许只有40%,那么如果只关注全天的这个利用率数据就会掩盖一些问题。
往往利用率的高位会导致资源饱和。利用率100%往往意味着系统有瓶颈,可以检查资源饱和度和系统性能加以确定。
该资源不能提供服务的程度被标识为它的饱和度,后文有资源饱和度的详细解释。
如果是检测的粒度比较大,那么很可能就会掩盖了偶尔的100%的峰值,一些资源,如磁盘,在60%的利用率的时候,性能就开始变差了。
响应时间(responsetime):也叫延迟,指操作执行所需要的耗时,它包括了等待时间和执行时间。
优化执行时间相对简单,优化等待时间则复杂多了,因为要考虑到各种其他任务的影响,以及资源的竞争使用。
对于一个数据库查询,响应时间就包括了从客户端发布查询命令到数据库处理查询,以及传输结果给客户端的所有时间。
延迟可以在不同的环节进行衡量,比如访问站点的装载时间,包括DNS延迟、TCP连接延迟和TCP数据传输时间。
延迟也可以在更高的级别进行理解,包括数据传输时间和其他时间,比如从用户点击链接到网页内容传输,并在用户的电脑屏幕上渲染完毕。
延迟是以时间做量度来衡量的,可以很方便地进行比较,其他的一些指标则不那么容易衡量和比较,比如IOPS,你可以将其转化为延迟来进行比较。
一般情况下,我们衡量性能主要是通过响应时间,而不是使用了多少资源,
优化本质上是在一定的负载下,尽可能地减少响应时间,而不是减少资源的占用,比如降低CPU的使用,资源的消耗只是一个现象,而不是我们优化的目标。
如果我们能够记录MySQL在各个环节所消耗的时间,那么我们就可以有针对性地进行调优,
如果我们可以将任务细分为一些子任务,那么我们就可以通过消除子任务、减少子任务的执行次数或让子任务执行得更有效率等多种手段来优化MySQL。
伸缩性(scalability):对于伸缩性,有两个层面的意思。
一是,在资源的利用率不断增加的情况下,响应时间和资源利用率之间的关系,当资源利用率升高时,响应时间仍然能够保持稳定,
那么我们就说它的伸缩性好,但是如果资源利用率一旦升高,响应时间就开始劣化,那么我们就认为其伸缩性不佳。
二是,伸缩性还有一层意义,表征系统不断扩展的能力,系统通过不断地增加节点或资源,处理不断增长的负载,同时依然能够保持合理的响应时间。
吞吐率(throughput):处理任务的速率。对于网络传输,吞吐率一般是指每秒传输的字节数,对于数据库来说,指的是每秒查询数(QPS)或每秒事务数。
并发(concurrency):指的是系统能够并行执行多个操作的能力。如果数据库能够充分利用CPU的多核能力,那么往往意味着它有更高的并发处理能力。
容量(capacity):容量指的是系统可以提供的处理负荷的能力。我们在日常运维中有一项很重要的工作就是容量规划,
即确保随着负荷的增长,我们的系统仍然能够处理负荷,确保服务良好和稳定。
容量也指我们的资源使用极限,比如我们的磁盘空间占用,在磁盘空间到达一定的阈值后,我们可能还要考虑扩容。
饱和(saturation):由于负荷过大,超过了某项资源的服务能力称为饱和。饱和度可以用等待队列的长度来加以衡量,或者用在队列里的等待时间加以衡量。
超过承载能力的工作往往处于等待队列之中或被返回错误,
比如CPU饱和度可用平均运行队列(runq-sz)来衡量,比如可以使用iostat命令输出的avgqu-sz指标衡量磁盘饱和度。
比如内存饱和度可以用交换分区的一些指标来衡量。
资源利用率高时,可能会出现饱和,图16-1是一个资源利用率、负载、饱和之间关系的说明图,
在资源利用率超过100%后,任务不能马上被处理,需要排队,饱和度就开始随着负载的增加线性增长。
饱和将导致性能问题,因为新的请求需要排队,需要时间进行等待。
饱和并不一定要在利用率100%的时候才会发生,它取决于资源操作的并行度。
饱和不一定能被发现,生产环境监控系统、监控脚本时存在一个容易犯的错误,那就是采样的粒度太粗,
比如每隔几分钟进行采样,可能就会发现不了问题, 但问题却会发生在短时间的几十秒内。
突然的利用率的高峰也很容易导致资源饱和,出现性能问题。
我们需要熟悉以上概念,并了解它们之间的关系,一般来说,随着负荷的上升,吞吐率也将上升,吞吐曲线开始时会一直是线性的,
我们的系统响应时间在开始的一个阶段会保持稳定,但是到达某个点后,性能就会开始变差,响应时间变得更长,
以后随着负荷的继续增加,此时我们的吞吐率将不能再继续增长,甚至还会下降,而响应时间也可能会变得不可接受。
有一种例外情况是,应用服务器返回错误状态码,比如Web服务器返回503错误,由于基本上不消耗资源,难以到达极限,所以返回错误码的吞吐曲线会保持线性。
对于性能的看法其实比较主观,一个性能指标是好还是坏,可能取决于研发人员和终端用户的期望值。
所以,如果我们要判断是否应该进行调优,那么我们需要对这些指标进行量化,当我们量化了指标,确定了性能目标时,这样的性能调优才更科学,才更容易被理解和沟通一致。
以下将简要叙述三个基础理论:阿姆达尔定律、通用扩展定律和排队论。

16.1.2 阿姆达尔定律
阿姆达尔定律(Amdahl’s law)是计算机科学界的一项经验法则,因IBM公司的计算机架构师吉恩·阿姆达尔而得名。
吉恩·阿姆达尔在1967年发表的论文中提出了这个重要定律。
阿姆达尔定律主要用于发现当系统的部分组件得到改进,整体系统可能得到的最大改进。
它经常用于并行计算领域,用来预测应用多个处理器时理论上的最大加速比。
在性能调优领域,我们利用此定律有助于我们解决或缓解性能瓶颈问题。
阿姆达尔定律的模型阐释了我们在现实生产中串行资源争用时候的现象。
图16-2分别展示了线性扩展(linear scaling)和按阿姆达尔定律扩展的加速比 (speedup)。
在一个系统中,不可避免地会有一些资源必须要串行访问,这就限制了我们的加速比,
即使我们增加了并发数(横轴),但取得的效果并不理想,难以获得线性扩展的能力(图16-2中的直线)。
以下介绍中,系统、算法、程序都可以看作是优化的对象,笔者在此不会加以区分,它们都有串行的部分和可以并行的部分。
在并行计算中,使用多个处理器的程序的加速比受限制于程序串行部分的执行时间。
例如,如果一个程序使用一个CPU核执行需要20个小时,其中的部分代码只能串行,需要执行1个小时,其他19小时的代码执行可以并行,
那么,如果不考虑有多少CPU可用来并行执行程序,最小的执行时间也不会少于1个小时(串行工 作的部分),因此加速比被限制为最多20倍(20/1)。
加速比越高,证明优化效果越明显。
阿姆达尔定律可以用如下公式表示: S(n) = T(1)/T(n) = T(1)/T(1)[B+(1-B)/n] =1/[B+(1-B)/n]
其中,
S(n):固定负载下,理论上的加速比。
B:串行工作部分所占比例,取值范围为0~1。
n:并行线程数、并行处理节点个数。
以上公式具体说明如下。
加速比=没有改进前的算法耗时T(1)/改进后的算法耗时T(n)。
我们假定算法没有改进之前,执行总时间是1(假定为1个单元)。
那么改进后的算法,其时间应该是串行工作部分的耗时(B)加上并行部分的耗时(1-B)/n,由于并行部分可以在多个CPU核上执行,所以并行部分实际的执行时间是(1-B)/n
根据这个公式,如果并行线程数(我们可以理解为CPU处理器数量)趋于无穷,那么加速比将与系统的串行工作部分的比例成反比,
如果系统中有50%的代码需要串行执行,那么系统的最大加速比为2。
也就是说,为了提高系统的速度,仅增加CPU处理器的数量不一定能起到有效的作用,需要提高系统内可并行化的模块比重,
在此基础上合理增加并行处理器的数量,才能以最小的投入得到最大的加速比。
下面对阿姆达尔定律做进一步说明。阿姆达尔这个模型定义了固定负载下,某个算法的并行实现相对串行实现的加速比。
例如,某个算法有12%的操作是可以并行执行的,而剩下的88%的操作不能并行,那么阿姆达尔定律声明,最大加速比是1/(1-0.12)=1.136。
如上公式中的n趋向于无穷大,那么加速比S=1/B=1/(1- 0.12)。
再例如,对于某个算法,可以并行的比例是P,这部分并行的代码能够加速S倍(S可以理解成CPU核的个数,即新代码的执行时间为原来执行时间的1/S)。
如果此算法有30%的代码可以被并行加速,即P等于0.3,这部分代码可以被加速2倍,即S等于2。那么,使用阿姆达尔定律计算其整个算法的加速比如下。
1/[B+(1-B)/n]=1/[(1-0.3)+0.3/2]=1.176
以上公式和前一个公式是类似的,只是前一个公式的分母是用串行比例B来表示的。
再例如,某项任务,我们可以分解为4个步骤,P1、P2、P3、P4,执行耗时占总耗时百分比分别是11%、18%、23%和48%。
我们对它进行优化,P1不能优化,P2可以加速5倍,P3可以加速20倍,P4可以加速1.6倍。
那么改进后的执行时间计算如下。0.11/1+0.18/5+0.23/20+0.48/1.6=0.4575
总的加速比是1/0.4575=2.186。我们可以看到,虽然有些部分加速比有20倍,有些部分有5倍,但总的加速比并不高,略大于2,因为占时间比例最大的P4部分仅仅加速了1.6倍。
图16-3演示了并行工作部分的比例不同时的加速比曲线,
我们可以观察到,加速比受限制于串行工作部分的比例,当95%的代码都可以进行并行优化时,理论上的最大加速比会更高,但最高不会超过20倍。
阿姆达尔定律也用于指导CPU的可扩展设计。CPU的发展有两个方向,更快的CPU或更多的核。
目前看来发展的重心偏向了CPU的核数,随着技术的不断发展,CPU的核数在不断地增加,目前我们的数据库服务器配置四核、六核都已经比较常见了,
但有时我们会发现虽然拥有更多的核,当我们同时运行几个程序时, 只有少数几个线程处于工作中,其他的并未做什么工作。
实践当中,并行运行多个线程往往并不能显著地提升性能,程序往往并不能有效地利用多核。
在多核处理器中加速比是衡量并行程序性能的一个重要参数,能否有效降低串行计算部分的比例和降低交互开销决定了能否充分发挥多核的性能,
其中的关键在于:合理划分任务、减少核间通信。

16.1.3 通用扩展定律
可扩展性指的是,我们通过不断地增加节点来满足不断增长的负载需求,这样的一种能力。
可是,很多人提到了可扩展性,却没有给它一个清晰的定义和量化标准。
实际上,系统可扩展性是可以被量化的,如果你不能量化可扩展性,你就不能确保它能够满足需要。
USL(universal scalability law,通用扩展定律)就提供了一 种方式,让我们可以量化系统的可扩展性。
USL,即通用扩展定律,由尼尔·巩特尔博士提出,相对比阿姆达尔定律,USL增加了一个参数β表示“一致性延迟”(coherency delay)。
图16-4是它的模型图,纵轴表示容量,横轴表示并发数。
USL可以用如下公式进行定义:C(N)=N/[1+α((N-1)+βN(N-1))]
其中, C(N):容量。 0≤α,β<1。
α:Contention,争用的程度,由于等待或排队等待共享资源,将导致不能线性扩展。
β:Coherency,一致性延迟的程度,由于节点之间需要交互以使数据保持一致,因此会带来延迟。
为了维持数据的一致性,将导致系统性能恶化,即随着N的上升,系统吞吐率反而会下降。当这个值为0时,我们可以将其看作是阿姆达尔定律。
N:Concurrency,并发数,理想情况下是线性扩展的。如果是衡量软件的可扩展性,那么N可以是客户端/用户并发数,
在固定的硬件配置下(CPU数不变),不断增加客户端/用户,以获取性能吞吐模型,我们的压力测试软件,如LoadRunner、sysbench即为此类。
如果是衡量硬件的可扩展性,那么N可以是CPU的个数,我们不断增加CPU的个数,同时保持每颗CPU上的负载不变,
即如果每颗CPU施加100个用户的负载,每增加一颗CPU,就增加100个用户,那么一台32个CPU的机器,需要3200个用户的并发负载。
下面我们来看看图16-5到图16-8所示的4个图,对应在不同负载下容量(吞吐能力)的变化。
图16-5中,α=0、β=0,此时,随着负载的升高,系统吞吐是线性上升的,即我们所说的线性扩展,这是很理想化的一种情况,
每份投入必然会获得等值回报, 但很难无限进行下去,性能模型的前面部分可能会表现为线性扩展。
图16-6中,α>0、β=0,此时对于共享资源的争用将导致性能曲线不再线性增长。
图16-7中,α>>0、β=0。此时共享资源的争用大大增加,我们将看到一种“收益递减”的现象,即我们的持续投入资源(比如金钱)变大,但是所取得的收益都越 来越小。
图16-8中,α>>0、β>0。此时β参数开始影响我们的性能曲线,我们除了共享资源的争用,还需要应对系统内各个节点的通信、同步状态的开销。此时性能曲线 将会变差,回报趋向于负值。
USL应用很广,如压力测试工具结果分析,对磁盘阵列、SAN和多核处理器及某些类型的网络I/O建模,分析内存颠簸、高速缓存未命中导致的延时等场景。
由于它的应用范围很广,所以也称之为通用扩展定律。
USL一些具体的应用场景如下。
(1)模拟压力测试
以下例子,如图16-9所示,将不断增加虚拟用户(横轴表示的Virtual users),记录其吞吐率(纵轴表示的Throughput),然后通过绘制的图形得到性能吞吐的模型。
(2)检测错误的测量结果
有时我们进行测试,会发现我们的测量输出结果不符合模型,这时我们需要审视下,是否我们的测量方式存在问题或受到了其他因素的干扰?
需要找出是什么原因导致的非预期的行为。
(3)性能推断
如果扩展性很差,我们可以通过公式和图得知是α(对共享资源的争用)还是β(一致性延迟)应该承担更大的责任。
(4)性能诊断
USL公式虽然简单易用,但普通人也许无法从中找到解决问题的思路和方法。因为所有的信息都被浓缩为2个参数α和β。
然而,应用程序开发者和系统架构师可能依据这些信息,就能轻易地找到问题症结所在。
(5)对生产环境收集的性能数据进行分析
对生产环境的性能数据(图形)进行分析,可以让我们确定合适的工作负载(比如并发线程数、CPU个数)。
(6)“扩展区”(scalability zone)概念的应用
我们看下图16-10,我们绘制不同场景下的性能曲线,这些曲线定义了可扩展区域Async msging、Sync waiting、Syncthrashing。
程序的性能点图跨越了多个区域。
在超过15个并发的时候,性能曲线进入另外一个区域Sync waiting,扩展性变差,这是因为“同步排队”(synchronous queueing)的影响所导致的。

16.1.4 排队论
(1)排队论的历史
排队论(queueing theory)起源于20世纪初的电话通话。
Agner Krarup Erlang,一个在丹麦哥本哈根电话交换局工作的工程师,
通过研究人们打电话的方式,发明了人们需要等待多久的公式,厄朗发表了一篇著名的文章“自动电话交换中的概率理论的几个问题的解决”。
随着以后的发展,排队论成为数学中一门重要的学科,20世纪50年代初,大卫·坎达(David G.Kendall)对排队论做了系统的研究,使排队论得到了进一步的发展。
(2)定义
排队论也称为随机服务系统理论、排队理论,是数学运筹学的分支学科。它是研究服务系统中排队现象随机规律的学科。
排队论广泛应用于电信、交通工程、 计算机网络、生产、运输、库存等各项资源共享的随机服务系统和工厂、商店、办公室、医院等的设计。
排队是我们每个人都很熟悉的现象。因为为了得到某种服务必须排队。
有一类排队是有形的,例如在售票处等待买票的排队,加油站前汽车等待加油的排队等;
还有一类排队是无形的,例如电话交换机接到的电话呼叫信号的排队,等待计算机中心处理机处理的信息的排队等。
为了叙述的方便,排队者无论是人、物或信息,以后都统称为“顾客”。
服务者无论是人或事物,例如一台电子计算机也可以是排队系统中的服务者,以后都统称为“服务台”。
排队现象是我们不希望出现的现象,因为人在排队至少意味着是在浪费时间;物的排队则说明了物资的积压。但是排队现象却无法完全消失,这是一种随机现象。
顾客到达间隔时间的随机性和为顾客服务时间的随机性是排队现象产生的主要原因。
如果上述的两个时间都是固定的,那么我们就可以通过妥善安排来完全消除排队现象。
排队论是研究排队系统在不同的条件下(最主要的是顾客到达的随机规律和服务时间的随机规律)产生的排队现象的随机规律性。
也就是要建立反映这种随机性的数学模型。研究的最终目的是为了运用这些规律,对实际的排队系统的设计与运行做出最优的决策。
(3)排队论的一般模型
图16-11是排队论的一般模型图。
其中,服务台用于服务队列中的顾客,可以多个服务台并发工作。
图16-11中的排队系统,各个顾客从顾客源出发,随机地来到服务机构,按一定的排队规则等待服务,直到按一定的服务规则接受服务后离开排队系统。
对于一个服务系统来说,如果服务机构过小,以致不能满足要求服务的众多顾客的需要,那么就会产生拥挤现象而使服务质量降低。
因此,顾客总是希望服务机构越大越好,但是,如果服务机构过大,人力和物力方面的开支就会相应地增加,从而就会造成浪费,
因此研究排队模型的目的就是要在顾客需要和服务机构的规模之间进行权衡和决策,使其达到合理的平衡。
(4)理论归纳
在计算机领域,许多软硬件组件都可以模型化为排队系统。我们可以使用排队理论分析排队现象,分析队列的长度、等待时间、利用率等指标。
排队理论基于许多数学和统计理论,比如概率理论、随机过程理论、Erlang-C公式(Erlangs C formula)、李特尔法则。
以下简单介绍排队论的一些理论。详细的内容可参考NeilJ.Gunther的图书《The PracticalPerformance Analyst》。
李特尔法则(Little’s law)
李特尔法则可以用如下公式来表示。 L=λW
这个公式定义了一个系统中的平均访问请求数=平均到达速率×平均服务时间
比如,我们有一个系统,平均到达速率是10000次请求/s,每个请求需要花费0.05s来处理,即平均服务时间为0.05s,
那么根据李特尔法则,服务器在任何时刻都将承担10000×0.05=500个请求的业务处理。
如果过了一段时间,由于客户端流量的上升,并发的访问速率达到了20000次请求/s,这种情况下,我们该如何改进系统的性能呢?
根据李特尔法则,我们有如下两种方案。
1)提高服务器的并发处理能力,即20000×0.5=1000。
2)减少服务器的平均服务时间,即W=L/λ=500/20000=0.025s。
排队论表示法
我们可以用肯德尔表示法(Kendall’s notation)来对排队系统进行分类,肯德尔表示法可使用如下的简化形式: A/S/m
其中, A:到达的规则,即到达的时间间隔的分布,可能是随机的、确定型的或泊松分布等其他分布方式。
S:服务规则,即指服务时间的分布,可能是固定的或指数的等其他分布方式。
m:服务台个数,一个或多个。
表示顾客到达的间隔时间和服务时间的分布常用的约定符号分别如下。
M:指数分布,在概率论和统计学中,指数分布(ExponentialDistribution)是一种连续概率分布。
指数分布可以用来表示独立随机事件发生的时间间隔,比如旅客进机场的时间间隔、中文维基百科新条目出现的时间间隔,等等。
D:确定型(Deterministic)。
G:一般(General)服务时间的分布。
一些常见的排队系统模型具体如下。
M/M/1:表示顾客相继到达的间隔时间为指数分布、服务时间为指数分布、单服务台。
M/M/c:表示顾客相继到达的间隔时间为指数分布、服务时间为指数分布、多服务台。
M/G/1:表示顾客相继到达的间隔时间为指数分布、服务时间为一般服务时间分布、单服务台。
M/D/1:表示顾客相继到达的间隔时间为指数分布、服务时间为确定型时间分布、单服务台。比如我们的旋转磁盘可用此模型进行分析。

16.2 诊断工具
我们需要熟悉Linux下常用的诊断性能的工具,确切地说,
我们需要在平时不使用这些命令的时候,就能够熟练应用它,这样我们在实际诊断性能问题的时候,才可以快速使用它们,而不是事到临头才去学某个命令应该如何使用。
我们进行性能调优的首要目的是需要找到系统的瓶颈所在。最常见的瓶颈是内存、I/O或CPU。
Linux提供了一系列的工具来检查系统和查找瓶颈。一些工具揭示了系统的总体健康状态,一些工具则提供了特定的系统组件信息。
使用这些工具将是一个好的起点,有助于我们确定性能调优的方向。
现实中,真正出现性能问题时,往往会有许多现象发生,发现一个现象并不难,难的是定位问题的根源,是什么因素的影响最大。
当你具备了知识,能够熟练使用各种工具,了解数据库、操作系统、硬件等各种组件的机制,通过检查各种工具和命令输出的数据,你对找到问题的根源所在将会越来越有经验。
使用工具要避免只使用自己熟悉的工具,因为工具在不断地进化中,所以,如果有了更好的工具,那么花一些学习成本也是值得的。
对于性能的优化,我们往往有许多种工具可以选择,这也会造成一些困扰,因为不同工具的功能有重叠,甚至大部分都有重复,
这样不仅浪费了资源,也让用 户的学习成本变得更高,因为可能你要熟悉许多工具而不只是一种。

16.2.1 OS诊断工具
sar、vmstat、iostat都是工具包sysstat(thesystemmonitoring tool)里的命令,如果你的系统中没有这些命令,那么你需要安装sysstat包。
本节对sar会做比较详细的介绍,因为其他命令收集的信息与它类似,因此本节将不对其他命令做详细说明,仅仅列出一些需要关注的要点。
1.sar
sar(system activity reporter,系统活动情况报告)命令是系统维护的重要工具,主要用于帮助我们掌握系统资源的使用情况,可以从多方面对系统的活动进行报告,
报告内容包括:文件的读写情况、系统调用的使用情况、磁盘I/O、CPU效率、内存使用状况、进程活动及与IPC有关的活动等。
sar通过cron定时调用执行以收集和记录信息,默认情况下,Linux每10分钟运行一次sar命令来收集信息,
如果你认为时间跨度太长,不容易发现性能问题,你也可以更改调度任务的间隔,修改/etc/cron.d/sysstat即可。
cat sysstat
# run system activity accounting tool every 10 minutes
*/10 * * * * root /usr/lib64/sa/sa1 1 1
然后重启crond生效。
/etc/init.d/crond restart
sar命令的常用格式如下: sar [ options... ] [ <interval> [ <count> ] ]
sar如果不加参数,则默认是读取历史统计信息,你可以指定interval和count对当前的系统活动进行统计。
其中参数的具体说明如下:interval为采样间隔,count为采样次数,默认值是1。

[root@bogon sa]# sar --help
Usage: sar [ options ] [ <interval> [ <count> ] ]
Options are:
[ -A ] [ -B ] [ -b ] [ -C ] [ -d ] [ -F [ MOUNT ] ] [ -H ] [ -h ] [ -p ] [ -q ] [ -R ]
[ -r ] [ -S ] [ -t ] [ -u [ ALL ] ] [ -V ] [ -v ] [ -W ] [ -w ] [ -y ]
[ -I { <int> [,...] | SUM | ALL | XALL } ] [ -P { <cpu> [,...] | ALL } ]
[ -m { <keyword> [,...] | ALL } ] [ -n { <keyword> [,...] | ALL } ]
[ -j { ID | LABEL | PATH | UUID | ... } ]
[ -f [ <filename> ] | -o [ <filename> ] | -[0-9]+ ]
[ -i <interval> ] [ -s [ <hh:mm:ss> ] ] [ -e [ <hh:mm:ss> ] ]

options为命令行选项,sar命令常用的选项分别如下。
-A:所有报告的总和。
-B:输出内存使用情况的统计信息。
-u:输出CPU使用情况的统计信息。
-v:输出inode、文件和其他内核表的统计信息。
-d:输出每一个块设备的活动信息,一般添加选项-p以显示易读的设备名。
-r:输出内存和交换空间的统计信息。
-b:显示I/O和传送速率的统计信息。
-c:输出进程的统计信息,每秒创建的进程数。
-R:输出内存页面的统计信息。
-y:终端设备的活动情况。
-w:输出系统交换活动的信息,即每秒上下文切换次数。
如下是一些sar使用的例子。

(1)CPU资源监控
例如,每10s采样一次,连续采样3次,观察CPU的使用情况,并将采样结果以二进制的形式存入当前目录下的文件test中,
需要键入如下命令: sar -u -o test 10 3
输出项说明如下:
CPU:all表示统计信息为所有CPU的平均值。我们可以使用sar-P n查看某颗CPU。
%user:显示在用户级别运行和使用CPU总时间的百分比。
%nice:显示在用户级别,用于nice操作,所占用CPU总时间的百分比。
%system:在核心级别(kernel)运行所占用CPU总时间的百分比。
%iowait:显示用于等待I/O操作所占用CPU总时间的百分比。
%idle:显示CPU空闲时间所占用CPU总时间的百分比。
1)若%iowait的值过高,则表示硬盘存在I/O瓶颈。
2)若%idle的值很高但系统响应很慢时,有可能是CPU正在等待分配内存,此时应加大内存容量。
3)若%idle的值持续低于10,则系统的CPU处理能力相对较低,表明系统中最需要解决的资源是CPU。

(2)查看网络的统计
语法:sar -n KEYWORD。
KEYWORD常用的值及说明具体如下。
DEV:显示网络设备统计,如eth0、eth1等。
EDEV:显示为网络设备错误统计。
NFS:显示NFS客户端活动统计。
ALL:显示所有统计信息。
如下命令可查看网络设备的吞吐,数据每秒更新一次,总共更新5次。
[root@localhost ~]# sar -n DEV 1 5
输出项说明。
第一字段:时间。
IFACE:设备名。
rxpck/s:每秒收到的包。
txpck/s:每秒传输的包。
rxkB/s:每秒收到的所有包的体积。
txkB/s:每秒传输的所有包的体积。
rxcmp/s:每秒收到的数据切割压缩的包的总数。
txcmp/s:每秒传输的数据切割压缩的包的总数。
rxmcst/s:每秒收到的多点传送的包。
可以使用grep命令对输出进行过滤,命令如下:sar -n DEV 2 5 | grep eth0
如果想知道网络设备错误报告,也就是用来查看设备故障的,应该用EDEV命令,比如下面的例子:sar -n EDEV 2 5

(3)内存分页监控
例如,每10s采样一次,连续采样3次,监控内存分页,命令如下:sar -B 10 3
10:45:04 AM pgpgin/s pgpgout/s fault/s majflt/s
10:45:14 AM 606.19 3648.35 13893.21 0.00
10:45:24 AM 626.17 3726.67 525.97 0.00
10:45:34 AM 557.36 3734.53 1.50 0.00
Average: 596.60 3703.17 4810.10 0.00
输出项说明如下:
pgpgin/s:表示每秒从磁盘或SWAP置换到内存的字节数(KB)。
pgpgout/s:表示每秒从内存置换到磁盘或SWAP的字节数(KB)。
fault/s:每秒钟系统产生的缺页数,即主缺页与次缺页之和(major+minor)。
majflt/s:每秒钟产生的主缺页数,这会导致将数据从磁盘加载到内存,因此需要留意。
pgfree/s:
pgscank/s:
pgscand/s:
pgsteal/s:
%vmeff:

(4)I/O和传送速率监控
例如,每10s采样一次,连续采样3次,需要键入如下命令:sar -b 10 3
输出项说明如下:
tps:每秒钟物理设备的I/O传输总量。
rtps:每秒钟从物理设备读入的数据总量。
wtps:每秒钟向物理设备写入的数据总量。
bread/s:每秒钟从物理设备读入的数据量,单位为块/s。
bwrtn/s:每秒钟向物理设备写入的数据量,单位为块/s。

(5)进程队列长度和平均负载状态监控
例如,每10s采样一次,连续采样3次,监控进程队列长度和平均负载状态,命令如下:sar -q 10 3
输出项说明如下:
runq-sz:运行队列的长度(等待运行的进程数)。
plist-sz:进程列表中进程(processes)和线程(threads)的数量。
ldavg-1:最后1分钟的系统平均负载(systemload average)。
ldavg-5:过去5分钟的系统平均负载。
ldavg-15:过去15分钟的系统平均负载。
blocked:

(6)系统交换活动信息监控
例如,每10s采样一次,连续采样3次,监控系统交换活动信息,命令如下:sar -W 10 3
输出项说明如下:
pswpin/s:每秒系统换入的交换页面(swap page)数量。
pswpout/s:每秒系统换出的交换页面数量。

(7)设备使用情况监控
例如,每10s采样一次,连续采样3次,报告设备使用情况,命令如下:sar -d 10 3 -p
其中,参数-p可以打印出sda、hdc等易读的磁盘设备名称,如果不使用参数-p,设备节点则有可能是dev8-0、dev22-0这样的形式。
输出项说明如下:
tps:每秒从物理磁盘I/O的次数。多个逻辑请求会被合并为一个I/O磁盘请求,一次传输的大小是不确定的。
rd_sec/s:每秒读扇区的次数。
wr_sec/s:每秒写扇区的次数。
avgrq-sz:发送到设备的请求的平均大小,单位为扇区。
avgqu-sz:磁盘请求队列的平均长度。
await:从请求磁盘操作到系统完成处理,每次请求的平均消耗时间,包括请求队列的等待时间,单位是毫秒。
svctm:系统处理每次请求的平均时间,不包括在请求队列中消耗的时间。
%util:I/O请求占CPU的百分比,比率越大,说明越饱和。
1)avgqu-sz的值较低时,设备的利用率较高。
2)当%util的值接近100%时,表示设备带宽已经占满。

(8)查看历史统计信息
有时我们希望能够看到历史性能统计信息,可以进入目录/var/log/sa,saXX文件是XX日的历史数据,
可以使用sar –f saXX 命令查看,例如, sar -f sa17 。默认将显示整天的数据。
我们可以加上-s选项指定特定时间段的数据,例如, sar -q -f sa17 -s 05:00:00 | head -n 10
以上命令将只显示17日5点之后的load的统计数据,且只显示最前面的10条记录。
05:00:01 AM runq-sz plist-sz ldavg-1 ldavg-5 ldavg-15 blocked
05:10:01 AM 0 174 0.00 0.01 0.05 0
05:20:01 AM 0 175 0.00 0.01 0.05 0
Average: 0 174 0.00 0.01 0.05 0
输出结果与“(5)进程队列长度和平均负载状态监控”一致

(9)输出inode、文件和其他内核表的统计信息
命令:sar -v 10 3
05:26:28 AM dentunusd file-nr inode-nr pty-nr
05:26:38 AM 85375 1152 37432 1
05:26:48 AM 85375 1152 37432 1
05:26:58 AM 85375 1152 37432 1
Average: 85375 1152 37432 1
输出项说明如下:
dentunusd:目录高速缓存中未被使用的条目数量。
file-nr:文件句柄(file handle)的使用数量。
inode-nr:索引节点句柄(inode handle)的使用数量。
要想判断系统的瓶颈问题,有时需要将几个sar命令选项结合起来。
怀疑CPU存在瓶颈,可用sar -u和sar -q等来查看。
怀疑内存存在瓶颈,可用sar -B、sar -r和sar -W等来查看。
怀疑I/O存在瓶颈,可用sar -b、sar -u和sar -d等来查看。

2.iostat
iostat是I/O statistics(输入/输出统计)的缩写,iostat工具将对系统的磁盘操作活动进行监视。
它的特点是汇报磁盘活动的统计情况,同时也将汇报出CPU的使用情况。
iostat有一个弱点,那就是它不能对某个进程进行深入分析,只能对系统的整体情况进行分析。
iostat的语法如下:iostat [ options... ] [ <interval> [ <count> ] ]
Usage: iostat [ options ] [ <interval> [ <count> ] ]
Options are:
[ -c ] [ -d ] [ -h ] [ -k | -m ] [ -N ] [ -t ] [ -V ] [ -x ] [ -y ] [ -z ]
[ -j { ID | LABEL | PATH | UUID | ... } ]
[ [ -T ] -g <group_name> ] [ -p [ <device> [,...] | ALL ] ]
[ <device> [...] | ALL ]

举例如下:
iostat -x sda 1
iostat -txm 10 3
参数及说明分别如下:
-t:打印汇报的时间。
-x:默认显示所有设备。
-m:统计信息显示每秒多少MB而不是默认的每秒多少块。
Device: rrqm/s wrqm/s r/s w/s rMB/s wMB/s avgrq-sz avgqu-sz await r_await w_await svctm %util
vda 0.00 0.00 0.01 0.02 0.00 0.00 34.80 0.00 29.88 52.71 23.03 5.34 0.02
dm-0 0.00 0.00 0.01 0.03 0.00 0.00 31.67 0.00 33.00 64.01 26.35 5.03 0.02
dm-1 0.00 0.00 0.00 0.00 0.00 0.00 49.78 0.00 14.17 14.17 0.00 13.30 0.00
dm-2 0.00 0.00 0.00 0.00 0.00 0.00 69.12 0.00 11.03 11.62 1.00 10.45 0.00
输出项解析如下:
avgqu-sz:对于在线OLTP业务,应该大概近似于MySQL的页块大小,如果你看到这个值远远大于你的MySQL实例页块(16KB),
那么可能存在一些其他的非数据库I/O负荷,或者你的数据库因为某些原因(如预读)导致检索数据的效率不高。
svctm:服务时间。
await:平均等待时间。 磁盘I/O请求包含服务时间(svctm)和等待时间(await),svctm一般小于10ms,我们要重点关注await。
%util:磁盘利用率。 在磁盘利用率达到100%的时候,意味着存在I/O瓶颈,这个时候,I/O达到饱和,此时的吞吐率我们可以用如下公式来衡量: (r/s+w/s)*svctm=%util
此时,吞吐率(r/s+w/s)就是svctm的倒数。注意,此公式仅在磁盘利用率达到100%的时候成立。

3.vmstat
vmstat这个工具提供了系统整体性能的报告,它能对进程、内存、页交换、I/O、中断及CPU使用情况进行统计并报告信息。
vmstat的输出类似如下。
vmstat 2 222
procs -----------memory-------------swap-- -----io---- --system-- -----cpu------
r b swpd free buff cache si so bi bo in cs us sy id wa st
1 0 0 27724752 1103508 26829932 0 0 0 26 0 0 3 0 96 0 0
2 0 0 27725012 1103508 26829940 0 0 0 136 7293 10504 5 0 95 0 0
1 0 0 27724244 1103508 26830532 0 0 0 4096 7620 11758 6 1 93 0 0
我们一般关注r、b、id及si、so。
(1)procs部分
r:表示当前有多少进程正在等待运行,如果r是连续的大于在系统中的CPU的个数,则表示系统现在运行得比较慢,有多数的进程正在等待CPU。
b:表示当前有多少进程被阻塞。
(2)memory部分
swpd、free、buff、cache这4项展示了内存是如何使用的,
swap列显示了swap被使用的数量,
free列显示了目前空闲的内存,
buffer列显示了buffer所使用的内 存,
cache列显示了page cache所使用的内存,cache越大,表明有越多的内存用于cache文件。
(3)swap部分
si:每秒从磁盘交换到swap的内存。
so:每秒从swap交换到磁盘的内存。
si、so正常情况下应该等于0,如果持续不为0,那么很可能存在性能问题。
(4)io部分
bi:从块设备读入数据的总量(读磁盘)。
bo:块设备写入数据的总量(写磁盘)。
bi、bo的变化反映了我们磁盘读取和写入的速率。
(5)system部分
in:每秒中断次数,包括时钟。
cs:每秒上下文切换次数。
(6)CPU部分
这些列展示了不同CPU完成不同任务的CPU时间百分比。
我们由此可以得知,CPU是否真正在做事,还是处于空闲或等待状态。
很高的sy值,表明可能有过多的 系统调用或系统调用效率不高。
us:运行非内核代码所花费的时间百分比,即进程在用户态使用的CPU时间百分比。
sy:运行内核代码所花费的时间百分比,即进程在系统态使用的CPU时间百分比。
id:空闲的CPU时间百分比。
wa:等待I/O的CPU时间百分比。
st:从虚拟机偷取的CPU时间百分比。

4.oprofile
oprofile是Linux内核支持的一种性能分析机制,是一套低开销的工具集合,简单易用,适合于在实际的系统中分析程序的性能瓶颈。
它可以工作在不同的体系结构上,包括MIPS、ARM、IA32、IA64和AMD。内核2.6的发行版一般都内置了这个工具。
通过oprofile这个工具,开发人员可以得知一个程序的瓶颈在哪里,进而指导代码优化。
oprofile的基本原理是进行抽样统计,处理器定时中断,oprofile可在这个时候记录哪些代码正在执行。
往往耗时更长的代码被取样的次数更多,通过这种方式,我们可以发现哪个可执行程序或哪个function消耗的CPU最多,进一步分析即可找到可供调优的代码片段。
系统的运行,往往就是在处理各种事件,比如CPU指令、磁盘I/O、网络包、系统调用、库调用、应用程序事务、数据库查询,等等。
性能分析往往和研究这些事件的统计有关,
例如每秒操作的次数、每秒传输的字节数、平均延时、有时重要的细节信息在统计中可能会被忽视掉,
这个时候对每类事件单独进行统计会更有助于你了解系统的行为。
oprofile支持两种采样(sampling)方式:基于事件的采样(eventbased)和基于时间的采样(timebased)。
基于事件的采样是oprofile只记录特定事件的发生次数。这种方式需要CPU内部有性能计数器(performacecounter)。
基于时间的采样是oprofile借助OS时钟中断的机制,每个时钟中断时oprofile都会记录一次(采一次样),
引入此种采样方式的目的在于提供对没有性能计数器的CPU的支持,
其精度相对于基于事件的采样要低,因为要借助OS时钟中断的支持,对禁用中断的代码oprofile不能对其进行分析。
虽然说基于事件的方法在理论上更精确,但在大部分简单的场景下,基于时间的方法也能工作得很好,
所以,如果你的CPU在基于事件的性能诊断中存在异常的情况,那么就使用基于时间的方法好了。
许多人不知道如何有效地使用oprofile来进行性能优化,这里将介绍一些基本的用法。
为了及时反映软硬件的发展,支持不同的CPU,oprofile版本更新得比较频繁,目前版本(截至2014年10月6日)是1.0.0版本。
由于主要的发行版内置的oprofile一般是较低的版本,因此如下所做的介绍,都将基于较低的版本0.9.4。
opcontrol --version
opcontrol: oprofile 0.9.4 compiled on Nov 22 2011 12:03:03
我的生产环境是RHEL 5.4,如果需要oprofile内核,则需要安装内核符号信息,命令如下:
rpm -i kernel-debuginfo-common-2.6.18-164.el5.x86_64.rpm
rpm -i kernel-debuginfo-2.6.18-164.el5.x86_64.rpm
安装好的vmlinux在这里/usr/lib/debug/lib/modules/2.6.18-164.el5/vmlinux
oprofile包含有一系列的工具集,这些工具默认在路径/usr/bin之下,工具及说明分别如下:
1)op_help:列出可用的事件,并带有简短的描述。
2)opcontrol:控制oprofile的数据收集。
2012年,oprofile 0.9.8开始引入operf工具,将替换旧的基于opcontrol的工具,允许非root用户也可以进行性能监测。
opcontrol的配置默认在/root/.oprofile/daemonrc下,可以使用demsg查看oprofile使用的是哪一种模式。
3)opreport:对结果进行统计输出。一般存在两种基本形式。
opreport -f
opreport -l ` which oprofiled` 2 > /dev/null | more
4)opannaotate:产生带注释的源文件/汇编文件,源语言级的注释需要在编译源文件时加上的调试符号信息的支持。
5)opgprof:产生与gprof相似的结果。
6)oparchive:将所有的原始数据文件收集打包,从而可以在另一台机器上进行分析。
7)opimport:将采样的数据库文件从另一种abi外部格式转化为本地格式。
基本步骤具体如下:
1)opcontrol --no-vmlinux #不对内核进行性能分析 或者opcontrol --vmlinux=/boot/vmlinux-`uname-r`#对内核进行性能分析
默认的配置存放在/root/.oprofile/daemonrc中
默认的采样的文件存放在/var/lib/oprofile/samples/中
也可以指定默认数据存放的地方,命令如下:
opcontrol --no-vmlinux --session-dir=/home/me/tmpsession
opcontrol --start --session-dir=/home/me/tmpsession
在开始收集采样数据前可回顾下我们的设置,运行opcontrol --status。
2)清除上一次采样到的数据。 opcontrol --reset
3)开始收集信息。opcontrol --start
4)运行程序,施加负荷。
5)dump出收集的数据,然后可以继续运行或关闭oprofile。 opcontrol --dump
我们可以随时运行命令opcontrol --reset清除我们当前会话的采样数据,重置计数器。
6)停止数据收集,且kill掉daemon进程。 opcontrol --shutdown
默认将数据放在/var/lib/oprofile/samples下,可以使用opcontrol--reset清理文件。
7)输出统计报告。 opreport -f [--session-dir=dir]
查看系统级别的报告:opreport --long-filenames。
查看模块级别的报告,opreport image:进程路径-l,命令如下。
opreport -l image:/bin/myprog,/bin/myprog2
opreport image:/usr/bin/*
查看源码级别的报告,opannotate image:进程路径-s。 输出的报告类似于图16-12的形式。
其中,第一列是收集的采样数据的统计次数,第二列是耗费的时间百分比,第三列是进程名。
oprofile还可以观测事件列表命令如下。 opcontrol --list-events
如下是一个完整的示例。
opcontrol --start --no-vmlinux --separate=kernel #启动收集 程序运行中,此时 oprofile收集数据
opcontrol --status #显示状态
opcontrol -h #关闭 oprofile
opreport -f | more #显示报告
opreport image: /usr/local/mysql-5.1.58-linux-x86_64-glibc23/bin/mysqld
opreport -l image: /usr/local/mysql-5.1.58-linux-x86_64-glibc23/bin/mysqld | more
注意事项 :
不建议在虚拟机里利用oprofile来测试性能。
调试的内核最好是原生内核。
使用oprofile定位CPU密集型的场景是合适的,但对于某些I/O密集型或是低负载类型的场景就会有些无能为力,这时可以借助其他工具进一步定位性能瓶颈。

5.free
free命令用于显示系统的自由内存和已经被使用的内存。
free指令显示的内存的使用情况包括实体内存、虚拟的交换文件内存、共享内存区段及系统核心使用的缓冲区等。
语法:free [-bkmotV] [-s]
参数及说明分别如下:
-b:以Byte为单位显示内存使用情况。
-k:以KB为单位显示内存使用情况。
-m:以MB为单位显示内存使用情况。
-o:不显示缓冲区调节列。
-s:持续观察内存使用状况。
-t:显示内存总和列。
-V:显示版本信息。
如下是free命令的输出:
total used free shared buffers cached
Mem: 65966584 65787112 179472 0 443532 15532932
-/+ buffers/cache: 49810648 16155936
Swap: 16779884 316 16779568
[root@bogon sa]# free
total used free shared buff/cache available
Mem: 8057824 357596 7198732 17448 501496 7601420
Swap: 4063228 0 4063228
其中各项说明如下:
Mem:表示物理内存统计。
-/+buffers/cache:表示物理内存的缓存统计。
Swap:表示硬盘上交换分区的使用情况,这里我们不去关心。
系统的总物理内存:65966584(64GB),但系统当前真正可用的内存大小并不是第一行free标记的179472KB,它仅代表未被分配的内存。
以下我们将逐行解释输出:
第1行 Mem
total:表示物理内存总量。 total=used+free
used:表示总计分配给缓存(包含buffers与cache)使用的数量,但其中可能有部分缓存并未实际使用。
free:未被分配的内存。
shared:共享内存。
buffers:系统已分配但未被使用的buffers数量。
cached:系统已分配但未被使用的cache数量。
buffer指的是作为buffer cache的内存,即块设备的读写缓冲区。
cache指的是作为page cache的内存,即文件系统的cache。
如果cache的值很大,则说明cache住的文件数很多。如果频繁访问到的文件都能被cache住,那么磁盘的读I/O必定会非常小。
但是过大的文件cache可能会影响到内存的使用效率,导致操作系统上其他进程的内存不够大,甚至还会使用到swap空间。
第2行 -/+buffers/cache
used:也就是第一行中的used-buffers-cached,也是实际使用的内存总量。
free:未被使用的buffers与cache和未被分配的内存之和(见第一行buffers、cached、free),这就是系统当前实际可用的内存。
第2行所指的是从应用程序的角度来看,对应用程序来讲,buffers/cache是等同可用的,当程序使用内存时,buffers/cache会很快地被使用。
从应用程序的角度来说,可用内存=系统free memory+buffers+cached。
第1行Mem是对操作系统来讲的。buffers/cache都是属于被使用的,所以它认为free只有179472。
我们一般理解的free输出应该从应用程序的角度去理解,应该关注第二行的free输出,也就是16155936KB。因为那些buffers和cache是可能被重用的。

6.top
能够实时显示系统中各个进程的资源占用状况,类似于Windows的任务管理器。它不断更新最新情况直至用户结束程序。
默认情况下,可列出消耗CPU资源最多的十多个进程。
你也可以交互式地键入不同的键按其他选项进行排序,比如
按【M】键可列出占用最多内存的几个进程,
按【1】键可切换显示各CPU的使用率或整体使用率等,
按【K】键可终止某个进程,
按空格键可重新刷新屏幕输出。
以下是一个系统运行top命令的例子。
top - 17:10:08 up 497 days, 40 min, 1 user, load average: 0.19, 0.26, 0.26
Tasks: 433 total, 2 running, 430 sleeping, 0 stopped, 1 zombie
Cpu(s): 0.5%us, 0.2%sy, 0.0%ni, 99.1%id, 0.2%wa, 0.0%hi, 0.1%si, 0.0%st
Mem: 65966584k total, 65791212k used, 175372k free, 444868k buffers
Swap: 16779884k total, 316k used, 16779568k free, 15532032k cached
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
19462 mysql 15 0 29.4g 29g 9996 S 16.2 46.4 111787:00 /usr/local/mysql/bin/mysqld --defaults-file=/my.cnf
24901 nemo 15 0 13024 1368 812 R 0.3 0.0 0:00.16 top -c
1 root 15 0 10368 640 544 S 0.0 0.0 5:12.51 init [3]
2 root RT -5 0 0 0 S 0.0 0.0 1:39.38 [migration/0]
3 root 34 19 0 0 0 S 0.0 0.0 50:46.99 [ksoftirqd/0]
4 root RT -5 0 0 0 S 0.0 0.0 0:00.02 [watchdog/0]

第一列显示的是当前时间、系统运行时间(up time)、使用者(users)数目和平均负载(load average)。
可以按 l 键切换是否显示。
top - 17:10:08 up 497 days, 40 min, 1 user, load average: 0.19, 0.26, 0.26
平均负载的三个数值分别表示在平均过去1分钟、5分钟和15分钟,可运行或不可中断状态的进程数目。
平均负载为1.0表示一个CPU被占用所有时间。如果计算 机有多个CPU,则平均负载的参考值亦会成倍数增长。
例如一个双CPU 4核的计算机,所有CPU所有时间被完全占用时的平均负载应该为1.0×2×4=8.0。

第二列显示任务(task)信息,任务表示一个进程或一个多线程进程中的某个线程,
任务信息包括任务总数、运行中(running)、睡眠中(sleeping)、已停止 (stopped)和不能运行(zombie)的进程数目,zombie就是僵尸进程。
可以按【t】键切换和下一列CPU状态列是否一同显示。
Tasks: 433 total, 2 running, 430 sleeping, 0 stopped, 1 zombie

第三列显示CPU状态,包括以下信息。
us(user):用户空间(user space)占用CPU的百分比。
sy(system):核心空间(kernelspace)占用CPU的百分比。
ni(nice):nice值比一般值0大(优先序较低)的进程占用CPU的百分比。
id(idle):CPU空闲时间百分比。
wa(iowait):CPU等待的百分比。当值过高时(如超过30%),表示系统的存储或网络I/O性能存在问题。
hi(H/W Interrupt):CPU处理硬件中断时间的百分比。除非光驱不断检查是否有光盘外,此值一般不会太高。
si(S/W Interrupt):CPU处理软件中断时间的百分比,此值一般不会太高。
st(Steal):在如Xen等的虚拟环境下,CPU运作虚拟机器时间的百分比。太高,则表示可能需要停止一些虚拟机器。
Cpu(s): 0.5%us, 0.2%sy, 0.0%ni, 99.1%id, 0.2%wa, 0.0%hi, 0.1%si, 0.0%st

第四列和第五列分别显示内存和交换空间(swap space)的使用率。
可以按【M】键切换是否显示。
Mem: 65966584k total, 65791212k used, 175372k free, 444868k buffers
Swap: 16779884k total, 316k used, 16779568k free, 15532032k cached
其他一些示例如下。
默认情况下,top是交互式(interactive)的输出,会一直在屏幕上刷新,如果我们需要获取top的输出,那么我们可以使用批处理模式。
例如如下示例。
top -b -d 5 -n 5
其中各参数及说明分别如下。
-b:批处理模式操作。
-d:刷新时间间隔。
-n:交互次数,即输出几次。
如下例子可指定某个或某几个进程的top输出:top -p 4360,4358
如下例子可指定某个用户的top输出:top -u garychen

7.dstat
对比其他工具,dstat更强大,可观察性也更强,dstat可综合显示各种系统资源的使用情况,如磁盘、网络、CPU、内存等。
以下是运行dstat的一个示例。
dstat 2 10
You did not select any stats, using -cdngy by default.
----total-cpu-usage---- -dsk/total- -net/total- ---paging-- ---system--
usr sys idl wai hiq siq| read writ| recv send| in out | int csw
4 0 95 0 0 0|2269B 641k| 0 0 | 0 0 |2867 5261
12 0 87 0 0 0| 0 134k| 552k 3655k| 0 0 |6787 11k
19 0 80 0 0 0| 0 1096k| 608k 3724k| 0 0 |6924 12k
6 0 94 0 0 0| 0 1182k| 501k 3246k| 0 0 |6351 8553
15 1 84 0 0 0| 0 206k| 576k 4241k| 0 0 |6800 12k
21 1 78 0 0 0| 0 504k| 597k 6403k| 0 0 |7053 14k
12 0 87 0 0 0| 0 304k| 461k 5518k| 0 0 |6229 9607
18 1 81 0 0 0| 0 328k| 511k 6113k| 0 0 |6651 12k
16 1 83 0 0 0| 0 266k| 580k 6359k| 0 0 |7116 14k
16 0 83 0 0 0| 0 188k| 673k 7052k| 0 0 |7452 11k
14 0 86 0 0 0| 0 144k| 532k 6051k| 0 0 |6557 8724

[root@bogon sa]# dstat 2 10
You did not select any stats, using -cdngy by default.
----total-cpu-usage---- -dsk/total- -net/total- ---paging-- ---system--
usr sys idl wai hiq siq| read writ| recv send| in out | int csw
0 0 100 0 0 0| 264B 301B| 0 0 | 0 0 | 92 142
0 0 100 0 0 0| 0 0 | 858B 490B| 0 0 | 98 142
0 0 100 0 0 0| 0 0 |2085B 511B| 0 0 | 179 212
0 0 100 0 0 0| 0 0 | 806B 186B| 0 0 | 131 186
0 0 100 0 0 0| 0 0 |1080B 338B| 0 0 | 102 147
0 0 100 0 0 0| 0 0 |1436B 338B| 0 0 | 129 171
0 0 100 0 0 0| 0 0 |1807B 338B| 0 0 | 131 169
0 0 100 0 0 0| 0 0 |1856B 338B| 0 0 | 139 176
0 0 100 0 0 0| 0 0 |1343B 338B| 0 0 | 116 156
0 0 100 0 0 0| 0 0 | 952B 338B| 0 0 | 96 144
0 0 100 0 0 0| 0 0 |1364B 338B| 0 0 | 112 147

上面输出中total-cpu-usage部分的hiq、siq分别为硬中断和软中断的次数。
上面输出中system部分的int、csw分别为系统的中断(interrupt)次数和上下文切换(context switch)。
若要将结果输出到CSV文件可以加--output filename。
我们可以使用绘图工具对此文件进行画图。
如果内核支持,还可以使用--top-io-adv参数查看最消耗I/O的进程。

[root@bogon sa]# dstat --help
Usage: dstat [-afv] [options..] [delay [count]]
Versatile tool for generating system resource statistics
Dstat options:
-c, --cpu enable cpu stats
-C 0,3,total include cpu0, cpu3 and total
-d, --disk enable disk stats
-D total,hda include hda and total
-g, --page enable page stats
-i, --int enable interrupt stats
-I 5,eth2 include int5 and interrupt used by eth2
-l, --load enable load stats
-m, --mem enable memory stats
-n, --net enable network stats
-N eth1,total include eth1 and total
-p, --proc enable process stats
-r, --io enable io stats (I/O requests completed)
-s, --swap enable swap stats
-S swap1,total include swap1 and total
-t, --time enable time/date output
-T, --epoch enable time counter (seconds since epoch)
-y, --sys enable system stats

--aio enable aio stats
--fs, --filesystem enable fs stats
--ipc enable ipc stats
--lock enable lock stats
--raw enable raw stats
--socket enable socket stats
--tcp enable tcp stats
--udp enable udp stats
--unix enable unix stats
--vm enable vm stats

--plugin-name enable plugins by plugin name (see manual)
--list list all available plugins

-a, --all equals -cdngy (default)
-f, --full automatically expand -C, -D, -I, -N and -S lists
-v, --vmstat equals -pmgdsc -D total

--bits force bits for values expressed in bytes
--float force float values on screen
--integer force integer values on screen

--bw, --blackonwhite change colors for white background terminal
--nocolor disable colors (implies --noupdate)
--noheaders disable repetitive headers
--noupdate disable intermediate updates
--output file write CSV output to file
--profile show profiling statistics when exiting dstat

delay is the delay in seconds between each update (default: 1)
count is the number of updates to display before exiting (default: unlimited)

8.netstat
netstat命令用于显示各种网络相关的信息。
常见的参数及说明分别如下:
-a:all,显示所有选项,默认不显示LISTEN相关。
-t:tcp,仅显示TCP相关选项。
-u:udp,仅显示UDP相关选项。
-n:不显示别名,能显示为数字的全部转化成数字。
-l:仅列出有正在Listen(监听)的服务状态。
-p:显示建立相关链接的程序名。
-r:显示路由信息,路由表。
-e:显示扩展信息,例如uid等。
-s:按各个协议进行统计。
-c:每隔一个固定时间,执行该netstat命令。
下面将列举几个示例进行说明。
我们可以使用netstat -tlpn显示当前正在监听TCP协议端口的MySQL服务。
以下命令将每隔一秒输出一次网络信息,检测MySQL服务是否已经起来并监听端口了。
netstat -tlpnc |grep mysql
以下命令将查看连接到MySQL服务端口的IP信息,并按连接数进行排序。
netstat -nat | grep ":3306" |awk '{print $5}'|awk -F: '{print $1}'|sort|uniq -c|sort -nr|head -20

[root@bogon sa]# netstat --help
usage: netstat [-vWeenNcCF] [<Af>] -r netstat {-V|--version|-h|--help}
netstat [-vWnNcaeol] [<Socket> ...]
netstat { [-vWeenNac] -I[<Iface>] | [-veenNac] -i | [-cnNe] -M | -s [-6tuw] } [delay]

-r, --route display routing table
-I, --interfaces=<Iface> display interface table for <Iface>
-i, --interfaces display interface table
-g, --groups display multicast group memberships
-s, --statistics display networking statistics (like SNMP)
-M, --masquerade display masqueraded connections

-v, --verbose be verbose
-W, --wide don't truncate IP addresses
-n, --numeric don't resolve names
--numeric-hosts don't resolve host names
--numeric-ports don't resolve port names
--numeric-users don't resolve user names
-N, --symbolic resolve hardware names
-e, --extend display other/more information
-p, --programs display PID/Program name for sockets
-o, --timers display timers
-c, --continuous continuous listing

-l, --listening display listening server sockets
-a, --all display all sockets (default: connected)
-F, --fib display Forwarding Information Base (default)
-C, --cache display routing cache instead of FIB
-Z, --context display SELinux security context for sockets

<Socket>={-t|--tcp} {-u|--udp} {-U|--udplite} {-S|--sctp} {-w|--raw}
{-x|--unix} --ax25 --ipx --netrom
<AF>=Use '-6|-4' or '-A <af>' or '--<af>'; default: inet
List of possible address families (which support routing):
inet (DARPA Internet) inet6 (IPv6) ax25 (AMPR AX.25)
netrom (AMPR NET/ROM) ipx (Novell IPX) ddp (Appletalk DDP)
x25 (CCITT X.25)

9.mtr
mtr是一个功能强大的网络诊断工具,综合了ping和tracerotue的功能。它可以帮助系统管理员诊断网络异常,并提供友好的网络状态报告以供分析。
以下将介绍mtr的数据是如何产生的,如何解读mtr的报告,以及如何诊断异常。
mtr使用“ICMP”包来测试网络争用和传输,mtr的工作原理是:启动mtr时,它通过发送不断增长TTL(生存时间)的ICMP包来测试本机和目标主机的连通性。
TTL 控制了ICMP包要经过多少“跳”才会被返回,
例如,主叫方首先发出TTL=1的ICMP数据包,第一个路由器将TTL减1得0后就不再继续转发此数据包,
而是返回一个 ICMP超时报文,主叫方从超时报文中即可提取出数据包所经过的第一个网关地址。
然后又发出一个TTL=2的ICMP数据包,可获得第二个网关地址,依次递增TTL便获取了沿途所有网关的地址。
mtr连续发送不断增长TTL(生存时间)的ICMP包以收集中间路由器的信息,包括各种连接、响应能力、状态等信息,
这就允许mtr能够打印出中间路由器的响应率和响应时间直到目标主机。
丢包率或响应时间的突然上升可能意味着这个网络存在问题。
我们可以把mtr看作一个单向的衡量网络质量的工具,从本机到目的主机和从目的主机到本机往往走的是不一样的网络路径,
本机到目的主机没有丢包,但目的主机到本机却可能会丢包。
从本地不同的主机到目的主机也可能走不一样的网络路径。
所以,如果诊断出是网络问题,那么建议在两个方向上都使用mtr进行测试验证,如果可以,多验证一些主机之间的通信。
示例如下:mtr --report www.baidu.com
添加--report表示发送10个包到目的主机www.baidu.com,然后生成报告。
如果不加--report选项,那么mtr会一直在交互模式中运行,交互模式会不断刷新报告以反映最新的信息。
一般情况下,--report选项生成的报告就已经足够了。
如何阅读报告。 如下是一个对baidu的mtr报告。
[root@bogon sa]# mtr --report www.baidu.com
Start: Tue Dec 17 20:51:31 2019
HOST: bogon Loss% Snt Last Avg Best Wrst StDev
1.|-- bogon 0.0% 10 0.2 0.3 0.2 0.7 0.0
2.|-- 1.202.223.97 0.0% 10 1.9 41.7 1.9 103.7 40.5
3.|-- ??? 100.0 10 0.0 0.0 0.0 0.0 0.0
4.|-- 220.181.0.234 70.0% 10 1.8 7.2 1.8 17.4 8.9
5.|-- 220.181.16.62 20.0% 10 3.6 3.7 2.9 5.7 0.7
6.|-- 220.181.182.26 0.0% 10 2.2 2.8 2.0 6.9 1.4
7.|-- ??? 100.0 10 0.0 0.0 0.0 0.0 0.0
8.|-- ??? 100.0 10 0.0 0.0 0.0 0.0 0.0
9.|-- ??? 100.0 10 0.0 0.0 0.0 0.0 0.0
10.|-- 220.181.38.150 0.0% 10 1.9 2.0 1.9 2.2 0.0
如上的mtr经过了10跳(hops),一般我们使用术语“跳”的计数来标识报告中的问题,“跳”指的是因特网上网络包到达目的地所经过的节点和路由器。
输出项说明如下:
loss%:每一跳的丢包率。
Snt:发送的数据包数量。
Last:最后一个包的响应时间(毫秒)。
Avg:所有包的平均响应时间(毫秒),一般情况下,我们更关注这个响应时间。
Best:最短的响应时间(毫秒)。
Wrst:最长的响应时间(毫秒)。
StDev:响应时间的标准差。StDev越大,表示各个包的响应时间差异越大。
如果差异很大,我们可能需要审视下Avg的平均值是否可靠,看看Best和Wrst,确认 Avg是否能够代表真实的延时时间,是否有被其他的因素所干扰。
一般情况下,我们可以把以上的输出报告分解为三个部分,前几跳往往是本地ISP,最后的两三跳往往是目的主机的ISP,中间的一些跳是网络传输经过的一些路由器。
对于本地ISP和目的主机ISP的网络问题,我们往往可以及时反馈,得到处理,但对于一些中间结点的路由器出现的问题,往往ISP自身也无力去解决。
下面我们通过4个例子来说明如何分析报告。
示例1: 当我们阅读报告时,我们一般关注的是丢包率和延时。
如果我们在任何一跳看到有超过一定百分比的延时(比如,超过5%),那么那个路由器就可能存在问题,
但也存在这样一种情况,一些ISP限制了ICMP包,我们看到了很高的丢包率,但实际上并没有发生丢包,
我们看到第2跳的丢包率高达50%,但实际上并没有丢包。
判断是否真正丢包,可以看下后续的跳,如果后续的跳显示没有丢包,那么就没有丢包,而是因为ISP限制了ICMP的速率。
示例2: ICMP速率限制和丢包可能会同时出现,这个时候,丢包率应该选择后续的跳中最低的丢包率,这个最低的丢包率才代表实际的丢包率。
示例3: 有时目的主机的不正确配置也会导致丢包,比如目的主机有防火墙Drop掉了ICMP包,我们可以看到最后一跳是100%的丢包率。
示例4: 有时路由器出于某种原因没有正确配置,或者是对其有特殊设置,例如下面的例子中,我们可能会看到许多问号,但网络质量良好,并没有发生丢包。
注意事项:
跨IDC的网络,本身就不是很稳定,特别是超长距离的横跨太平洋、大西洋的网络,中间经过的节点很多,很可能会出现波动或堵塞,导致延时很高,
如果网络丢包率不是很高(比如大于10%),那么你不需要过分地关注,
从架构上进行设计,让用户尽可能地访问本地的网络,提高用户体验,比去解决跨IDC的高延时和不稳定的网络会更具实践性。
网络的质量也和本地的连接、负载有关系,所以测量的时候也要留意这些因素的影响。

10.strace
strace是一个简单易用的工具,用于跟踪一个进程的系统调用或信号产生的情况,它最简单的用法就是跟踪一个可执行文件的执行,记录程序运行过程中的系统调用。
通过使用参数-c,它还能对进程中所有的系统调用做一个统计分析。它也能筛选出特定的系统调用,以下是一些示例。
1)查找程序启动的时候加载了哪些配置文件。
$ strace php 2>&1 | grep php.ini 可以查看加载了哪个 php.ini文件。
如果想只筛选某个系统调用则可以使用如下命令。 $ strace -e open php 2>&1 | grep php.ini
2)有时程序没有权限打开文件,它并不会提示你详细的信息,这时我们就可以用strace来判断是否存在权限的问题。
$ strace -e open,access 2>&1 | grep your-filename
3)对于一些很消耗资源的进程,我们有时会想知道它们正在做什么?知道了pid后,可以使用-p参数来查看。 $ strace -p 1344
4)加-c参数进行统计分析,可以查看哪些操作占据了最多资源。
$ strace -c -p 1344 监视一段时间后,按【Ctrl+C】键退出,会输出统计报表供你分析。
% time seconds usecs/call calls errors syscall
------ ----------- ----------- --------- --------- ----------------
96.49 0.014377 55 261 37 futex
1.30 0.000194 13 15 epoll_pwait
1.13 0.000169 15 11 write
0.79 0.000117 39 3 pselect6
0.15 0.000023 4 6 3 read
0.13 0.000020 20 1 1 restart_syscall
------ ----------- ----------- --------- --------- ----------------
100.00 0.014900 297 41 total

[root@bogon sa]# strace -h
usage: strace [-CdffhiqrtttTvVwxxy] [-I n] [-e expr]...
[-a column] [-o file] [-s strsize] [-P path]...
-p pid... / [-D] [-E var=val]... [-u username] PROG [ARGS]
or: strace -c[dfw] [-I n] [-e expr]... [-O overhead] [-S sortby]
-p pid... / [-D] [-E var=val]... [-u username] PROG [ARGS]

Output format:
-a column alignment COLUMN for printing syscall results (default 40)
-i print instruction pointer at time of syscall
-o file send trace output to FILE instead of stderr
-q suppress messages about attaching, detaching, etc.
-r print relative timestamp
-s strsize limit length of print strings to STRSIZE chars (default 32)
-t print absolute timestamp
-tt print absolute timestamp with usecs
-T print time spent in each syscall
-x print non-ascii strings in hex
-xx print all strings in hex
-y print paths associated with file descriptor arguments
-yy print protocol specific information associated with socket file descriptors

Statistics:
-c count time, calls, and errors for each syscall and report summary
-C like -c but also print regular output
-O overhead set overhead for tracing syscalls to OVERHEAD usecs
-S sortby sort syscall counts by: time, calls, name, nothing (default time)
-w summarise syscall latency (default is system time)

Filtering:
-e expr a qualifying expression: option=[!]all or option=[!]val1[,val2]...
options: trace, abbrev, verbose, raw, signal, read, write
-P path trace accesses to path

Tracing:
-b execve detach on execve syscall
-D run tracer process as a detached grandchild, not as parent
-f follow forks
-ff follow forks with output into separate files
-I interruptible
1: no signals are blocked
2: fatal signals are blocked while decoding syscall (default)
3: fatal signals are always blocked (default if '-o FILE PROG')
4: fatal signals and SIGTSTP (^Z) are always blocked
(useful to make 'strace -o FILE PROG' not stop on ^Z)

Startup:
-E var remove var from the environment for command
-E var=val put var=val in the environment for command
-p pid trace process with process id PID, may be repeated
-u username run command as username handling setuid and/or setgid

Miscellaneous:
-d enable debug output to stderr
-v verbose mode: print unabbreviated argv, stat, termios, etc. args
-h print help message
-V print version


16.2.2 MySQL诊断工具
1.MySQL自带工具
MySQL自带了一些工具,大都是为管理的目的而发布的一些工具,如mysql、mysqladmin、mysqldump,具体的使用方法,请参考前面的章节。
如果我们需要获得更全面的信息,进行更准确的诊断,那么更好的选择是使用一些第三方工具,如Percona工具包。
一般我们可以通过如下几种方式来收集信息。
通过使用mysql、mysqladmin执行一些查询和命令来获取当前的全局状态变量。
查询information_schema库下面的表,information_schema库保存了一些数据库的元信息,如连接信息。
MySQL 5.5版本后新增了performance_schema库,这个库下的动态性能视图主要用于收集MySQL的性能信息,比如锁、事件等信息。
基于事件的调优是一个方向,未来performance_schema会越来越成熟,将成为很重要的调优手段。

2.Percona工具包详解
(1)介绍和安装
Percona工具包是Percona公司的一个性能诊断工具集,由于MySQL自带的性能诊断工具很匮乏,所以很多时候需要借助第三方的工具来协助诊断和定位问题,
作 为一名DBA,有必要熟练使用Percona工具包里的部分工具,以便更有效地管理数据库和诊断问题。
以下将简要介绍Percona的安装方式和一些常用的工具。
本书所介绍的内容是基于Percona tool kit 2.1的版本。由于开源工具发展得比较快,可能这本书中所举的例子已不再适用。
由于是第三方工具,虽然有Percona公司的支持,许多工具也经过了大量用户的验证,但是仍然可能存在一些风险因素。
强烈建议在使用以下所介绍的工具之 前,仔细阅读官方文档,并经过自己的测试,验证其确实是可行的,才投入到生产环境中使用。
我们可以下载源码包、二进制包或RPM包进行安装。
你需要确保Perl已经安装了模块DBI和DBD::mysql。可以很容易地下载到单独的工具,下载命令如下。
wget percona.com/get/TOOL(工具名 )
以下以RHEL 5.4 64位为例简要说明如何安装Percona工具。
1) root下
[root@db1000 pkgs]# perl --version
his is perl, v5.8.8 built for x86_64-linux-thread-multi
cpan> install Time::HiRes
2) 确认 perl已经安装了 DBI,DBD::mysql, Time::HiRes
3) 安装
percona-toolkit下载源码包,解压之,进入解压缩目录
perl Makefile.PL
make
make test
make install 默认安装在 /usr/bin/ 下
ls -lrt /usr/bin/pt-*
Percona工具也有其配置文件,配置文件的语法简单直接,配置文件的规则具体如下。
每行的格式可以是:option=value或option,等于号两边的空格被忽略。
--表示选项解析结束,后面的行都是程序的附加参数。
忽略空行。
空格#‘空格’+‘#’表示后面的是注释内容。
Percona工具读取配置文件的顺序具体如下。
1)/etc/percona-toolkit/percona-toolkit.conf:全局配置。
2)/etc/percona-toolkit/TOOL.conf:可以指定某个工具的具体配置,TOOL是工具名,如pt-query-digest
3)$HOME/.percona-toolkit.conf:这是用户下的一个全局配置。
4)$HOME/.TOOL.conf:这是用户下的某个工具的具体配置。
也可以在命令行中指定配置文件,如--config/path/to/file,注意不要有=号,必须把--config参数放在命令行的最前面。--config表示不读取任何配置文件。
Percona工具包一般支持使用DSN语法来设置如何去连接MySQL。
DSN的英文全称是DATA SOURCE NAME,一个DSN是一个逗号分隔的key=value形式的字符串,例如:h=host1,P=3306,u=bob。
DSN对于大小写敏感,中间不允许有空格,如果有空格的话,必须要用引号引起来。
也有部分工具不支持DSN的方式连接数据库,它们自身提供了连接数据库的参数,如:“--host”、“--user”、“--password”。
一些工具可同时使用DSN和“--host”、“--user”、“--password”之类的参数。
一些标准的key及其说明分别如下:
A:字符集。 例如,A=utf8表示在连接时SET NAMES UTF8
D:数据库名。
F:设置mysql客户端库读取的配置文件,如果不进行设置,那么就读取标准配置文件,如/etc/my.cnf、$HOME/.my.cnf。
h:主机名或IP。
p:密码。
P:端口。
S:socket file。
u:数据库账号。
有些工具还会附加一些其他的key,这里就不赘述了。
可以通过设置环境变量PTDEBUG=1,启用Percona工具的Debug功能。命令的Debug信息会输出到STDERR,
例如,输出Debug信息到一个文件,命令如下: PTDEBUG=1 pt-table-checksum ... > FILE 2>&1

(2)pt-query-digest
pt-query-digest是最应该被掌握的一个工具。它可以分析MySQL的各种日志,如慢查询日志、generel日志,也可以分析SHOW PROCESSLIST的输出。
配合tcpdump 我们还可以对线上数据库流量进行采样,实时监控数据库流量,及时发现性能问题。
其基本用法如下:pt-query-digest /path/to/slow.log > /path/to/keep/report_file
如果你有大量的数据库节点,可以考虑把pt-query-digest的分析报告写入数据库,以方便检索和绘图。
输出的结果报表类似如下,以下截取了报告的部分内容。
# Overall: 565 total, 22 unique, 0.00 QPS, 0.00x concurrency _____________
# Time range: 2012-09-22 18:33:43 to 2012-10-16 10:45:31
# Attribute total min max avg 95% stddev median
# ============ ======= ======= ======= ======= ======= ======= =======
# Exec time 1233s 503ms 15s 2s 7s 2s 1s
# Lock time 53ms 31us 145us 94us 119us 17us 93us
# Rows sent 1.67k 0 20 3.02 9.83 4.12 0.99
# Rows examine 616.77M 72.90k 12.03M 1.09M 6.61M 2.02M 245.21k
# Query size 139.49k 25 381 252.81 346.17 70.94 234.30
# Profile
# Rank Query ID Response time Calls R/Call Apdx V/M Item
# ==== ================= ============= ==== ========== ==== ====== ========
# 1 0xBE5D289C750F172A 308.6929 25.0% 40 7.7173 0.00 0.08 SELECT ccc tbl_eee tbl_ddd bbb
# 2 0x5C898C5E065DD204 149.4144 12.1% 105 1.4230 0.50 0.00 SELECT tbl_ddd_info tbl_eee tbl_ddd
# 3 0x6F05415421300718 136.7381 11.1% 97 1.4097 0.50 0.00 SELECT tbl_ddd_info tbl_eee tbl_ddd
# 4 0x2E9AE41A4D2149A1 123.0681 10.0% 22 5.5940 0.00 0.02 SELECT ccc tbl_eee tbl_ddd bbb
# 5 0xAFF556BC27138443 121.9603 9.9% 73 1.6707 0.50 0.00 SELECT tbl_ddd_info tbl_eee tbl_ddd
# 6 0xD07F224EF598BD9A 105.0456 8.5% 16 6.5653 0.00 0.23 SELECT ccc tbl_eee tbl_ddd bbb
# 7 0xC22F9709F846BB4E 99.1936 8.0% 73 1.3588 0.50 0.00 SELECT tbl_ddd_info tbl_eee tbl_ddd
# 8 0x4CAD792BF4A54CE9 53.7477 4.4% 4 13.4369 0.00 0.17 SELECT tbl_fff tbl_eee tbl_ddd
# 9 0x347319A37AC29893 39.1390 3.2% 69 0.5672 1.00 0.00 SELECT tbl_fff pt_game_base_score
# 10 0x7EF77B274F1C37D3 27.2826 2.2% 4 6.8207 0.00 0.00 SELECT ccc tbl_eee tbl_ddd bbb
# 11 0x8383B2CB219358F3 16.7553 1.4% 18 0.9308 0.97 0.00 SELECT tbl_iii tbl_hhh tbl_eee
# MISC 0xMISC 51.5793 4.2% 44 1.1723 NS 0.0 <11 ITEMS>
# Query 1: 0.00 QPS, 0.00x concurrency, ID 0xBE5D289C750F172A at byte 120071
# This item is included in the report because it matches --limit.
# Scores: Apdex = 0.00 [1.0]*, V/M = 0.08
# Query_time sparkline: | ^_|
# Time range: 2012-09-25 11:01:09 to 2012-10-16 10:14:31
# Attribute pct total min max avg 95% stddev median
# ============== ======= ======= ====== ====== ====== ======= ======
# Count 7 40
# Exec time 25 309s 7s 10s 8s 9s 802ms 7s
# Lock time 6 4ms 59us 110us 89us 103us 12us 91us
# Rows sent 2 40 1 1 1 1 0 1
# Rows examine 45 281.88M 6.73M 7.29M 7.05M 6.94M 183.33k 6.94M
# Query size 5 7.19k 184 184 184 184 0 184
# String:
# Hosts
# Users db_user
# Query_time distribution
# 1us # 10us # 100us # 1ms # 10ms # 100ms # 1s
################################################################
# 10s+ #
# Tables
# SHOW TABLE STATUS LIKE 'ccc'G
# SHOW CREATE TABLE `tbl_eee`G
# SHOW TABLE STATUS LIKE 'tbl_ddd'G
# SHOW CREATE TABLE `bbb`G
# SHOW TABLE STATUS LIKE 'ggg'G
# EXPLAIN /*!50100 PARTITIONS*/
select ... from ....
输出格式的解释具体如下:
Rank:所有查询日志分析完毕后,此查询的排序。
Query ID:查询的标识字符串。可以搜索这个字符串以快速定位到慢查询语句。
Responsetime:总的响应时间,以及总占比,应优化占比较高的查询,对于比例较小的查询一般可以忽略,不进行优化。
Calls:查询被调用执行的次数。
R/Call:每次执行的平均响应时间。
Apdx:应用程序的性能指数得分,响应时间越长,得分越低。
V/M:响应时间的方差均值比。可说明样本的分散程度,这个值越大,往往是越值得考虑优化的对象。
Item:查询的简单显示,包含了查询涉及的表。
对于报告中的如下输出,我们可以利用偏移量到慢查询日志里定位具体的sql语句。
# Query 1: 0.00 QPS, 0.00x concurrency, ID 0xBE5D289C750F172A at byte 120071
定位方法如下:tail -c +120071 /path/to/slow.log. | head
查询响应时间的分布,这里使用了很形象的表示方式,如下所示。
# Query_time distribution # 1us # 10us # 100us # 1ms # 10ms# 100ms # 1s # 10s+ #
可以看到,有许多查询响应时间在1秒到10秒之间。
对于一些TOP SQL,我们可以使用EXPLAIN工具分析执行计划,进行调优。
对于更新语句,此工具会帮你改写成可以使用EXPLAIN工具的SELECT语句。
其他的一些使用方式示例如下。
1)分析SHOW PROCESSLIST的输出。
./pt-query-digest --processlist S=/path/3307/mysql.sock,u=root,p=dxwd* --interval 5 --run-time 5m
pt-query-digest --processlist h=host1 --print --no-report
2)分析tcpdump的输出。
分析执行SQL的频率,一般在高峰期取样,一定要记得关闭tcpdump,因为生成的文件可能会很大。
首先运行命令。 nohup tcpdump -i eth1 port 3306 -s 65535 -x -nn -q -tttt > dbxx_sql_new.log &
过一段时间后,如1分钟,终止tcpdump任务。
然后使用pt-query-digest进行分析。
./pt-query-digest --type=tcpdump --watch-server 12.12.12.12:3306 dbxx_sql_new.log > report_to_develper.rtf
对生产环境的采样可以采取如上的方法,比如每分钟抓取5秒的网络包,然后把分析结果入库。
利用监控系统及时发现问题,通知DBA或研发人员线上的性能问题。
3)把分析过的SQL记录到历史信息表中,就可以知道分析的SQL最后出现的时间,如果已经解决掉了,就不用再优化了。具体步骤如下。
第一步,在存放优化信息的数据库中创建一个用户用于存放信息。
GRANT CREATE,SELECT,INSERT,UPDATE,DELETE ON ptool.* to ptool@'%' IDENTIFIED BY 'ptool';
第二步,执行如下命令分析慢查询日志。
/home/mysql/scripts/pt-query-digest --create-review-table --reviewh=13.13.13.13,P=3305,u=ptool,p=ptool,D=ptool,t=query_review --create-review-history-table -- review-history h=13.13.13.13,P=3305,u=ptool,p=ptool,D=ptool,t=query_review_history --report-all /path/to/log3307/slowquery.log
以上命令如果是第一次运行,则会将信息存储到指定的表中,以后再次运行时,如果表中的某条SQL的reviewed_by列已有设定值,那么此工具就不会显示标记了的SQL。
如果要显示所有SQL,那么需要使用选项--report-all。
如果有--report-all和query_review表(表中记录的SQL及你添加的意见等信息),那么生成的报告里将带有你检查过的SQL的意见,这点会很有用。
第三步,可以运行如下命令查询Top SQL。
SELECT * FROM `query_review_history` WHERE ts_max > '2012-09-20 00:00:00'ORDER BY ts_cnt DESC , query_time_sum DESC LIMIT 3;
可以按照checksum(数据表里的是十进制的显示,报告里的是十六进制的显示方式)去数据表中查询对应的SQL,记录自己的优化意见,命令如下。
SELECT * FROM `query_review` WHERE CHECKSUM=0xB76366269B6B4973;
4)报告不记录到历史信息表中,只记录简单的信息。
/home/mysql/scripts/pt-query-digest --create-review-table --review h=13.13.13.13,P=3305,u=ptool,p=ptool,D=test,t=query_review /path/to/log3307/slowquery.log
存放信息的表需要我们手动建立,或者添加选项--create-review-table。每次重新运行以上命令时,都会重新更新表内的值。比如最早、最近出现的时间。
5)使用pt-query-digest分析通用日志和二进制日志。
分析通用日志示例如下。
pt-query-digest --type genlog general.log > /tmp/xxx.log
分析二进制日志示例如下。
pt-query-digest --type binlog --group-by fingerprint --limit "100%" --order-by "Query_time:cnt" --output report --report-format profile /tmp/xxx.log
注意:/tmp/xxx.log日志是文本形式的二进制日志。

(3)pt-stalk
pt-stalk的语法格式如下:Usage: pt-stalk [OPTIONS]
即使我们有了完备的性能收集程序,对于一些突然的性能波动也仍然会难以捕捉,如果是偶发性的性能问题,几天才发生一次,
那么持续地对系统收集大量信息不仅会显得没有必要而且还会耗费太多资源。
pt-stalk这个工具有助于解决此类问题,它可以在一定的条件下被触发,用于收集系统信息。
我们可以查询SHOW GLOBAL STATUS命令结果绘制的图形,查看是否某个变量发生了突变,然后选择这个变量做一个启动pt-stalk的条件,收集足够的信息。
比较好的一个衡量的阈值是 Threads_running,
比如,我们可以将某个生产环境Threads_running的阈值设置为20,pt-stalk每秒监控status的变量Threads_running,如果连续5秒都超过20,那么就可以开始收集统计信息了。
注意,触发的阈值不能设置得太高,因为会导致不能发现故障,或者导致故障发生的真正原因已经过去,当然,阈值也不能太低,因为可能会误报警。
pt-stalk是一个后台程序,默认我们可以通过文件/var/log/pt-stalk.log,查看pt-stalk的运行状态。
如下命令将检查pt-stalk的日志。 tail -f /var/log/pt-stalk.log
2013_07_09_10_24_04 Check results: status(Threads_running)=55, matched=yes, cycles_true=1
2013_07_09_10_25_03 Check results: status(Threads_running)=51, matched=yes, cycles_true=1
2013_07_09_10_26_04 Check results: status(Threads_running)=44, matched=yes, cycles_true=1
2013_07_09_10_28_04 Check results: status(Threads_running)=62, matched=yes, cycles_true=1
2013_07_09_10_28_05 Check results: status(Threads_running)=57, matched=yes, cycles_true=2
2013_07_09_10_29_03 Check results: status(Threads_running)=46, matched=yes, cycles_true=1
2013_07_09_10_29_04 Check results: status(Threads_running)=56, matched=yes, cycles_true=2
pt-stalk将收集的数据默认放在目录/var/lib/pt-stalk下,你可以使用参数--dest指定你希望存放数据的目录。
你还可以使用--notify-by-email参数指定邮件报警联系人。
如下示例中,pt-stalk运行在后台(--daemonize),监视SHOW GLOBAL STATUS中的Threads_running状态,如果Threads_running的值超过了64,就将状态信息记录到日志里。
pt-stalk每秒钟检测一次Threads_running值,如果连续5次满足触发条件,就开始收集主机和MySQL信息。
我们使用--dest指定存放数据的目录,
使用--disk-pct-free来定义磁盘的剩余空间阈值,如果剩余空间小于20%,则不再进行数据收集。
--iterations可限制收集数据的次数,默认情况下pt-stalk会永久执行。
--collect- tcpdump表示还要调用tcpdump收集网络包信息。
pt-stalk --pid /path/to/pt-stalk.pid --dest /path/to/data --disk-pct-free 20 --log /path/to/log/pt_stalk.log --collect-tcpdump --function status
--variable Threads_running --threshold 64
--iterations 2000 --notify-by-email=garychen@db110.com --daemonize --user=root --password=your_password -S /tmp/mysql.sock
除了常用的threads_running,我们还可以使用SHOW PROCESSLIST的输出值触发pt-stalk,
例如“--function processlist--variable State--match statistics--threshold 20”表示,show processlist输出中State列的值为statistics的线程数如果超过20则触发收集。
性能故障时刻,我们应该尽可能地收集操作系统和MySQL的信息,不仅要收集正在执行的任务信息,还要收集正在等待资源的信息,因为我们并不能确定是执行慢还是等待了太多资源。
这个工具还可以调用tcpdump来收集网络包信息,然后我们再调用pt-query-digest进行分析,命令如下。
tcpdump -r 2013_04_30_18_20_48-tcpdump -nn -x -q -tttt | pt-query-digest --type tcpdump --watch-server ip:port >report.rtf
其他的一些参数及说明如下:
--run-time:触发收集后,该参数将指定收集多长时间的数据。默认是30秒。
--sleep:为防止一直触发收集数据,该参数指定在某次触发后,必须sleep一段时间再继续观察并触发收集。默认是300秒。
--cycles:默认情况下pt-stalk只有连续5次观察到状态值满足触发条件时,才会触发收集。
有了数据之后,我们就可使用pt-sift对收集的数据进行分析,这个工具将帮助我们分析pt-stalk收集到的信息,它会自动下载其他需要用到的工具。

(4)pt-sift
pt-sift的语法格式如下:
pt-sift FILE│ PREFIX│ DIRECTORY
如果存在/var/lib/pt-stalk,则默认读取/var/lib/pt-stalk下的所有文件,否则读取当前目录下的文件。
如果是非默认目录,则请指定自己的工作目录,命令如下:./pt-sift /path/to/data
如果是指定文件名或前缀,则它会到默认目录里去查找。
例如如下命令:
./pt-sift /var/lib/pt-stalk/2012_09_07_00_00 #在默认目录 /var/lib/pt-stalk里查找 以 2012_09_07_00_00为前缀的文件分析。
./pt-sift /var/lib/pt-stalk/2012_09_07_00_00_13 #在默认目录里查找 以 2012_09_07_00_00_13为前缀的文件分析。
如下是pt-sift的一个输出,这里仅显示磁盘信息。

(5)pt-align
功能:将文件内容按列对齐。
语法:pt-align [FILES]
用途:pt-alian也可正确地处理空白字符(如空格、TAB),我们可以用它来格式化和vmstat、iostat的输出,移除一些不需要显示的内容。

(6)pt-archiver
语法:pt-archiver [OPTION...] --source DSN --where WHERE
用途:pt-archiver可将MySQL数据库中表的记录归档到另外一个表或文件中,也可以直接进行记录的删除操作。
工作原理 :pt-archiver工具能够智能地选择表上的索引,从源表中分批次找出符合WHERE条件的数据,
根据要求把表数据归档成csv格式或将表数据插入到归档表中,然后删除源表中的数据(默认删除)。
工作过程 :从源表SELECT数据,插入到新表或归档到文件,然后删除源表的数据。通过这种方式,保证只有归档成功了才删除源表的数据。
这个工具可用于迁移数据,可以减少对线上OLTP应用的影响,它可以小批量地把OLTP数据库上的数据导入OLAP数据库中。
也可以将它写入一个文件中,方便 使用LOAD DATA INFILE命令导入数据。我们还可以用它来实现增量的删除操作。
注意:默认归档数据的同时会删除生产库上的数据,因此在生产环境中使用时一定要慎重。
示例1:从OLTP数据库归档表tbl到OLAP数据库,并且归档到一个文件中。
pt-archiver --source h=oltp_server,D=test,t=tbl --dest h=olap_server --file '/var/log/archive/%Y-%m-%d-%D.%t' --where "1=1" --limit 1000 --commit-each
示例2:删除子表的孤立数据,这部分数据在父表中不存在关联的信息。
pt-archiver --source h=host,D=db,t=child --purge --where 'NOT EXISTS (SELECT * FROM parent WHERE col=child.col)'
示例3:将test库的userinfo表中id小于10000的记录归档到/home/mysql/tmp/userinfo_archive_20131010.log文件中。
pt-archiver --source h=host,D=test,t=userinfo --user=root --password=your_password --file '/home/mysql/tmp/userinfo_archive_20131010.log' --where "id<=10000" -- commit-each
参数说明:
--limit:控制导出数据归档的粒度,默认是1。
--commit-each:控制在每批次归档数据的时候提交。
--no-delete:不删除源表的数据。
--progress:每进行一次归档或删除,都显示所耗时间的信息,并可以据此预估总时间。
--statistics:结束的时候给出统计信息,包括开始的时间点、结束的时间点、查询的行数、归档的行数、删除的行数,以及各个阶段所消耗的总的时间和比例, 便于后续以此进行优化。
--columns:需要导出哪些列,列名用逗号隔开。
--sleep:在前后两次导出之间sleep(休息)的时间。避免给服务器造成较大的压力。如果同时设置了--commit-each选项,那么提交和刷新文件的操作应在sleep之前发生。
--primary-key-only:只选择主键列。对于删除表数据的场景,该选项可以避免取回整行数据,因此也更高效。
--ignore或--replace选项:使用insert ignore或replace语句可代替insert语句。

(7)pt-config-diff
语法:pt-config-diff [OPTION...] CONFIG CONFIG [CONFIG...]
功能:检查配置文件和服务器变量之间的差异。
DBA有时变更了MySQL全局变量,但忘记了同步修改配置文件,这样可能会导致隐患,因为有时重启后,又使用了旧的配置,
另外,这个工具可以用来在迁移或重新搭建环境的时候,确保新的迁移环境的配置和原来的生产环境的配置一致。
示例1:对比host1和host2的变量
pt-config-diff h=host1 h=host2
示例2:检查本地实例和本地配置文件。
pt-config-diff u=root,P=3306,S=/tmp/mysql.sock,p=password /etc/my.cnf
示例3:对比两个配置文件。
pt-config-diff /etc/my-small.cnf /etc/my-large.cnf

(8)pt-show-grants
语法:pt-show-grants [OPTION...] [DSN]
功能:导出权限的工具,方便我们在配置主从、迁移数据库的时候比对权限。
有时我们并不想通过导出导入系统库mysql来实现权限的配置,使用这个工具可以生成更友好的赋权语句,我们在目标库上直接执行即可。
利用这个工具也可以方便地回收权限。
示例1:导出权限。
pt-show-grants u=root,P=3306,S=/tmp/mysql.sock,p=password
示例2:查看每个用户的权限生成revoke收回权限的语句。
pt-show-grants --host='localhost' --user='root' --password='password' --revoke

(9)pt-summary
语法:pt-summary
功能:仅收集系统信息,它不是一个用于调优诊断的工具。
它可以生成一个友好的报告,可展示系统的平台、CPU、内存、磁盘、网络、文件系统、进程等各种信息,让你对基础环境有一个很好的概览。
pt-summary会运行许多命令去收集系统的状态和配置信息,先将这些信息保存到临时目录的文件中去,
然后运行一些Unix命令对这些结果做格式化,建议用root用户或有权限的用户运行此命令。
示例1:$ ./pt-summary

(10)pt-mysql-summary
语法:pt-mysql-summary [OPTIONS] [-- MYSQL OPTIONS]
其中,“--”后的参数是传递给MySQL的。
功能:对MySQL的配置和STATUS信息进行汇总,所生成的报告可以告诉我们,当前系统上存在哪些MySQL实例,主机的一般信息,
对SHOW PROCESSLIST的一些分析总结,以及变量的设置,并对STATUS状态变量进行采样,显示各种状态变量的增量变化。
例子1:将汇总本地MySQL服务器的信息。
pt-mysql-summary -- --user=root --password='password ' --socket=/tmp/mysql.sock
其他参数:
--sleep:采样GLOBAL STATUS时,间隔多少秒,默认是10秒。

(11)pt-fifo-split
功能:模拟切割文件并通过管道传递给先入先出队列(FIFO)而不用真正地切割文件。
当InnoDB使用load data的方式导入一个巨大的文件时,会创建一个很大的事务,产生很多UNDO,
如果异常回滚的话,会很耗时,可能会远远超过导入数据的时间,
所以更合理的方式是分批导入数据,那么如何在不切割数据文件的情况下达到分批导入数据的目的呢?使用Unix fifo即可。
例1: 如下是一个每次读取一百万行记录的范例。
pt-fifo-split --lines 1000000 hugefile.txt while [ -e /tmp/pt-fifo-split ]; do cat /tmp/pt-fifo-split; done
例2: 每次读取一百万行,指定fifo文件为/tmp/my-fifo,并使用LOAD DATA命令导入数据。
CREATE TABLE load_test ( col1 bigint(20) NOT NULL, col2 bigint(20) default NULL, key(col1), key(col2) ) ENGINE=InnoDB DEFAULT CHARSET=utf8
一个窗口: pt-fifo-split infile.txt --fifo /tmp/my-fifo --lines 1000000
另一个窗口:
while [ -e /tmp/my-fifo ]; do
mysql -e "set foreign_key_checks=0; set sql_log_bin=0; set unique_checks=0; load data local infile '/tmp/my-fifo' into table load_test fields terminated by ' ' lines terminated by ' ' (col1, col2);"
sleep 1;
done
如果在mysql命令提示符下使用LOAD DATA导入数据将会出现乱码,请设置SET character_set_database=字符集,或者修改LOAD DATA命令,添加character set语句,具体如下。
mysql -e "set foreign_key_checks=0; set sql_log_bin=0; set unique_checks=0; load data local infile '/home/mysql/db110.data' into table test.db110 character set gbk fields terminated by ' ' lines terminated by ' ' ;"

(12)pt-duplicate-key-checker
功能:查找重复的索引和外键。
这个工具会将重复的索引和外键都列出来,并生成删除重复索引的语句。
其原理是检查SHOW CREATE TABLE的输出,查找重复或冗余的信息。
冗余指的是索引的字段是其他索引的最左部分。
示例1:
./pt-duplicate-key-checker --user=root --password=password --socket=/tmp/mysql.sock

(13)pt-slave-delay
语法:pt-slave-delay [OPTION...] SLAVE-HOST [MASTER-HOST]
SLAVE-HOST [MASTER-HOST]可以使用DSN语法。
功能:设置从服务器滞后于主服务器的时间。
MySQL同步在快速的网络中是毫秒级的,如果有误操作,从库很快就变更了,对于一些频繁进行,不是经过严格测试的升级,可能会带来风险。
可考虑配置一个延迟复制的副本,以改善故障情况下的可恢复性。
MySQL5.6版本已经支持延迟复制,如果是5.1版本,可以用pt-slave-delay来设置延迟。
工作原理:通过启动和停止复制SQL线程来设置从服务器落后于主服务器的指定时间。
默认是基于从服务器上relay日志的二进制日志的位置来判断,因此不需要连接到主服务器。
检查主库传输过来的日志的位置信息(可以用SHOW SLAVE STATUS命令查看relay日志),对比本地已经应用的日志的位置信息,就能知道延迟的时间了。
如果IO线程不落后主服务器太多的话,这个检查方式就能工作得很好。一般是通过--delay和--delay加--interval来控制的。
--interval间隔多久检查一次,默认设置是1分钟检查一次,即每隔1分钟检查一次延迟,通过不断启动和关闭复制SQL线程来保持主从一直延迟固定的时间。
正常运行时,如果关闭了数据库,那么这个工具会每隔15秒重试一次连接,连续几次之后,如果还是不能连接,那么就会异常退出。
示例1:以下命令将以daemon模式在后台运行,从库将保持一直滞后主库1小时。
./pt-slave-delay u=xxxx,S=/tmp/mysql.sock,p=password --log /path/to/log/delay.log --daemonize
参数说明:
--delay:设置延迟时间,默认为1小时。
--interval:设置检查点频率,每次检查间隔多久,默认是1分钟(1m)。
--delay和--interval可选的时间允许使用不同的单位,如,s秒、m分、h小时、d天。
--log:日志,可以检查日志输出从而了解其原理和运行机制。
示例2:以下命令将运行这个工具10分钟(默认是永久运行的),从库保持一直滞后主库1分钟,每次检查间隔15秒,因此理论上是延迟1分钟15秒。
如果正在运行这个工具,而且这个工具已经停止了复制SQL线程,那么当我们按【Ctrl+C】退出这个工具时,它会友好地退出,意即它会启动复制SQL线程,并恢复现场。
连接MySQL的用户需要如下权限:PROCESS、REPLICATION CLIENT、SUPER。
如果启动出错,则会报错如下:“Had to create DBD::mysql::dr::imp_data_size unexpectedly”,这时建议升级Perl。
pt-slave-delay --delay 1m --interval 15s --run-time 10m slavehost

(14)pt-online-schema-change
语法:pt-online-schema-change [OPTIONS] DSN
长期以来困扰DBA的一个问题是,MySQL在线修改表结构的能力很弱,对于大表修改表结构将会很耗时,还会影响到服务。
为了减少对服务的影响,可能需要进行主从切换,或者选择特定的时间进行升级。
MySQL新的版本5.6和5.7增强了数据库在线修改表结构的功能,对于早期的版本,我们可以试试pt-online-schema-change这款工具。
这个工具的功能是实现在不锁表的情况下修改表结构。
这点对于在线应用很重要,这样在修改表结构的同时,数据库还可以继续提供读写服务。
工作原理 :创建一个和原表表结构一样的新表,新表为空数据,对新表进行表结构修改,
然后从原表中复制数据到新表,当数据复制完成以后就进行新旧表的切换,新表被命名为原表的名字,默认动作会将原表删除。
在复制数据的过程中,任何在原表中所做的更新操作都会更新到新表,因为这个工具会在原表上创建触发器,触发器会捕获原表上的更新,并将它们更新到新表。
上述过程中,原表复制数据到新表是分批分批复制记录到新表的,也有相关的参数可以控制负载,如--max-load。
示例1:向表sakila.actor中添加一个列。
pt-online-schema-change --alter "ADD COLUMN c1 INT" D=sakila,t=actor
示例2:更改表的引擎为InnoDB。
pt-online-schema-change --alter "ENGINE=InnoDB" D=sakila,t=actor
注意事项:
表需要有主键或唯一索引。
如果有外键,请仔细阅读官方文档。
需要确保原表中之前没有触发器。
利用此工具修改表结构,建议先进行备份。
切换新旧表的时候,会导致连接中断,需要确保应用中有重连的机制。
如果已经有长事务在操作这个表,那么这个工具可能会因为等不到锁而超时退出。
可能导致复制延时。
推荐使用独立表空间,以便释放空间。

(15)pt-kill
语法:pt-kill [OPTIONS] [DSN]
功能:kill掉满足某些条件的MySQL查询。
pt-kill获取SHOW PROCESSLIST的信息,对信息进行过滤,打印满足条件的连接,或者kill掉这些连接。
主要的目的是kill掉那些严重消耗资源的查询,以保障服务的正常运行。
pt-kill也可以检查运行SHOW PROCESSLIST命令的输出文件,打印满足条件的连接,这种情况下,不需要连接MySQL去kill掉连接。
参数说明:
--busy-time:匹配运行时间超过busy-time的查询。这些查询的SHOW PROCESSLIST输出的Command列为Query,Time列大于busy-time指定的值,才会被匹配。
--victims:指定哪些匹配的查询会被kill掉或被打印。
有如下三个值:oldes:默认值,kill掉运行时间最长的查询;all:kill掉所有查询;all-but-oldest:kill掉除了运行时间最长的查询之外的所有查询。
有时我们并发了许多同样的查询,这时我们只需要确保最长运行时间的那条查询能够执行成功即可,这种情况下,我们可以使用all-but-oldest选项。
--kill:kill匹配条件的连接。
--print:打印kill语句,并不会真正地kill掉连接。如果--kill和--print都指定了,那么不仅要kill掉匹配的连接,也要打印被kill掉的连接。
pt-kill命令kill连接需要4个步骤,了解这4个步骤,有助于你理解这个工具的使用,并能准确选择要kill掉的连接,4个步骤具体如下:
1)分组查询到不同的类别。--group-by选项可控制分组。默认情况下,这个选项没有值,所有查询都将被分组到一个默认的类中。
2)进行匹配。每个类别都要进行匹配。首先,查询会被不同的查询匹配选项过滤,如--match-user。然后,查询会被不同的类匹配选项过滤,如--query-count。
3)victim(kill候选者)选择。如果一些查询被过滤出来,那么它可以被kill掉,--victims控制哪些查询会被kill掉。
一般情况下,你可能会选择kill掉运行时间最长的查询,或者希望kill掉所有匹配到的查询。
4)对选择的查询执行操作。如kill、print。
示例1:kill运行时间超过60s的查询,默认情况下kill最长时间的查询,命令如下。
pt-kill --busy-time 60 --kill
示例2:仅仅打印运行时间超过60s的查询,而不是kill掉它们,命令如下。
pt-kill --busy-time 60 --print u=root,S=/tmp/mysql.sock,p=password
示例3:每隔10s检查一次sleep状态的所有连接,并kill掉它们,注意参数--victims all,命令如下。
pt-kill --match-command Sleep --kill --victims all --interval 10
示例4:打印所有的登录连接,命令如下。
pt-kill --match-state login --print --victims all
示例5:检查SHOW PROCESSLIST输出的文件,查看哪些连接匹配条件,命令如下。
mysql -e "SHOW PROCESSLIST" > proclist.txt
pt-kill --test-matching proclist.txt --busy-time 60 --print
示例6:kill掉运行时间超过120s且运行时间最长的那个连接,命令如下。
./pt-kill --busy-time 120 --match-command Query --match-db db_name --match-user user_name --kill --print u=root,S=/tmp/mysql.sock,p=password
--match-db和--match-user限定了数据库名和用户名,以免误操作。
示例7:kill掉运行时间超过120s的所有连接,命令如下。
./pt-kill --busy-time 120 --match-command Query --match-db db_name --match-user user_name --victims all --kill --print u=root,S=/tmp/mysql.sock,p=password
示例8:kill掉运行时间超过600s且正在创建临时表的所有查询,命令如下。
./pt-kill --busy-time 600 --match-command Query --match-db db_name --match-user user_name --match-state "Copying to tmp table" --victims all --kill

(16)pt-visual-explain
语法:pt-visual-explain [OPTION...] [FILE...]
功能:格式化EXPLAIN出来的执行计划,并按照Tree方式输出,以方便阅读。
示例1:
pt-visual-explain <file_containing_explain_output>
pt-visual-explain -c <file_containing_query>
mysql -e "explain select * from mysql.user" | pt-visual-explain

(17)pt-slave-restart
功能:检查MySQL的复制状态,处理指定的MySQL复制错误。
比较常用的使用方式是,忽略指定的MySQL错误号,重新启动复制SQL线程。
建议不要滥用这个工具,仅在你明确知道复制错误可以忽略的时候,才使用这个工具忽略掉复制错误。
如果错误太多导致了严重的数据不一致,那么建议重建整个从库。
语法:pt-slave-restart [OPTIONS] [DSN]
参数说明:
--verbose:可以显示复制错误。
--error-length:显示复制错误的长度。
--daemonize:后台模式。
--log:当是daemonize模式时,输出日志到这里。
--error-numbers:匹配了错误号才处理,错误代码代号之间使用逗号进行分隔,对应SHOW SLAVE STATUS输出里的last_errno。
--error-text:匹配了错误文本才处理,对应SHOW SLAVE STATUS输出里的last_error。
--run-time:运行多久才退出,默认以秒为单位,其他单位s=seconds、m=minutes、h=hours、d=days。
--skip-count:当重新启动salve时,应跳过多少条语句,默认值是1。
--sleep:每次检查复制状态的间隔休眠时间。
--until-master:重启salve,直到从库应用日志到指定的主库日志位置。
例子1::检查MySQL服务器的复制,跳过错误代码为1062的复制错误。
pt-slave-restart --verbose --error-numbers=1062 --run-time=60 u=root,S=/tmp/mysql.sock,p=password

(18)pt-diskstats
语法:pt-diskstats [OPTION...] [FILES] 按q可退出,按“?”显示帮助。
功能:pt-diskstats是一个交互式的监控系统I/O的工具,这个工具类似于iostat,但显示的信息更具可观察性,它也可以分析从其他机器收集来的数据。
其实现的原理是读取/proc/diskstats中的数据进行展示。
参数说明:
--interval:默认为1,设置对/proc/diskstats采用的间隔。
--iterations:运行多少次,默认情况下是永久运行。
示例1:pt-diskstats的一个输出
./pt-diskstats --interval=5
./pt-diskstats --interval=2 --iterations 10
输出项说明:
rd_s:每秒读的次数,此数值是每秒平均读次数,表征了每秒实际发送到底层物理设备的读请求的次数。
rd_avkb:每次读的平均大小,单位是KB(千字节)。
rd_mb_s:每秒读多少MB。
rd_mrg:请求在发送给底层实际物理设备前,被I/O调度合并的百分比。
rd_cnc:读操作的平均并发。
rd_rt:读操作的平均响应时间,以毫秒为单位。
wr_s、wr_avkb、wr_mb_s、wr_mrg、wr_cnc、wr_rt对应写操作,与rd_*相关的解释类似。
busy:对应iostat的%util。
in_prg:正在进行请求的数目。
ios_s:物理设备的平均吞吐量,即IOPS(每秒IO),它是rd_s和wr_s的总和。
qtime:平均排队时间,即请求在被发送到物理设备前,在调度队列里等待的时间。
stime:平均服务时间,即实际物理设备处理请求的平均时间。
注意块设备(block device)和物理设备(physical device)的区别:
块设备,如/dev/sda1,应用程序以文件系统的方式访问它,逻辑I/O发生在此。
物理设备指的是底层的实际物理设备,比如磁盘、RAID卡,物理I/O发生在此。
我们所说的队列指的是与块设备相关的队列,队列保存读写请求直到这些请求被实际发送给物理设备为止。

(19)pt-deadlock-logger
语法:pt-deadlock-logger [OPTIONS] DSN
功能:pt-deadlock-logger是一个死锁检测工具,适用于InnoDB引擎,可以提取和记录InnoDB的最近的死锁信息。
工作原理:检测死锁(通过SHOW ENGINE INNODB STATUSG;),然后直接打印死锁信息,或者指定--dest参数将信息存入test库下的一个表test.deadlocks中。
参数说明:
--iterations:迭代检查多少次,如 --iterations 4 --interval 30表示检测4次,每次间隔30s。
--run-time:运行此工具多久时间。这个参数的优先级比iterations更高,如--run-time 1m。
--interval:检测死锁的频率,默认是30s。
--dest:指定存储死锁信息的数据库表,需要预先创建表。
示例1:检测死锁,输出至屏幕,检测10次即可。
pt-deadlock-logger u=root,S=/tmp/mysql.sock,p=password --iterations 10
示例2:检测死锁,并把死锁信息存入test库的表中。
pt-deadlock-logger u=root,S=tmp/mysql.sock,p=password --dest D=test,t=deadlocks
示例3:检测死锁,并把死锁信息存入另外一个数据库中。
pt-deadlock-logger SOURCE_DSN --dest DEST_DSN,D=test,t=deadlocks
示例4:运行4个小时,每次间隔30秒,在后台运行,检查死锁信息中。
pt-deadlock-logger SOURCE_DSN --dest D=test,t=deadlocks --daemonize --run-time 4h --interval 30s
注意:频繁调用SHOW INNODB STATUS,也可能对生产系统产生影响,对于负载较重的MySQL数据库,建议每次间隔大于30s以上。

(20)pt-table-checksum
功能:pt-table-checksum这个工具的目的是在线检测MySQL的主从一致性。
数据库主从数据不一致的原因可能如下:
从库被误写了。
数据库主机宕机导致MyISAM表损坏。
数据库实例崩溃后,我们指定了不准确的日志点重新进行同步。
基于语句的复制。
一些Bug,特别是一些非核心的功能,比如存储过程,可能会导致复制出错,从而导致主从数据的不一致。
我们需要确保主从数据是一致的。有时我们在做了迁移或升级之后,也希望能有一个工具来确认主从数据的一致性。
工作原理:通过对比主从数据内容的CRC值来判断数据的一致性,
它可以分批次地校验数据,以减少对生产的影响,它把一张表分为若干个trunk,
如果一张表有 300万行,分为100个trunk,那么每个trunk就是有3万行,它会锁定这个trunk,进行CRC值的计算。
运行此工具时,如果有权限将会自动创建如下的表格。
CREATE TABLE checksums (
db char(64) NOT NULL, tbl char(64) NOT NULL, chunk int NOT NULL, chunk_time float NULL, chunk_index varchar(200) NULL,
lower_boundary text NULL, upper_boundary text NULL, this_crc char(40) NOT NULL, this_cnt int NOT NULL, master_crc char(40) NULL,
master_cnt int NULL, ts timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (db, tbl, chunk), INDEX ts_db_tbl (ts, db, tbl) ) ENGINE=InnoDB;
pt-table-checksum在主库上生成REPLACE INTO语句,然后通过复制传递到从库。
类似如下的语句,是基于语句级别的复制,将这条语句复制到从库,从库会执行这条语句,可以知道,从库上的这条记录保存了从库内容的checksum值。
checksum默认采用crc32值。
REPLACE INTO`percona`.`checksums` (db, tbl, chunk, chunk_index, lower_boundary, upper_boundary, this_cnt, this_crc)
SELECT 'db_name', 'table_name', '1', NULL, NULL, NULL,
COUNT(*) AS cnt, COALESCE(LOWER(CONV(BIT_XOR(CAST(CRC32(CONCAT_WS('#', `id`, `name`, CONCAT(ISNULL(`name`)))) AS UNSIGNED)), 10, 16)), 0) AS crc
FROM `percona`.`garychen`
以上的crc列,简单说明一下,它会把一个trunk里面的每一行数据的所有字段都拼成一个String,然后对String取32位校验码,
然后对这个trunk内所有计算好的校验码进行异或操作,从十进制转换成十六进制。
在主库中UPDATE更新master_src的值,运行命令类似如下的语句。
UPDATE `percona`.`checksums` SET chunk_time = '0.000563', master_crc = '31012777', master_cnt = '4'
WHERE db = 'db_name' AND tbl = 'table_name' AND chunk = '1'
这个操作,同样会被复制到从库。
由上述示例可以得知,通过检测从库上的this_src和master_src列的值可以判断复制是否一致。命令类似如下的语句。
SELECT db, tbl, SUM(this_cnt) AS total_rows, COUNT(*) AS chunks FROM percona.checksums
WHERE ( master_cnt <> this_cnt OR master_crc <> this_crc OR ISNULL(master_crc) <> ISNULL(this_crc)) GROUP BY db, tbl;
可以使用pt-table-checksum--explain查看工具是如何检查表的。
它可以检测到从库,并自动连接它们。自动连接从库有多种办法,可以使用--recursion-method来指定,
默认是PROCESSLIST,即通过检查SHOW PROCESSLIST 里的输出来判断从库,然后去连接对应的从库。
host方法是利用SHOW SLAVE HOSTS的输出信息判断从库,但这种方法在低版本中支持得不好。
你也可以建立一个表存储从库的信息,通过检索这个表来连接从库进行数据比对。
它仅针对一台服务器执行checksum操作,一次只针对一个表进行checksum操作。如果显示有很多表,那么--replicate-check-only可仅显示存在差异的表。
如果被完全终止了,可以使用--resume选项继续执行,你也可以使用Ctrl加C退出。
示例1:chunk的大小也是可以动态调整的,调整的依据是checksum操作在一定时间内可以完成。
./pt-table-checksum h=ip,P=3306,u=test_gary,p=password --databases=db_name --tables=tbl_name --recursion-method=processlist
选项说明:
--max-lag:最大延迟,超过这个就等待。
--max-load:最大负载,超过这个就等待。
--databases:只检查某些库。
--tables:只检查某些表。
注意事项:
主从数据库的schema应该一致,否则复制可能会失败。
使用的时候应选择在业务低峰期运行,因为运行的时候会造成表的部分记录被锁定。
虽然操作是对trunk逐个进行的,但是它会对每个trunk做SELECT FOR UPDATE,这样做主要是担心做checksum的时候会有写入,所以各个trunk都不适合太大。
pt-table-checksum提供了多种手段以确保尽量不会对生产环境造成影响,你可以使用--max-load来指定最大负载,如果达到最大负载,就暂停运行。
你也可以设置超时时间innodb_lock_wait_timeout。
如果发现有不一致的数据,则可以使用pt-table-sync工具来进行修复。
如果表中没有主键或唯一索引,或者没有合适的索引,或者处于其他不适合检查的情况下,那么工具可能会忽略这个表。

(21)pt-table-sync
语法:pt-table-sync [OPTION...] DSN [DSN...]
功能:进行数据表的同步。
工作原理:对比主从库之间的差异,在主库上执行数据的更改(使用REPLACE INTO语句或DELETE语句),再同步到从库上。
对于主库上存在,从库上不存在的数据,执行REPLACE INTO语句。
对于从库上存在,主库上不存在的数据,执行DELETE语句。
对于在主库、从库上同时存在,但数据不一致的情况,基于主库现在的数据执行更改操作(REPLACE INTO语句),所以REPLACE INTO语句不会更改主库上的数据。
这个工具会更改数据,所以如果需要使用这个数据在不同的MySQL库之间进行数据同步,那么建议先进行数据备份。
主从库实例可能会因为一些误操作或软硬件异常而导致数据出现不一致的问题,而使用这个工具可以修复主从库之间的数据差异。
使用这个工具修复主从库的不一致问题,必须先保证被修复的表上有主键 或唯一键。
参数说明:
--execute:执行变更。
--print:仅打印变更语句,可以把--execute参数换成--print先查看会变更什么数据。
--replicate:指定一个同步列表,--replicate指定的表里存储了需要同步的表的信息。
这个表实际上就是pt-table-checksum工具生成的校验信息,我们可以先利用pt-table-checksum工具进行校验,然后利用校验结果进行同步。
--sync-to-master:指定从库,同步到主库。
--ignore-databases:忽略同步的数据库列表,以逗号分隔。
--ignore-engines:忽略同步的引擎列表,以逗号分隔。
--ignore-tables:忽略同步的表,以逗号分隔。
示例1:我们假设host1是主库,host2是从库,端口为3306。
1)先使用pt-table-checksum进行校验,默认将校验结果存储在percona.checksums中。
./pt-table-checksum --user=user --password=password --host=host1 --port=port --databases=db_name --tables=tbl_name --recursion-method=processlist
2)根据校验结果,修复从库中的数据。
./pt-table-sync --execute --replicate percona.checksums --sync-to-master h=host2,P=3306,u=user,p=password
3)修复后,使用第步骤1)的语句重新校验一次。
注意:
使用pt-table-sync的风险比较大,对于生产环境,建议使用pt-table-checksum进行校验,如果有数据不一致的问题,则考虑重建从库;
如果要使用pt-table-sync进行数据同步,则建议仔细阅读官方文档,了解它的限制和可能产生的影响,以避免影响生产环境或修复数据不成功。

(22)pt-query-advisor
语法:pt-query-advisor /path/to/slow-query.log
功能:分析慢查询日志或generel日志,并给予建议。
输出的报告中包含了3种级别的提示,note级别、warn级别和critical级别。
note级别 :
应该使用“table as 别名”的方式,而不是“table 别名”的方式,因为使用as可读性更好。
别名不要和原来的表名一样。
INSERT INTO语句应该显式地指定列名,如INSERT INTO tbl(col1,col2)VALUES...。
日期/时间值需要用引号括起来。如WHERE col<'2012-02-12'
应该使用“<>”而不是“!=”。因为“!=”不标准。
warn级别:
参数尽量不使用前导通配符,比如 LIKE '%name' ,这样的方式是用不到索引的,MySQL的索引一般是前缀索引。
SELECT语句如果没有WHERE条件,则有可能会导致检索太多的记录。
多表查询GROUP BY或ORDER BY子句的列不在同一个表中会导致使用临时表(temporary table)和文件排序(filesort)。
不要使用SQL_CALC_FOUND_ROWS,这种方式检索数据效率很低,会需要检索所有的记录以确定记录的总数。
critical级别 :
不要混合ANSI标准的JOIN方式和MySQ中的JOIN语法,否则容易导致用户混淆。
可以使用ON或USING语句来指定一个简单的连接,被连接的列在ON或USING子句中列出,而WHERE子句中可以列出附加的选择条件。

(23)pt-mext
功能:用于查看SHOW GLOBAL STATUS信息,可以增量(-r选项)显示,逐列进行显示。
示例1:将每隔10秒执行一次SHOW GLOBAL STATUS,执行4次, 并将结果合并到一起查看。
pt-mext -r -- mysqladmin ext -uroot -p111111 -i 10 -c 4

(24)pt-upgrade
功能:用于在多个MySQL实例上执行查询,并比较查询结果和耗时。这个工具在进行数据库版本升级的时候会很有用。
语法:pt-upgrade [OPTION...] DSN [DSN...] [FILE]
示例1:查看慢查询在不同主机实例上的运行效果
pt-upgrade h=host1 h=host2 slow.log
示例2:比较host2和host1的结果文件
pt-upgrade h=host1 --save-results host1_results/ slow.log
pt-upgrade host1_results1/ h=host2

(25)pt-find
功能:pt-find命令可以查找MySQL中的表,并执行一些操作,这个工具类似于我们操作系统下的find命令。默认的操作是打印数据库名和表名。
示例1:打印一天以前创建的表,注意,仅对MyISAM有效。
pt-find --ctime +1 --engine MyISAM
示例2:查找InnoDB引擎的表,并转化为MyISAM表。
pt-find --engine InnoDB --exec "ALTER TABLE %D.%N ENGINE=MyISAM"
示例3:查找test库和junk库中的空表,并删除之。
pt-find --empty junk test --exec-plus "DROP TABLE %s"
示例4:查找大于5GB的表。
pt-find --tablesize +5G
示例5:对所有表都按照数据占用空间大小(数据+索引)进行排序。
pt-find --printf "%T %D.%N " | sort -rn
示例6:把数据表的size信息存放到表中。
pt-find --noquote --exec "INSERT INTO sysdata.tblsize(db, tbl, size) VALUES('%D', '%N', %T)"

16.3 调优方法论
16.3.1 性能调优的误区
许多人犯的错误可能仅仅只是因为自己熟悉一些工具、知道一些命令,就到处使用它,而不管是什么场合。
特别是初级工程师,因为了解的命令和方法有限,他们更容易这样做。
还有些人喜欢去网上寻找答案,但是一些问题的解决方案,特别是涉及一些商业厂商的软硬件,网上并没有标准的答案,只有原厂工程师才掌握处理这些问题的方案,
而且,如果是紧急处理性能问题,上网查找很可能费力不讨好。
有些人不清楚性能问题到底出现在哪,会尝试修改不同的参数配置,然后查看效果怎么样,这样的调优不但耗时,还可能给生产系统带来隐患甚至导致生产环境异常。
有些人往往把问题归咎于其他环节、其他团队。
比如,怀疑是网络的问题,怀疑是数据库的问题,如果你怀疑是其他团队、其他环节出了问题,
那么你应该说明下自己是如何分析、如何查找和为什么怀疑的,这样可以更有助于沟通,而不是浪费了其他部门的时间和精力。
所有以上出现的问题,都是因为没有一定的规则指引,没有按照一定的方法论来处理问题。
方法论包括了一些定量分析和确认疑问的方法,标识了哪些是最重要的性能问题。
它可以帮助性能分析者定位问题,告诉我们应该从哪里开始,应该通过哪些步骤来定位问题。
对于初学者,可以按照方法论逐步来定位问题,对于专家和熟手,可以将方法论作为一个检查列表,确认我们没有忽视一些细节。

16.3.2 调优指引
当发生性能问题时,我们需要知道从哪里开始我们的分析,应该收集什么样的数据,我们应该如何分析这些数据。如下是一些调优指引。
1)首先,我们需要定义调优的目标。
在调优之前,我们需要设定我们的目标,比如:资源使用率、延时、吞吐率。要依据你的业务、服务协议等级和服务标准量化你的性能调优所能达到的效果,
例如:平均响应时间小于5ms,99%的响应时间小于10ms。
确定调优的目标之后,我们再通过各种方式找到系统的瓶颈所在,然后优化它们。
2)我们需要了解我们的数据流,了解我们的物理部署,这样我们才能有意识地针对整个系统进行调优。
3)影响服务性能的主要因素从大到小大致是:架构和设计、应用程序、硬件、数据库和操作系统。
高性能的服务是设计出来的,而不是调优出来的,并且,如果你的架构设计良好,那么不需要怎么调优,调优也会更加容易。
4)大规模、高性能的服务往往不是一蹴而就的,需要在后期不断持续地迭代优化,甚至调整架构。
一个优秀的架构师有一个很重要的素质,那就是:在合适的时间以合理的成本介入进行调整优化。
5)性能调优有两个方向,一个是让工作做得更快,一个是让工作做得更少。针对数据库来说,就是要尽量减少对数据库的访问,使用最快的路径访问数据。
6)DBA需要从系统资源使用和应用访问的双重角度去考虑问题,应用程序开发人员往往更关注程序的负载情况,而系统管理员往往更关注资源的使用情况。
作为一名DBA,需要能够同时从这两个角度去考虑问题。
从资源使用的角度进行分析,我们常用的一些指标有IOPS、吞吐率、利用率和饱和度。
从工作负载的角度进行分析,我们常用的指标有吞吐率和延时。
对工作负载的分析要求我们熟悉工作负载的属性,具体负载做了什么?
比如对于数据库访问, 包括客户端主机/IP、数据库名、数据表名、查询字符串,通过熟悉这些情况,我们可以确定哪些工作是不需要做的,是可以消除的,哪些工作是可以加快的。
我们还可能需要深入到应用的代码细节里去优化。
7)性能调优是一种取舍,我们需要意识到性能调整是有权衡取舍的,性能好、成本低、快速交付这三个目标往往不可兼得。
许多公司的项目选择了成本低和快速交付,而把性能问题留待以后去解决,但是一旦你的架构存在问题,你将很难进行调优,反而不得不付出昂贵的调整成本。
在许多互联网公司,更普遍存在的一种情况是项目的时间是固定的,甚至要赶在对手发布之前进行发布,
如果选择了成本低、性能好的决策,那么如期交付往往很难做到,这个时候如果仍然按照原定的进度计划交付软件,则往往意味着软件和服务的稳定性会下降。
在物理组件之间也有权衡取舍,比如有些应用CPU很空闲,内存很紧张,那么可以使用CPU压缩数据以节省内存的使用。
参数的调整也有权衡取舍,比如文件系统的块大小,较小的块,接近应用的访问I/O大小,更适合随机访问的应用负载。
而较大的块,更适合一些大的操作,比如备份。比如,小的网络缓冲区可以减少每个连接的内存开销,使系统更好地扩展,而大的网络缓冲区则可以提升网络吞吐率。
8)性能调优越靠近任务处理的环节就越有效,因为它们更了解数据,对于施加负荷的应用程序而言,针对应用程序自身的调优最有效。
对于一个普通的基于数据库的网站服务而言,我们的软件栈是应用程序→数据库→系统调用→文件系统→存储。
对存储设备进行优化可以提升I/O能力,但这是对最底层的优化,它的改善反映到更上层的应用的改善,往往是打了折扣的,
比如存储设备的性能提升了1倍,也许应用的性能才提升20%。
造成这种现象的根本原因是越远离应用层,越不熟悉数据是如何存取的,所以针对存储设备所做的优化,相对于应用程序自身的调优,可能对于应用的性能改善并没有太大的用处。
9)参数的调整可能会马上见效,但很可能随着软硬件环境的变更,又变得不再适合,甚至还会对性能起反作用。
所以,如果不是必须要调整,那么就不要去调整。更少的参数配置,使用默认的参数配置也意味着更少的维护成本。
网上有许多调优的建议和分享。这些分享往往都是基于特定的环境的,或者是基于特定的场合,而在之后,他们又有了调整,却往往没有继续分享,
一些调优的建议,特别是互联网上的文章,以讹传讹,很可能存在某种隐患,为了保持系统的稳定,除非我们确定必须要更改,否则建议不要修改。
更合适的策略是,关注业内的专家,看看他们的分享,看看哪些参数是可以在我们的系统上进行修改的。做一些记录,一旦有需要,我们再采纳也不迟。
10)我们不仅要标识出哪些问题可能是需要调优的,也要衡量修复这些问题所带来的收益。
如果修复一个问题,对于整体系统的贡献不大,那么投入时间和精力去调优可能不是一个好的主意。

16.3.3 调优步骤
调优的具体步骤如下。
1)收集信息。
如果你是一个支持工程师,或者是一个独立的数据库咨询顾问,那么当有性能问题发生时,你需要和客户确认一些信息。
如下是要问的一些基本问题,通过这些问题,你往往可以很快就能确定原因,或者确定一个合适的解决方案,或者确定下一步应该怎么做。
你为什么认为有性能问题,你是如何判断的?
系统之前是好的吗?最近有做过什么软硬件的变更吗?业务流量、负载有变化吗?
问题可以使用延时或运行时间来描述吗?
影响到了其他的用户和程序吗?
软硬件的环境是什么样的?版本/配置/参数是什么样的?
如果我们的线上有性能问题,那么我们需要收集负载信息和资源使用情况。
我们还可能需要检查配置,比如出现网络问题,比如有时网卡工作在100MB的模式下而不是1000MB的模式,比如RAID阵列坏了一块盘,
比如操作系统、应用程序、固件的版本有变化,比如因为程序配置错误,访问了远程资源而不是本地资源。
2)分析问题。
在问完这些问题之后,我们大致有了诊断的思路。这时我们可以推测有可能是哪些因素导致了性能问题。
3)验证性能问题的原因。
之后我们再进行一些验证测试工作来验证我们的想法。
比如MySQL的InnoDB buffer pool对性能的影响很大,我们假定是InnoDB buffer pool不够才导致的性能恶化,
我们可以部署一个新的环境,增大InnoDB buffer pool,进行压力测试,这个时候,我们会发现性能有所改善,从数据库的状态信息我们也可以发现物理读减少了许多。
通过不断地猜测和验证,我们可以逐步缩小范围,定位到真正的导致性能恶化的因素上。
4)进行调整。
5)观察性能调整的效果。

16.3.4 调优的方法
(1)USE方法
有一种调优的方法叫USE方法。其基本思路是,对于每项资源,检查其错误、利用率和饱和度。
在资源利用率很高或趋向饱和时,如果出现瓶颈,性能降低,那么这种情况下,USE的分析方法将会最有效。
我们首先检查错误,对于错误,需要仔细就调查其产生原因。错误很可能会导致性能降低,如果错误是不可以被重现的,那么你可能不能及时发现错误。
然后我们检查资源利用率,检查饱和度,在检查资源利用率和饱和度的过程中,我们逐步缩小范围,最终定位到问题所在。
(2)延迟分析法
找出响应最慢的环节,这个环节将被再次细分,再找出最影响时间的因素,并不断循环,直到最终解决问题。

小结:
本章介绍了一些性能调优的基础概念、理论和常用的诊断工具。
综合知识、工具、经验、意识,我们才可能成为一名性能调优专家。
其中一些工具和命令的解释是摘录自网上的信息,笔者做了一些修正,但仍然可能有错漏之处,建议不熟悉命令的读者,好好阅读man帮助,这是笔者认为学习命令的最好的一种方法。
Percona Toolkit包含了很多工具,平时我们经常使用的可能只有寥寥几个工具。
笔者没有对所有工具做实际验证,读者应该牢记一点,如果工具可能修改生产环境中的数据,那么就一定要慎重使用,建议首先备份好数据。

原文地址:https://www.cnblogs.com/BradMiller/p/12085579.html