有些时候,我们需要优化MySQL。那我们要对MySQL进行哪些改进呢?一条特殊的query?数据库模式?服务器硬件?唯一的办法是测量你的系统在做什么,在各种条件下测量它的性能。这就是我们下面要学习的。
最好的策略就是找出最弱的环节,并加强你的应用程序链的组成。这非常有用,如果你不知道什么阻止最优性能,或者以后什么将要阻止最优性能的发挥。
基准测试和剖析是两条基本的找出瓶颈的方法。它们是有关联的,但是它们又不完全相同。基准测试你的系统的性能。这将有助于确定系统的承受能力,向你展示哪些改变有用哪些没用,或者显示在不同的数据下你的应用程序的性能。
相反,剖析帮助你找出你的应用程序在哪里花费了大量的时间,或者消耗了大量的资源。换句话说,基准可以回答“这种执行表现怎么样?”,而剖析可以回答“为什么它会这个样子执行?”
我们准备在这个章节中讲述两部分内容,基准测试和剖析。我们开始讨论基准测试的原因和对策,然后引入特定的基准测试的标杆(或者尺子)。我们先向你展示如何计划和设计基准测试,为精确的结果做设计,执行基准测试和分析结果。最后,来看看基准测试工具和如何使用它们的例子。
剩下的章节讲述如何优化应用程序和MySQL。我们将会详细地展示我们已经应用于生产帮助分析应用程序的性能,真实的优化代码。我们也会展示怎样记录MySQL的query语句,分析日志,使用MySQL的状态计数器,以及用来查看MySQL和你的query语句怎样做的其它工具。
为什么需要基准?
很多大中型的MySQL部署有专门的标杆用在基准测试里。然而,每个开发者和DBA也应该熟悉基础的基准测试和操作,因为它们非常有用。下面是基准测试可以帮助你的一些事情:
测量你的应用程序当前是怎样执行的。如果你不知道你的应用程序当前执行多快,你不能确定哪些改变有用。你还可以用历史基准结果,来诊断比不能预期的问题。
证实你系统的可扩充性。你可以用基准测试来模仿比你的生产环境能处理的多得多的负载,比如成千上百倍的增加用户。
计划增长。基准测试能帮助你评估将来你的预计负荷需要多少硬件,网络容量和其它资源。这能在升级或者大量应用程序改变的时候,帮助减少风险。
测试你的应用程序在一个变化的环境里的承受能力。例如,你可以找出你的应用程序,在并发下不定时的峰值或者不同的服务器的配置的情况下,是怎样执行的,或者你可以看到在不同的数据分布下它是怎样处理的。
测试不同的硬件、软件和操作系统配置。对于你的系统来说,是RAID5还是RAID10更好?当你从ATA磁盘切换到SAN存储的时候,随机写的性能是怎样变化的?2.4的Linux内核比2.6的更好吗?MySQL的升级有助于提高性能吗?对于你的数据,不同的存储引擎有影响吗?你可以用不同的基准来回答这些问题。
对于其他目的,你也可以用基准测试,例如,为你的应用程序创建一个单元测试套件,但是在这里我们仅仅关注性能相关方面。
2.基准策略
有两条基本的基准测试策略:你可以对应用程序作为一个整体,或者隔离MySQL,用基准问题测试。这两种策略分别以全栈和单组件基准测试闻名。有以下几点测试整个应用程序而不仅仅是MySQL:
你测试整个应用程序,包括web服务,应用程序代码和数据库。这非常有用,因为你不仅仅关注MySQL的性能,更关心整个应用程序。
MySQL并不总是应用的瓶颈,全站基准测试可以证明这一点。
只有测试整个应用,你才能知道每个部分的缓存行为。
基准测试在某种程度上是好的,因为它反映了你的应用的真正行为,当你单独测试某个模块的时候,很难发现的行为。
另一方面,应用程序基准测试很难创建,甚至很难正确地安装。如果你的基准测试设计的很糟糕,你就会得出错误的结论,因为结果不能反映真实情况。
然而,有时你不想了解整个应用。在最初阶段,可能你只想了解MySQL基准测试。下面的基准测试是有用的:
你想比较不同的模式或者query语句
你想测试应用中一个特殊的问题
相比长篇大论的基准测试来说,你更倾向短的基准测试,能向你展示标记和测量改变的快的“循环时间”。
当你在真实数据集的环境中,一次又一次的重复你的应用query语句时,基准测试MySQL是非常有用的。数据集本身和数据集的大小都必须是真实的。如果可能的话,做一个生产环境中的数据快照。
不幸的是,建立一个真实的基准,是非常复杂和耗时的;如果你能得到生产环境中的数据集的复制品,算你走运。当然,这有可能是不可行的。比如,你可能开发了一个新的应用程序,只有少数的用户和数据。如果你想知道,如果它变得庞大时,将会有什么问题发生,除了模拟更大应用数据和负载,你没得选择。
测试什么?
在你开始基准测试之前,甚至是在你设计测试之前,你需要确定你的目标。你的目标将会决定你的工具和技术,以便得到精确地有意义的结果。用问题来设计你的目标,比如“CPU是多的好吗?”或者“新的索引是不是比现在的索引执行的更快?”
它不可能是显而易见的,这就需要你用不同的方法来测试不同的事情,例如:延迟和吞吐量需要不同的基准测试。
考虑以下几个量度和它们如何完善你的性能目标:
单位时间的交易量
这是一个经典的历史为基准的数据库应用程序。标准化测试,如TPC-C标准(见http://www.tpc.org)被广泛引用,很多数据库提供商,工作非常努力以使它们工作的好。这些基准测试在线处理(OLTP)性能,这些基准最适合多用户交易应用程序。通常的测量单位是每秒交易量。
吞吐量这个词通常的意思是等同于单位时间内的交易量(或者工作的其它单元)。
响应时间或延迟
这测量了一个任务需要的总时间。依赖你的应用,你可能需要测量毫秒,秒或者分钟。从这里你可以得出平均响应时间,最小响应时间和最大响应时间。
最大响应时间是很少有用的度量,因为基准测试运行时间越长,可能最大响应时间越大。它并不总是能重复的,这就可能会在运行的过程中拉大差距。正是因为这个原因,很多人使用百分比的响应时间。例如,如果95%的响应时间是5毫秒,你就可以知道任务可以在总时间的95%内少于5毫秒完成。
画出基准测试的结果,为图形或者线性图(例如,平均值和95%百分比)或者是散列图,将是非常有帮助的,因为这样你就可以看到结果的分布情况。通过这些图形,可以看出在长时间运行过程中,基准测试是怎样执行的。
假设你的系统每小时做一分钟检测。在检测期间,系统“抛锚”,没有交易完成。95%的响应时间不会显示峰值,所以结果会掩盖这个问题。然而,一个图形会显示响应时间内的周期性峰值。图2-1会阐述这点。
图2-1显示了每分钟的交易量。线条显示了象征性的超过平均值的峰值。第一个峰值是因为服务器的缓存被冷冻了,另一个峰值显示了服务器刷新脏页稳定性到磁盘花费的时间。如果没有图形我们很难看到这些差异。
稳定性
对于系统来说,稳定性测试非常重要,因为系统需要在变化的工作负载下保持性能。
“在一个变化的工作负载下保持性能”是一个很抽象的概念。性能是可以被度量的,例如,吞吐量和响应时间;工作负载随着数据库大小,当前连接数,或者硬件不同,可能会存在差异。
稳定性测试,对于评估系统承载能力来说是好的,因为它能展示出你的应用中的薄弱环节,而在其它基准测试中不会展示。
图2-1 30分钟运行的结果
例如,在单链接(不好的测试策略)的情况下做响应时间测试,你设计的系统性能良好;但是在任何等级的并发下,你应用可能会表现糟糕。一个测试关注的是在不断增加的连接下的持续响应时间,这样才可以看到设计的瑕疵。
有一些活动,例如搜集颗粒数据创建总结性数据表的周期性批量作业,仅仅需要快速响应时间。单纯地测试响应时间是好的,但是也要关心他们和其它活动是怎么交互(相互影响)的。批量作业可能会导致交互的query语句表现较差,反之亦然。
并发
并发是很重要的,但是很多时候都被滥用和被错误地衡量。例如,有一种很流行的说法,有多少用户在同时浏览网站。然而,HTTP是无状态的,大多数用户只是简单地阅读浏览器展示的内容,所以这并不能转化为web服务器的并发。同样地,在web服务器上的并发并不一定转化到数据库服务器上。有直接关联的就是你的会话存储机制能处理多少数据。一个更精确的测试web服务器的并发的方法是在峰值的时候,用户每秒请求的次数。
你也可以在应用程序的不同地方测试并发。在web服务器上的并发越高,可能引起更高的数据库并发等级。但是语言和工具套件可能影响它。例如,Java的连接池可能会比持续连接的PHP,会降低MySQL服务器的并发连接。
更重要的是在一个给定时间内运行query语句的并发数量。一个很好的设计应用程序可能会打开MySQL服务器的数以百计的并发,但是其中的一少部分应该会同时执行query语句。这样,一个“50,000用户同时在线”的web站点,可能在MySQL服务器上只需要10~15个同时执行query语句。
换句话说,你要真正关心的基准测试就是工作并发,或者线程数量,或者同时工作连接。测试当并发增加的时候,性能掉下来多少。如果是这样的话,你的应用程序可能就无法处理高负载下的峰值。
你也需要确保性能不会很快地降下来,或者设计应用程序,这样就不会在应用程序的各个部分产生不能处理的高并发了。在通常情况下,你要设计限制MySQL服务器的并发,如应用队列。
并发不能完全等同于响应时间和稳定性:它并不是一个结果,而是你怎样建立基准测试的一个属性。你应该在不同的并发水平下测试应用程序的性能,而不是测试你的应用程序的能达到的并发。
总之,你应该测试对用户来说重要的东西。测试衡量性能,但是“性能”对不同的人意味着不同的东西。收集一些关于系统应当怎样测量的需求(正式或非正式的),能接受的响应时间,期望的并发类型,等等。然后,尝试设计你的测试来解释所有的需求,而不是“井底之蛙”排除其他东西关注某项东西。
3.测试标杆
在有个大致了解的情况下,让我们转向怎样设计和执行基准测试上来。在我们讨论如何把基准测试做好之前,先让我们看下一些常见的错误,这些错误能导致不能用或者不精确地结果:
使用真实数据大小的子集,例如,当应用程序不得不处理好几百G的数据时,我们只使用其中的1G数据;或者当你准备扩大你的应用程序时,使用现在的数据集
使用错误的数据分布,例如当真实系统数据中的“热点”规则的数据分布(随机生成的数据通常是不切实际的分布)。
使用不切实际的分布参数,例如,假设所有用户的配置文件同样地被浏览。
在多用户应用中,使用单用户场景。
在单台服务器上测试分布式应用。
和真实用户的行为错误地比较,例如web页面上的“思考时间”。真实用户请求并阅读它;他们不会一个接一个没有停顿地点击链接。
在一个循环里执行相同的query语句。真实的query语句是不同的,所以它们会引起缓存未命中的情况。相同的query语句将会在某种级别全部或者部分被缓存。
未能检查错误。如果一个基准测试的结果没有意义-例如,如果一个慢操作突然非常快地完成,那么就该检查错误。你就能测试出在一个SQL查询时,MySQL能多快地探测到语法错误!原则性来说,每次测试完后都应该检查错误日志。
当系统还没有变热的时候,忽略系统是怎样执行的,例如,系统刚刚重启后。有时你需要知道你的服务器重启后,需要多长时间达到承载能力,所以你需要在热启动期间注意观察。相反地,如果你想研究它的正常性能,你需要关心,如果你的测试正好在重启后,许多缓存将被冷冻,那么测试结果将不会反映,在缓存在变热后,在负载下得到的结果。
使用默认的服务设置。
仅在避免这些错误上就会花费你很长时间来改进你的结果质量。
对于别的所有事情都是同样的,你应该在尽可能真实的环境中做测试。尽管有时,使用一个稍微不真实的测试也是明智的。例如,假如说你的应用程序在不同的主机上。使用相同的配置执行测试,将会更接近真实情况,但是这样做就会增加更多变量,例如,网络负载多少,多快。在单节点上测试往往很简单,然而在某些情况下,将会更精确。什么时候使用最合适,完全取决于你的判断。
设计和规划测试
规划测试的第一步就是确定问题和目标。然后,决定是否使用标准的测试还是你自己设计。
如果你使用标准测试,要保证你选用的测试符合你的需求。例如,不要使用TCP测试你的电子商务系统。用TCP自己的话说,TCP“”。所以对于OLTP系统来说,不是一个合适的测试。
设计你自己的测试是一个复杂的反复的进程。开始,使用你生产环境中的数据集的快照。确保你能为后来的运行恢复这些数据集。
然后,你需要在数据里运行query语句。你可以在基本测试里添加单元测试套件,多次执行,但是这和你怎样真实地使用数据库,不大可能匹配。一个比较好的方法是在一个典型的时间框架内,记录你生产环境中的所有的query语句,例如,在峰值负载内的一个小时,或者一整天。如果你在很短的时间框架内,记录了query语句,你可能需要选择几个时间框架。这将会使你覆盖所有系统活动,例如,每周报告query语句,或者在低峰值的时期,执行计划任务。
你可以在不同等级下记录query语句。例如,如果你需要全栈测试的话,你就可以记录WEB服务器上的HTTP请求。你也可以启用MySQL的查询日志,但是如果你重放查询日志,要确保重新创建单独的线程,代替线性地重放每条query语句。在日志里为每个连接创建一个单独的线程也是非常重要的,避免线程间的query阻塞。查询日志显示了哪个连接执行了哪条query语句。
即使你还没有构建自己的测试,你可以写下你的测试计划。你可以使测试跑很多遍,你需要重新精确地构建你的测试。也为将来打算。你可能不是下次执行这个测试的人,即使你是,你可能也不太记得你第一次是怎么执行它的。你的计划应该包括测试数据,安装系统的步骤和热启动计划。
设计一些规范参数和结果的方法,并详细地记录每次执行。你的文档方法可能如电子表格或者笔记那么简单,也可能如定做的数据库那么复杂(但是要记住,你要写一些脚本来帮助分析测试结果,所以没有比打开电子表格和文本文件更容易的方法了)。
你可能会发现创建一个测试目录,包含每次执行的结果的子目录,会很有用。在相应地的子目录,你可以放结果,配置文件,和每次执行的笔记。如果你的测试比你预想的多,而且你也很感兴趣,无论如何记录额外的数据。错过记录重要的数据总比不需要的数据要好,可能以后你会发现额外数据非常有用。在测试期间尽可能多地记录附加信息,如CPU的使用情况,磁盘I/O,和网络流量统计;SHOW GLOBAL STATUS的计数器,等等。
获得精确结果
获得精确结果的最好方法是,设计你的测试来回答你想要的问题。你有选择正确的测试吗?你捕捉到你需要的答案的数据了吗?你的测试有错误的标准吗?例如,你有运行一个计算密集型的测试来预测I/O密集型的应用程序的性能吗?
接着,确保你的测试结果可以重复的。尽量确保你的系统在每次开始执行的时候,是处在相同状态的。如果测试很重要,你应当在每次执行后重启系统。如果你需要一个预热过的服务器,正常来讲,你也应当确保你的系统已经有足够长的预热。例如,如果预热过程包含了随机query查询,那么你的测试结果将会不可重复。
如果测试改变了测试数据或数据库模式,在每次执行的时候,用快照重新设置它。向一个表中插入一千行记录和向一个表中插入一百万行记录,不会给出相同的的结果。在磁盘上的数据存储和分布也会使结果不可重复。一个方法是确保物理布局相近,做一个快速的格式和文件拷贝分区。
当心额外负载,优化和监控系统,详细记录日志,计划任务,以及其它因素能使你的结果发生偏移。