第三章 服务器性能剖析 高性能MySql阅读总结

        在我们的技术咨询生涯中,最常碰到的三个性能相关的服务请求是:如何确认服务器是否达到了性能最佳的状态、找出某条语句为什么执行不够快,以及诊断被用户描述成“停顿” 、“堆积” 或者”卡死” 的某些间歇性疑难故障。本章将主要针对这三个问题做出解答。我们将提供一些工具和技巧来优化整机的性能、优化单条语句的执行速度, 以及诊断或者解决那些很难观察到的问题(这些问题用户往往很难知道其根源,有时候甚至都很难察觉到它的存在)。

        这看起来是个艰巨的任务,但是事实证明,有一个简单的方法能够从噪声中发现苗头。这个方法就是专注于测量服务器的时间花费在哪里,使用的技术则是性能剖析(profiling)。在本章,我们将展示如何测最系统并生成剖析报告, 以及如何分析系统的整个堆栈(stack), 包括从应用程序到数据库服务器到单个查询。

3.1性能优化简介

        问10个人关于性能的问题,可能会得到10个不同的回答,比如“每秒查询次数” 、"cpu利用率” 、“ 可扩展性” 之类。这其实也没有问题,每个人在不同场景下对性能有不同的理解,但本章将给性能一个正式的定义。我们将性能定义为完成某件任务所需要的时间度量,换句话说,性能即响应时间,这是一个非常重要的原则。我们通过任务和时间而不是资源来测最性能。数据库服务器的目的是执行SQL语句,所以它关注的任务是查询或者语句,如SELECT、UPDATE、DELETE等。数据库服务器的性能用查询的响应时间来度量, 单位是每个查询花费的时间。

        还有另外一个问题:什么是优化?我们暂时不讨论这个问题, 而是假设性能优化就是在一定的工作负载下尽可能地 降低响应时间。

        很多人对此很迷茫。 假如你认为性能优化是降低CPU利用率, 那么可以减少对资源的使用。 但这是一个陷阱, 资源是用来消耗并用来工作的, 所以有时候消耗更多的资源能够加快查询速度。 很多时候将使用老版本InnoDB引擎的MySQL升级到新版本后,CPU利用率会上升得很厉害, 这并不代表性能出现了问题, 反而说明新版本的InnoDB对资 源的利用率上升了。 查询的响应时间则更能体现升级后的性能是不是变得更好。 版本升级有时候会带来一些bug, 比如不能利用某些索引从而导致CPU利用率上升。 CPU利用率只是一种现象, 而不是很好的可度量的目标。

        同样, 如果把性能优化仅仅看成是提升每秒查询量, 这其实只是吞吐量优化。 吞吐量的提升可以看作性能优化的副产品。对查询的优化可以让服务器每秒执行更多的查询, 因为每条查询执行的时间更短了(吞吐量的定义是单位时间内的查询数最, 这正好是我们对性能的定义的倒数)。

        所以如果目标是降低响应时间, 那么就需要理解为什么服务器执行查询需要这么多时间, 然后去减少或者消除那些对获得查询结果来说不必要的工作。 也就是说, 先要搞清楚时间花在哪里。 这就引申出优化的第二个原则:无法测量就无法有效地优化。 所以第一步应该测量时间花在什么地方。

        我们观察到,很多人在优化时,都将精力放在修改一些东西上,却很少去进行精确的测量。我们的做法完全相反, 将花费非常多, 甚至90%的时间来测量响应时间花在哪里。 如果 通过测量没有找到答案, 那要么是测最的方式错了, 要么是测量得不够完整。 如果测量 了系统中完整而且正确的数据, 性能问题一般都能暴露出来, 对症下药的解决方案也就比较明了。 测量是一项很有挑战性的工作, 并且分析结果也同样有挑战性, 测出时间花 在哪里,和知道为什么花在那里, 是两码事。

        前面提到需要合适的测量范围, 这是什么意思呢?合适的测量范围是说只测量需要优化 的活动。 有两种比较常见的情况会导致不合适的测量:

• 在错误的时间启动和停止测量。

• 测量的是聚合后的信息,而不是目标活动本身。

        例如, 一个常见的错误是先查看慢查询, 然后又去排查整个服务器的情况来判断问题在哪里。 如果确认有慢查询, 那么就应该测量慢查询,而不是测量整个服务器。 测量的应该是从慢查询的开始到结束的时间,而不是查询之前或查询之后的时间。

        完成一项任务所需要的时间可以分成两部分:执行时间和等待时间。 如果要优化任务的执行时间, 最好的办法是通过测量定位不同的子任务花费的时间, 然后优化去掉一些子任务、 降低子任务的执行频率或者提升子任务的效率。 而优化任务的等待时间则相对要复杂一些, 因为等待有可能是由其他系统间接影响导致, 任务之间也可能由于争用磁盘或者CPU资源而相互影响。 根据时间是花在执行还是等待上的不同,诊断也需要不同的 工具和技术。

        刚才说到需要定位和优化子任务, 但只是一笔带过。 一些运行不频繁或者很短的子任务对整体响应时间的影响很小, 通常可以忽略不计。 那么如何确认哪些子任务是优化的目标呢?这个时候性能剖析就可以派上用场了。

        如何判断测量是正确的?如果测量是如此重要,那么测量错了会有什么后果?实际上,测量经常都是错误的。 对数量的测量并不等于数量本身。 测量的错误可能很小,跟实际情况区别不大, 但 错的终归是错的。 所以这个问题其实应该是:"测量到底有多么不准确?” 这个问 题在其他一些书中有详细的讨论, 但不是本书的主题。 但是要意识到使用的是测量 数据, 而不是其所代表的实际数据。 通常来说, 测量的结果也可能有多种模糊的表现, 这可能导致推断出错误的结论。

3.1.1通过性能剖析进行优化

        一且掌握井实践面向响应时间的优化方法, 就会发现需要不断地对系统进行性能剖析(profiling)。

        性能剖析是测量和分析时间花费在哪里的主要方法 性能剖析一般有两个步骤:测量任务所花费的时间,然后对结果进行统计和排序, 将重要的任务排到前面

        性能剖析工具的工作方式基本相同。 在任务开始时启动计时器, 在任务结束时停止计时  器, 然后用结束时间减去启动时间得到响应时间。 也有些工具会记录任务的父任务。 这些结果数据可以用来绘制调用关系图, 但对于我们的目标来说更重要的是, 可以将相似的任务分组并进行汇总。 对相似的任务分组并进行汇总可以帮助对那些分到一组的任务做更复杂的统计分析, 但至少需要知道每一组有多少任务, 并计算出总的响应时间。 通过性能剖析报告 (profile report) 可以获得需要的结果。 性能剖析报告会列出所有任务列表。 每行记录一个任务, 包括任务名、 任务的执行时间、 任务的消耗时间、 任务的平均执行时间, 以及该任务执行时间占全部时间的百分比。 性能剖析报告会按照任务的消耗 时间进行降序排序。

        为了更好地说明, 这里举一个对整个数据库服务器工作负载的性能剖析的例子, 主要输出的是各种类型的查询和执行查询的时间。 这是从整体的角度来分析响应时间, 后面会演示其他角度的分析结果。 下面的输出是用 Percona Toolkit 中的pt-query-digest (实际上就是著名的 Maatkit 工具中的 mk-query-digest) 分析得到的结果。 为了显示方便, 对结果做了一些微调, 并且只截取了前面几行结果:

第三章 服务器性能剖析 高性能MySql阅读总结_第1张图片

        上面只是性能剖析结果的前几行, 根据总响应时间进行排名, 只包括剖析所需要的最小列组合。 每一行都包括了查询的响应时间和占总时间的百分比、 查询的执行次数、 单次执行的平均响应时间, 以及该查询的摘要。 通过这个性能剖析可以很清楚地看到每个查询相互之间的成本比较, 以及每个查询占总成本的比较。 在这个例子中, 任务指的就是查询, 实际上在分析 MySQL 的时候经常都指的是查询。

        我们将实际地讨论两种类型的性能剖析:基于执行时间的分析和基于等待的分析。基于执行时间的分析研究的是什么任务的执行时间最长, 而基于等待的分析则是判断任务在什么地方被阻塞的时间最长

        如果任务执行时间长是因为消耗了太多的资源且大部分时间花费在执行上, 等待的时间 不多, 这种情况下基于等待的分析作用就不大。 反之亦然, 如果任务一直在等待, 没有消耗什么资源, 去分析执行时间就不会有什么结果。 如果不能确认问题是出在执行还是等待上, 那么两种方式都需要试试。 后面会给出详细的例子。

        事实上, 当基于执行时间的分析发现一个任务需要花费太多时间的时候, 应该深入去分析一下, 可能会发现某些 “执行时间” 实际上是在等待。 例如, 上面简单的性能剖析的 输出显示表 InvitesNew上的 SELECT 查询花费了大量时间, 如果深入研究, 则可能发现时间都花费在等待I/O完成上。

        在对系统进行性能剖析前,必须先要能够进行测量,这需要系统可测量化的支持。 可测量的系统一般会有多个测最点可以捕获井收集数据,但实际系统很少可以做到可测量化。大部分系统都没有多少可测址点,即使有也只提供一些活动的计数,而没有活动花费的时间统计。 MySQL就是一个典型的例子,直到版本5.5才第一次提供了Performance Schema, 其中有一些基于时间的测量点 , 而版本5.1及之前的版本没有任何基于时间的测量点。能够从MySQL收集到的服务器操作的数据大多是show status计数器的形式,这些计数器统计的是某种活动发生的次数。 这也是我们最终决定创建Percona Server的主要原因,PerconaServer从版本5.0开始提供很多更详细的查询级别的测址点。

        虽然理想的性能优化技术依赖于更多的测量点,但幸运的是,即使系统没有提供测量点,也还有其他办法可以展开优化工作。 因为还可以从外部去测量系统,如果测量失败,也可以根据 对系统的了解做出一些靠谱的猜测。 但这么做的时候一定要记住,不管是外部测量还是猜测,数据都不是百分之百准确的,这是系统不透明所带来的风险。

        举个例子,在Percona Server 5.0中,慢查询日志揭露了一些性能低下的原因,如磁盘I/O等待或者行级锁等待。 如果日志中显示一条查询花费10秒,其中 9.6 秒在等待磁盘I/O,那么追究其他4%的时间花费在哪里就没有意义,磁盘1/0才是最重要的原因。

3.1.2理解性能剖析

        MySQL的性能剖析(profile)将最重要的任务展示在前面,但有时候没显示出来的信息也很重要。 可以参考一下前面提到过的性能剖析的例子。 不幸的是,尽管性能剖析输出 了排名 、 总计和平均值,但还是有很多需要的信息是缺失的,如下所示。

值得优化的查询(worthwhile query)

        性能剖析不会自动给出哪些查询值得花时间去优化。 这把我们带回到优化的本意,如果你读过Cary Millsap的书, 对此就会有更多的理解。 这里我们要再次强调两点: 第一 一些只占总响应时间比重很小的查询是不值得优化的。 根据阿姆达尔定律(Amdahl's Law), 对一个占总响应时间不超过 5% 的查询进行优化,无论如何努力,收益也不会超过 5%。 第二,如果花费了1 000 美元去优化一个任务,但业务的收入没有任何增加,那么可以说反而导致业务被逆优化了1 000 美元。 如果优化的成本大于收益,就应当停止优化。

异常情况

        某些任务即使没有出现在性能剖析输出的前面也需要优化。 比如某些任务执行次数很少, 但每次执行都非常慢,严重影响用户体验。 因为其执行频率低,所以总的响应时间占比井不突出。

未知的未知

        一款好的性能剖析工具会显示可能的 “丢失的时间”。丢失的时间指的是任务的总时间和实际测量到的时间之间的差。 例如,如果处理器的CPU时间是10秒, 而剖析 到的任务总时间是9.7秒, 那么就有300毫秒的丢失时间。 这可能是有些任务没有测量到, 也可能是由于测量的误差和精度问题的缘故。 如果工具发现了这类问题, 则要引起重视,因为有可能错过了某些重要的事情。即使性能剖析没有发现丢失时间, 也需要注意考虑这类问题存在的可能性, 这样才不会错过重要的信息。 我们的例子中没有显示丢失的时间, 这是我们所使用工具的一个局限性。

被掩藏的细节

        性能剖析无法显示所有响应时间的分布。 只相信平均值是非常危险的, 它会隐藏很多信息, 而且无法表达全部情况。Peter经常举例说医院所有病人的平均体温没有任何价值。假如在前面的性能剖析的例子的第一项中,如果有两次查询的响应时间是 1秒, 而另外12 771次查询的响应时间是几十微秒, 结果会怎样?只从平均值里是 无法发现两次1秒的查询的。 要做出最好的决策, 需要为性能剖析里输出的这一行中包含的12773次查询提供更多的信息,尤其是更多响应时间的信息,比如直方图、百分比、标准差、偏差指数等。

        好的工具可以自动地获得这些信息。 实际上,pt-query-digest就在剖析的结果里包含了很多这类细节信息, 并且输出在剖析报告中。 对此我们做了简化, 可以将精力集中在重要而基础的例子上:通过排序将最昂贵的任务排在前面。 本章后面会展示更多丰富而有用的性能剖析的例子。

        在前面的性能剖析的例子中, 还有一个重要的缺失,就是无法在更高层次的堆栈中进行交互式的分析。 当我们仅仅着眼于服务器中的单个查询时, 无法将相关查询联系起来,也无法理解这些查询是否是同一个用户交互的一部分。 性能剖析只能管中窥豹, 而无法将剖析从任务扩展至事务或者页面查看(page view)的级别。 也有一些办法可以解决这 个问题,比如给查询加上特殊的注释作为标签, 可以标明其来源井据此做聚合, 也可以在应用层面增加更多的测量点, 这是下一节的主题。

3.2对应用程序进行性能剖析

        对任何需要消耗时间的任务都可以做性能剖析, 当然也包括应用程序。 实际上, 剖析应用程序一般比剖析数据库服务器容易, 而且回报更多。 虽然前面的演示例子都是针对MySQL服务器的剖析,但对系统进行性能剖析还是建议自上而下地进行,这样可以追踪自用户发起到服务器响应的整个流程。 虽然性能问题大多数情况下都和数据库有关,但应用导致的性能问题也不少。 性能瓶颈可能有很多影响因素:

• 外部资源, 比如调用了外部的Web服务或者搜索引擎。

• 应用需要处理大量的数据, 比如分析一个超大的XML文件。

• 在循环中执行昂贵的操作, 比如滥用正则表达式。

• 使用了低效的算法, 比如使用暴力搜索算法(naive search algorithm)来查找列表中的项。

        幸运的是,确定MySQL的问题没有这么复杂,只需要一款应用程序的剖析工具即可(作为回报, 一且拥有这样的工具, 就可以从一开始就写出高效的代码)。

        建议在所有的新项目中都考虑包含性能剖析的代码。 往已有的项目中加入性能剖析代码 也许很困难, 新项目就简单一些。

3.3剖析MySQL查询

        对查询进行性能剖析有两种方式,可以剖析整个数据库服务器,这样可以分析出哪些查询是主要的压力来源(如果已经在最上面 的应用层做过剖析,则可能已经知道哪些查询需要特别留意)。定位到具体需要优化的 查询后,也可以钻取下去对这些查询进行单独的剖析分析哪些子任务是响应时间的主 要消耗者。

3.3.1剖析服务器负载

        服务器端的剖析很有价值,因为在服务器端可以有效地审计效率低下的查询。如果只是需要剖析并找出代价高的查询,就不需要如此复杂。有一个工具很早之前就能帮到我们了,这就是慢查询日志。

        在MySQL的当前版本中,慢查询日志是开销最低、精度最高的测量查询时间的工具。慢查询日志带来的开销可以忽略不计(实际上在CPU密集型场景的影响还稍微大一些)。更需要担心的是日志可能消耗大量的磁盘空间。如果长期开启慢查询日志,注意要部署日志轮转(log rotation)工具。或者不要长期启用慢查询日志,只在需要收集负载样本的期间开启即可。

分析查询日志

        不要直接打开整个慢查询日志进行分析,这样做只会浪费时间和金钱。首先应该生成一个剖析报告,如果需要,则可以再查看日志中需要特别关注的部分。自顶向下是比较好 的方式,否则有可能像前面提到的,反而导致业务的逆优化。

3.4诊断间歇性问题

        尽量不要使用试错的方式来解决问题。这种方式有很大的风险,因为结果可能变得更坏。这也是种令人沮丧且低效的方式。如果一时无法定位问题,可能是测量的方式不正确, 或者测量的点选择有误,或者使用的工具不合适(也可能是缺少现成的工具,我们已经开发过工具来解决各个系统不透明导致的问题,包括从操作系统到MySQL都有)。

为了演示为什么要尽量避免试错的诊断方式, 下面列举了我们认为已经解决的一些间歇性数据库性能问题的实际案例:

• 应用通过curl从一个运行得很慢的外部服务来获取汇率报价的数据。

• memcached缓存中的一些重要条目过期,导致大最请求落到M ySQL以重新生成缓存条目。

• DNS查询偶尔会有超时现象。

• 可能是由于互斥锁争用,或者内部删除查询缓存的算法效率太低的缘故,MySQL的查询缓存有时候会导致服务有短暂的停顿。

• 当并发度超过某个闽值时,InnoDB的扩展性限制导致查询计划的优化需要很长的时间。

3.6总结

        本章给出了一些基本的思路和技术, 有助千你成功地进行性能优化。 正确的思维方式是开启系统的全部潜力和应用本书其他章节提供的知识的关键。 下面是我们试图演示的一 些基本知识点:

• 我们认为定义性能最有效的方法是响应时间。

• 如果无法测量就无法有效地优化, 所以性能优化工作需要基于高质量、 全方位及完整的响应时间测量。

• 测量的最佳开始点是应用程序, 而不是数据库。 即使问题出在底层的数据库, 借助良好的测量也可以很容易地发现问题。

• 大多数系统无法完整地测量, 测量有时候也会有错误的结果。 但也可以想办法绕过 一些限制, 并得到好的结果(但是要能意识到所使用的方法的缺陷和不确定性在哪里)。

• 完整的测量会产生大量需要分析的数据, 所以需要用到剖析器。 这是最佳的工具, 可以帮助将重要的问题冒泡到前面, 这样就可以决定从哪里开始分析会比较好。

• 剖析报告是一种汇总信息, 掩盖和丢弃了太多细节。 而且它不会告诉你缺少了什么, 所以完全依赖剖析报告也是不明智的。

• 有两种消耗时间的操作:工作或者等待。 大多数剖析器只能测晕因为工作而消耗的时间, 所以等待分析有时候是很有用的补充, 尤其是当 CPU 利用率很低但工作却一直无法完成的时候。

• 优化和提升是两回事。 当继续提升的成本超过收益的时候, 应当停止优化。

        总体来说, 我们认为解决性能问题的方法, 首先是要澄清问题, 然后选择合适的技术来 解答这些问题。 如果你想尝试提升服务器的总体性能, 那么一个比较好的起点是将所有查询记录到日志中, 然后利用pt-query-digest工具生成系统级别的剖析报告。 如果是要追查某些性能低下的查询, 记录和剖析的方法也会有帮助。 可以把精力放在寻找那些消耗时间最多的、 导致了糟糕的用户体验的, 或者那些高度变化的, 抑或有奇怪的响应时 间直方图的查询。 当找到了这些 “坏 ” 查询时, 要钻取pt-query-digest报告中包含的该查询的详细信息, 或者使用 SHOW PROFILE 及其他诸如 EXPLAIN 这样的工具。

        如果找不到这些查询性能低下的原因, 那么也可能是遇到了服务器级别的性能问题。 这 时, 可以较高精度测量和绘制服务器状态计数器的细节信息。 如果通过这样的分析重现了问题, 则应该通过同样的数据制定一个可靠的触发条件, 来收集更多的诊断数据。 多花费一点时间来确定可靠的触发条件, 尽量避免漏检或者误报。 如果已经可以捕获故障活动期间的数据,但还是无法找到其根本原因, 则要么尝试捕获更多的数据, 要么尝试寻求帮助。

        我们无法完整地测量工作系统,但说到底它们都是某种状态机, 所以只要足够细心, 逻辑清晰井且坚持下去, 通常来说都能得到想要的结果。 要注意的是不要把原因和结果搞混了, 而且在确认问题之前也不要随便针对系统做变动。

        理论上纯粹的自顶向下的方法分析和详尽的测量只是理想的情况, 而我们常常需要处理的是真实系统。 真实系统是复杂且无法充分测量的, 所以我们只能根据情况尽力而为。使用诸如pt-query-digest和MySQL企业监控器的查询分析器这样的工具并不完美, 通常都不会给出问题根源的直接证据。 但真的掌握了以后, 已经足以完成大部分的优化诊断工作了。


你可能感兴趣的:(第三章 服务器性能剖析 高性能MySql阅读总结)