读书-高性能MySQL 第二章

第二章 MySQL基准测试

基准测试(Benchmark)是针对系统的一种压力测试。通常的目标是为了掌握系统的行为,评估系统的容量,掌握哪些是重要变化,或者观察系统如何处理不同的数据。基准测试可以在系统实际负载之外创造一些虚构场景进行测试。基准测试可以完成以下工作或者更多:

  • 验证基于系统的一些假设,确认这些假设算法符合实际情况。
  • 重现系统的某些异常行为,以解决这些异常。
  • 测试系统当前运行情况。如果不清楚当前系统的性能,就无法确认某些优化的效果如何。也可以利益历史的基准测试结果来分析诊断一些无法预测的问题。
  • 模拟比当前系统更高的负载,以找出系统随着压力的增加可能遇到的扩展性瓶颈。
  • 规划未来的业务增长。基准测试可以评估在项目未来的负载下,需要什么样的硬件,需要多大容量的网络,以及其他相关资源。这有助于降低系统升级和重大变更的风险。
  • 测试应用适应可变环境的能力。例如,通过基准测试,可以发现系统在随机的并发峰值下的性能表现,或者是不同配置的服务器之间的性能表现。基准测试可以测试系统对不同数据分布的处理能力。
  • 测试不同的硬件、软件和操作系统配置。比如RAID5和RAID10更适合当前系统?如果系统从ATA硬盘升级到SAN存储,对于随机读写性能有什么帮助?Linux2.4系列内核会比2.6系列的可扩展性更好吗?升级MySQL版本能改善性能吗?为当前数据采用不同的存储引擎会有什么效果?所有的这些问题都可以通过专门的基准测试来获得答案。
  • 证明新采购的设备是否配置正确。笔者曾经无数次通过基准测试来对新系统压测,发现了很多错误的配置,以及硬件组件失效等问题。因此在新系统正式上线到生产环境之前进行基准测试是一个好习惯,永远不要相信主机提供商或者硬件提供商所谓的系统以及安装好,并能运行多块的说法。如果可能执行实际的基准测试永远是一个好主意。

基准测试还可以用于其他目的,比如为应用创建单元测试套件。但是本章我们只关注与性能有关的基准测试。

基准测试的一个主要问题在于其不是真实的压力的测试。基准测试施加给系统的压力相对真实的样例来说,通常比较简单。真实的压力是不可预期且变化多端的,有时候会过于复杂而难以理解。所以使用真实的压力测试,可能难以从结果中分析出确切的结论。

基准测试的压力和真实的压力在那些方面不同?有很多因素会影响基准测试,比如数据量、数据和查询的分布,淡最重要的一点还是基准测试通常要求尽可能快地执行完成,所以经常给系统造成过大的压力。在很多案例,我们都会调整个测试工具的最大压力,以在系统可以容忍的压力阈值内尽可能快地执行测试,这对于确定系统的最大容量非常有帮助。然而大部分压力测试工具不支持对压力进行复杂性控制。务必要记住,测试工具自身的局限也会影响到结果的有效性。

使用基准测试进行容量规划也要掌握技巧,不能只根据测试的结果做简单的推断。例如,假设想知道使用新数据库服务器后,系统能够支撑多大的业务增长。首先对原系统进行基准测试。然后对新系统做测试,结果发现新系统可以支持原系统40倍的TPS(每秒事务数),这时候就不能简单的推断说新系统一定可以支持40倍的业务增长。这是因为在业务增长的同时,系统的流量、用户、数据以及不同数据之间的交互都在增长,他们不可能都有40倍的支撑能力,尤其是相互之间的关系。而且当业务增长到40倍时,应用本身的设计可能已经随之改变。可能有更多的新特性会上线,其中某些特性对数据库造成的压力远大于原有功能。而这些压力、数据、关系和特性的变化都很难模拟,所以他们对系统造成的影响也很难评估。

结论就是,我们只能进行大概的测试,来确定系统大致的余量有多少。当然也可以做一些真实的压力测试(和基准测试有区别),但在够着数据集合压力的时候要特别小心,而且这样就不在是基准测试了。基准测试要尽量简单直接,结果之间容易相互比较,成本低且易于执行。尽管有诸多限制,基准测试还是非常有用的(只要搞清楚测试的原理,并且了解如何分析结果所代表的意义)。

2.2 基准测试的策略

基准测试有两种主要的策略:一是针对整个系统的整体测试,另外是单独测试MySQL。这两种策略也被称为集成式(full-stack)以及单组件式(signal-component)基准测试。针对这整个系统做集成式测试,而不是单独测试MySQL的原因主要有以下几点:

  • 测试整个应用系统,包括Web服务器、应用代码、网络和数据库是非常有用的,因为用户关注的并不仅仅是MySQL本身的性能,而是应用整体的性能。
  • MySQL并非总是应用的瓶颈,通过整体的测试可以揭示这一点。
  • 只有对应用做整体测试,才能发现个部分之间的缓存带来的影响。
  • 整体应用的集成式测试更能揭示应该的真实表现,而单独组建的测试很难做到这一点。

另一方面,应用的整体基准测试很难建立,甚至很难正确设置。如果基准测试的设计有问题,那么结果就无法反映真实的情况。从而基于此做的决策也就可能是错误的。

不过,有时候不需要了解整个应用的情况,而只需要关注MySQL的性能,至少在项目初期可以这样做。基于以下情况,可以选择只测试MySQL:

  • 需要比较不同的schema或查询性能。
  • 针对应用中某个具体问题进行测试
  • 为了避免漫长的基准测试,可以通过一个短期的基准测试,做快速的“周期循环”,来检测出某些调整后的效果。

另外,如果能够在真实的数据集上执行重复的查询,那么可以针对MySQL的基准测试也是有用的,但是数据本身和数据集的大小都应该是真实的。如果不能,可以采用生产环境的数据快照。

不幸的是,设置一个基于真实的数据的基准测试复杂而且耗时。如果能得到一份生产数据的拷贝当然很幸运,但这通常不太可能。比如要测试的是一个刚开发的新应用,它只有很少的用户和数据。如果想测试该应用在规模扩张到很大以后的性能表现,就只能通过模拟大量的数据和压力来测试。

2.2.1 测试何种指标

在开始执行甚至是在设计基准测试之前,需要先明确测试的目标。测试目标决定了选择什么样的测试工具和技术,以获取精确而有意义的测试结果。可以将测试结果细化成一系列的问题,比如“这种CPU是否比另外一种要快?”,或“新索引是否比当前索引性能更好”。

有时候需要用不同的方法测试不同的指标。比如针对延迟(latency)和吞吐量(throughput)就需要采用不同的测试方法。

请考虑以下指标,看看如何满足测试的需求。

吞吐量

吞吐量指的是单位时间内的事务处理数。这一直是经典的数据库应用测试指标。一些标准的基准测试被广泛的引用,如TPC-C(参考http://www.tpc.org),而且很多数据库厂商都努力争取在这些测试中取得好的成绩。这类基准测试主要针对在线事务处理(OLTP)的吞吐量,非常适用于多用户的交互式应用。常用的测试单位是每秒的事务数(TPS),有些也采用每分钟的事务数(TPM)。

响应时间或者延迟

这个指标用于测试任务所需要的整体时间。根据具体的应用,测试的时间单位可能是微秒、毫秒、秒或者分钟。根据不同的时间单位可以计算出平均相应时间、最小响应时间、最大相应时间和所占的百分比。最大相应时间通常意义不大,因为测试时间越长,最大相应时间也可能越大。而且其结果通常不可重复,每次测试都可能得到不同的最大相应时间。因此,通常可以使用百分比相应时间(percenttile response time)来替代最大相应时间。例如,如果95%的响应时间都是5毫秒,则表示任务在95%的时间段内都可以在5毫秒之内完成。

使用图表有助于理解测试结果。可以将测试结果绘制成折线图(比如平均值折线或者95%的折线)或者散点图,直观第表现数据结果集的分布情况,通过这些图可以发现长时间测试的趋势。本章后面讲更加详细的讨论这一点。

并发性

并发性是一个非常重要被误解和误用的指标。例如,它经常表示成多少用户在同一时间浏览一个Web站点,经常使用的指标是有多少个会话。HTTP协议是无状态的,大多数用户至上简单第读取浏览器上显示的信息,这并不等同于Web服务器的并发性,而且Web服务器的并发性也不等同于数据库的并发性,而仅仅表示会话存储机制可以处理多少数据的能力。Web服务器的并发性更精确的度量指标,应该是在任意时间内有多少同时发生的并发请求。

在应用的不同环节都可以测量相应的并发性。Web服务器的高并发,一般也会导致数据库的高并发,但服务器采用的语言和工具集对此都会有影响。注意不要将创建数据库连接和高并发高混淆。一个设计良好的应用,同时尅打开成百上千个MySQL服务器的连接,但可能同时只有少数连接唉执行查询。所以说一个Web站点“同时有50000个用户”访问,却可能只有10-15个并发请求到MySQL数据库。

换句话说,并发性的基准测试需要关注的是正在公告中的并发操作,或者是同时工作中的线程的连接数。当并发性增加时,需要测量吞吐量是否下降,相应时间是否变长,如果是这样,应用可能就无法处理峰值压力。

并发性的测量完全不同于相应时间和吞吐量。它不像是一个结果,而更像是设置基准测试的一种属性。并发性通常不是为了测试应用能够达到的并发度,而是为了测试应用在不同并发下的性能。当然,数据库的并发性还是需要测量的。可以通过sysbench指定的32、64或者128个线程的测试,然后在测试期间记录MySQL数据库的Threads_running状态值。在第11章会讨论这个指标对容器规划的影响。

可扩展性

在系统的业务压力可能发生变化的情况下,测量可扩展性就非常必要了。第11章将会进一步讨论可扩展性的话题。简单地说,可扩展性指的是,给系统增加一倍的工作,在理想条件下就能获取两倍的结果(即吞吐量增加一倍)。或者说,给系统增加一倍的资源(比如两倍的CPU数),就可以获得两倍的吞吐量。当然同时性能(响应时间)也必须要在可以接受的范围内。大多数系统是无法做到如此理想的线性扩展的。随着压力的变化,吞吐量和性能都可能会越来越差。

可扩展性能指标对于容量规范非常有用,他可以提供其它测试无法提供的信息来帮助发现应用的瓶颈。比如.如果系统是基于单个用户的响应时间测试设计的,虽然测试结果很好,但当并发度增加的时候,系统的性能有可能变得非常糟糕。而一个基于不断增加用户连接的情况下的相应时间测试可以发现这个问题。

一些任务,不如从细粒度数据创建汇总表的批量工作,需要的是周期性的快速相应时间。当然也可以测试这些任务纯粹的相应时间,但要注意考虑这些任务之间的相互影响。批量工作可能导致相互之间有影响的查询性能变差,反之亦然。

归根结底,应该测试那些对用户来说最重要的指标。因此应该尽可能多的收集一些需求,比如,什么样的相应时间是可以接收的,期待多少的并发性,等等。然后基于这些需求来设计基准测试,避免目光短浅只关注部分指标,而忽略其他指标。

2.3 基准测试方法

在了解基准测试的概念之后,现在可以具体讨论如何设计和执行基准测试。但是在讨论之前先来看一下如何避免一些常见的错误,这些错误可能导致测试结果无用或者不精确。

  • 使用真实数据的子集而不是全集。例如应用需要处理几百GB的数据,但测试自用1GB的数据;或者只使用当前数据进行测试,去希望模拟未来业务大幅度增长后的情况。
  • 使用错误的数据分布。例如使用均匀分布的数据测试,而系统的真实数据有很多热点区域(随机生成的测试数据通常无法模拟真实的数据分布)
  • 使用不真实的分布参数,例如假定所有用户的个人信息(profile)都会被平均地读取。
  • 在多用户场景中,只做单用户测试。
  • 在单服务器上测试分布式应用。
  • 与真实用户行为不匹配。例如Web页面中的“思考时间”。真实用意在请求到一个页面后会阅读一段时间,而不是不停顿地一个接一个的点击相关链接。
  • 反复执行同一查询。真实的查询是不尽相同的,这可能会导致缓存命中率降低。而反复执行同一个查询在某种程度上,会全部或者部分缓存结果。
  • 没有检查错误。如果此时的结果无法得到合理的解释,比如一个本应该很慢的查询突然变快了,就应该检查是否有错误产生。否则可能只是测试了MySQL检查语法错误的速度了,基准测试完成后,一定要检查一下错误日志,这个应该是基本要求。
  • 忽略了系统预热(warm up)的过程。例如系统重启后马上进行测试。有时候需要了解系统重启后需要多长时间才能达到正常的性能容量,要特别留意预热的时长。反过来讲,如果想分析正常的性能,需要注意,若基准测试在重启以后马上启动,则缓存是冷的,还没有数据,这时即使测试的压力相同,得到的结果也和缓存已经装满数据时是不同的。
  • 使用默认服务器配置。第3章将详细讨论服务器的优化配置。
  • 测试时间太短。基准测试需要持续一定的时间。后面会继续讨论这个问题。

只有避免了上述错误,才能走上改进测试质量的慢慢长路。

如果其他条件相同,就应该努力使测试过程尽可能接近真实应用的情况。当然,有时候和真实情况稍有出入问题也不大。例如,实际应用服务器和数据库分别部署在不同的机器。如果采用和实际部署完全相同的配置当然更真实,但也会引入更多变化因素,比如加入了网络负载和速度等。而在单一节点删运行测试相对要容易,找某些情况下也可以接受,那么就可以在单一节点上进行测试。当然,这样的选择需要根据实际情况来分析是否合适。

2.3.1 设计和规划基准测试

规划基准测试的第一步是提出问题并明确目标。然后决定是采用标准的基准测试,还是设计专用的测试。

如果采用标准的基准测试,应该确认选择了合适的测试方案。例如,不要使用TPC-H测试电子商务系统。在TPC的定义中,“TPC-H”是即席查询和决策支持应用的基准测试,因此不适合用来测试OLTP系统。

设计专用的基准测试是很复杂的,往往需要一个迭代的过程。首先需要获得生产数据集的快照,并且该快照很容易还原,以便于进行后续的测试。

然后,针对数据运行查询。可以建立一个单元测试集作为初步的测试,并运行多遍。但是这和真实数据库环境还是有差别的。更好的办法是选择一个有代表性的时间段,比如高峰期的一个小时,或者一整天,记录生产系统上所有查询。如果时间段选的比较小,则可以选择多个时间段。这样有助于覆盖整个系统的活动状态,例如每周报表的查询、或者非峰值时间运行的批处理作业。

可以在不同级别记录查询。例如,如果是集成式(full-stack)基准测试,可以记录Web服务器上的HTTP请求,也可以打开MySQL的查询日志(Query Log)。倘若要重演这些查询,就要确保创建多线程来并发执行,而不是单个现场性执行。对日志中的每个连接都应该创建独立的线程,而不是将所有的查询随机分配到一些线程中。查询日志中记录了每个查询是哪个连接执行的。

即使不需要常见专用的基准测试,详细的写下测试规划也是必要的。测试可能要反复运行,因此需要精确地重现测试过程。而且也应该考虑到未来,执行下一轮测试时可能不是同一个人了。即使是同一个人,也有可能不会确切地记得初次运行时的情况。测试规划应该记录测试数据、系统配置的步骤、如何测量和分析结果,以及预热方案等。

应该建立将参赛和结果文档化的规范,每一轮测试都必须进行详细的记录。文档规范可以很简单,比如电子表格或者记事本,也可以是复杂的自定义数据库。需要记住的是,经常要写一些脚本分析测试结果,因此如果能够给不用打开电子表格或者文本文件等额外操作当然是更好的。

2.3.2 基准测试应该运行多长时间

基准测试应该运行足够长的时间,这一点很重要。如果需要测试系统在稳定状态时的性能,那么当然需要在稳定状态下测试并观察。如果系统有大量的数据和内存,要达到稳定状态可能需要非常长的一段时间。大部分系统都会有一些应对突发情况的余量,能够吸收性能尖峰,将一些工作延迟到高峰期之后执行。但当机器加压足够长的时间之后,这些余量会被消耗掉,系统的短期尖峰也就无法维持原来的高性能。

有时候无法确认测试需要运行多长时间才足够。如果是这样,可以让测试一直运行,持续观察直到确认系统已经稳定。西面是一个在已知系统上执行测试的例子,图2-1显示了系统磁盘读和写吞吐量的时序图。

系统预热完成后,读I/O活动在三四个小时候曲线趋向稳定,但写I/O至少在八个小时内变化还是很大,之后有一些点波动比较大,但读和写总体来说基本稳定了。一个简单的测试规则,就是等系统看起来稳定的时间至少要等于系统预热的时间。本例中的测试持续了72小时才结束,以确保能够体现系统长期的行文。

一个常见的错误的测试方式是,只执行一系列短期的测试,比如每次60秒,并在此测试的基础上去总结系统的性能。我们经常可以听到类似这样的话:“我尝试对新版本做了测试,但是还不如旧版本快”,然后我们分析实际的测试结果发现,测试的方式根本就不足以得出这样的结论。有时候人们也会强调说不可能有时间去测试8或者12个小时,以验证10个不同并发性在两到三个不同版本下的性能。如果没有时间去完成准确完整的基准测试,那么已经花费的所有时间都是一种浪费。有时间要相信别人的测试结果,这总比做一次半拉子的测试来得到一个错误的结论要好。

2.3.3 获取系统性能和状态

在执行基准测试时,尽可能多地收集被测系统的信息。最好为基准测试简历一个目录,并且每执行一块测试都创建单独的子目录,将测试结果、配置文化部、测试指标、脚本和其他相关说明保存其中。即使有些结果不是目前需要的,也应该保存下来了。多余一些数据重臂缺乏重要数据要好,而且多余的数据以后也许会用得到。需要记录的数据包括系统状态和性能指标,诸如CPU使用率、磁盘I/O、网络流量统计,SHOW GLOBAL STAUTS计数器等。

下面是一个收集MySQL测试数据的shell脚本。

这个shell脚本很简单,但提供了一个有效的收集状态和性能数据的框架。看起来好像作用不大,但当需要在多个服务器上执行比较复杂的测试的时候,要回答以下关于系统行为的问题,没有这种脚本就很困难了。下面是这个脚本的一些要点。

#!/bin/sh

INTERVAL=5

PREFIX=$INTERVAL-sec-status

RUNFILE=/home/gugu/running

mysql -e 'SHOW GLOBAL VARIABLES' >> mysql-variables

while test -e $RUNFILE; do

file=$(date+%F_%I)

sleep=$(date+%s.%N | awk "{print $INTERVAL - ($1 % $INTERVAL)}")

sleep $sleep

ts="$(date + "TS %s.%N %F %T")"

loadavg="$(uptime)"

echo "$ts $loadavg" >> $PREFIX-${file}-status

mysql -e 'SHOW GLOBAL STATUS' >> $PREFIX-${file}-status &

echo "$ts $loadavg" >> $PREFIX-${file}-innodbstatus

mysql -e 'SHOW ENGINE INNODB STATUS\G' >> $PREFIX-${file}-innodbstatus &

echo "$ts $loadavg" >> $PREFIX-${file}-processlist

mysql -e 'SHOW FULL PROCESSLIST\G' >> $PREFIX-${file}-processlist &

echo $ts

done

echo Exiting beacuse $RUNFILE does not exist.
  • 迭代是基于固定时间间隔的,每隔5秒运行一次收集的动作,注意这里的sleep的时间有一个特殊的技巧。如果只是简单的在每次循环时插入一条“sleep 5”的指令,循环的执行间隔一般都会稍大于5秒,那么这个脚本就没有办法通过其他脚本和图形简单地捕获时间相关的准确数据。即使有时候循环能恰好在5秒内完成,但是如果某些系统的时间戳是15:32:18.218.192,另外一个是15:32:23.819437,这个时候就比较讨厌了。当然这里的5秒也可以改成其他的时间间隔,比如1、10、30或者60秒。bug还是推荐5秒或者10秒间隔来搜集数据。
  • 每个文件名都包含了改轮测试的开始日期和小时。如果测试持续好几台,那么这个文件可能会非常大,有必要的话需要将文件手动迁移到其他地方,但分析全部结果的时候需要注意从最早的文件开始。如果只需要分析某个时间点上的数据,则可以根据文件名中的日期和小时迅速定位,这比在一个GB以上的大文件中去搜索要快捷的多。
  • 每次抓取数据都会记录当前时间戳,所以可以在文件中搜索某个时间点的数据。也可以写一些awk或者sed脚本简化操作。
  • 这个脚本不会处理或者过滤收集到的数据。先收集所有的原始数据,然后再基于此做分析和过滤是一个好习惯。如果在收集的时候对数据做了预处理,而后续分析发现了一些异常的地方需要用到更多的原始数据,这时候就要“抓瞎”了。
  • 如果需要在测试完成后脚本自动退出,需要删除/home/gugu/running文件即可。

这只是一段简单的代码,或许不能满足全部的要求,但却很好第演示了该如何捕获测试的性能和状态数据,从代码可以看出,只捕获了MySQL的部分数据,如果需要,则很容易通过修改脚本添加新的数据捕获。例如,可以通过pt-diskstats工具捕获/proc/diskstats的数据为后续分析磁盘I/O使用。

2.3.4 获得准确的测试结果

获得准确测试结果的最好办法,是回答一些关于基准测试的基本问题,是否选择了正确的基准测试?是否为问题收集了相关的数据?是否采用了错误的测试标准?例如,,是否对一个I/O密集型(I/O-bound)的应用,采用了CPU密集型(CPU-bound)的测试标准来评估性能。

接着,确认测试结果是否可以重复。每次重新测试之前都要确保系统的状态是一致的。如果是非常重要的测试,甚至有必要在每次测试都重启系统(请参考前面关于基准测试需要运行多长时间的内容)、是否可重复。如果预热采用的随机查询,那摩测试结果可能就不是可以重复的。

如果测试过程找会修改数据或者schem,那摩每次测试前,需要利用快照还原数据。在表中插入1000条记录和插入100万条记录,测试结果肯定不会相同。数据的碎片度和在磁盘上的分布,都可能导致测试是不可重复的。一个确保物理磁盘数据的分布尽可能一致的办法是,每次都进行快熟格式化并进行磁盘分区复制。

要注意很多因素,包括外部的压力、性能分析和监控系统、详细的日志记录、周期性作业,以及其他一些因素,都会影响到测试结果。一个典型的案例,就是测试过程中突然有cron定时作业启动,或者正处在一个巡查读取周期(patrol Read cycle),亦或RAID卡启动了定时的一致性检查等。要确保基准测试运行过程中锁需要的资源是专用于测试的。如果有其他额外的操作,则会消耗网络带宽,或者测试基于的是和其他服务器共享的SAN存储,那么得到的结果很可能是不准确的,

每次测试中修改的参数应该尽量少。如果必须要异常修改多个参数,那么可能会丢失一些信息。有些参数依赖其他参数,这些参数可能无法单独修改。有时候甚至没有意识到这些依赖,这给测试带来了复杂性。

一般情况下,都是通过迭代逐步地修改基准测试的参数,而不是每次运行时都做大量的修改。举个例子,如果要通过调整阐述来创建一个特定的行为,可以通过使用分治法(divide-and-conquer,每次运行时将参数对分减半)来找到正确的值。

很多基准测试都是用来做预测出系统迁移后的性能的,比如从Oracle迁移到MySQL。这种测试通常比较麻烦,因为mysql执行的查询和Oracle完全不同。如果想知道在Oracle运行的好的应用迁移到mysql以后性能如何,通常需要重新设计MySQL的schema和查询(在某些情况下,比如简历一个跨平台的应用时,可能想知道同一条查询是如何在两个平常运行的,不过这种情况并不多见)。

另外,基于MYSQL的默认配置测试没有什么意义,因为默认配置是基于消耗很少内存的极小应用的。有时候可以看到一些MySQL和其他商议数据库的产品的对比测试,结果让人尴尬,可能就是MySQL采用了默认配置的缘故。让人无语的是,这样明显有误的测试结果还容易变成头条新闻。

固态存储(SSD或者PCI-E卡)给记住测试带来了恨到的挑战,第九章将进一步讨论。

最后如果测试结果中出现异常结果,不要轻易当做槐树街点而丢弃。应该认真研究并找到产生这种结果的原因。测试可能会得到有价值的结果,或者一个严重的错误,亦或基准测试的一个设计缺陷。如果对测试记过不了解,就不要轻易公布。有一些案例表明,异常的测试结果往往是由于很小的错误导致的,最后搞得测试无功而返。

2.3.5 运行基准测试并分析结果

一旦准备就绪,就可以开始着手基准测试,手机和分析数据了。

通常来说,自动化基准测试是一个好主意。这一昂做可以获得更精准的测试结果。因为自动化的过程可以防止测试人员偶尔遗漏某些步骤,或者误操作。另外也有助于归档整个测试过程。

自动化的方式有很多,可以是一个MakeFile文件或一组脚本。欧本语言可以根据需要选择shell、PHP、Perl等都可以。要尽可能地使多有测试过程都自动化,包裹装载数据、系统预热、执行测试、记录结果等。

一旦设置了正确的自动化操作,基准测试将成为一步式操作。如果只是针对某些应用做一次性的快熟验证测试,可能就没有必要自动化。但是只要未来可能会引用到测试结果,建议都尽量自动化。否则到时候很可能就搞不清楚是如何获取这个结果的,也不记得采用了什么参数,这样就很难再通过测试重现结果了。

基准测试通常需要测试运行很多次。具体需要运行多少次要看对结果的计分方式,以及测试的重要程度。要提高测试的准确度,就需要多运行几次。一般在测试的实践中,可以取最好的结果值,或者所有结果的平均值,亦或是五个测试结果中最好的三个的平均值,可以根据需要更进一步精确化测试结果。还可以对结果使用统计方法,确定置信区间(confidence interval)等。不过通常来说,不会用到这种程度的确定性测试结果。如果结果变化很大,可以再多运行几次,或者运行更长时间,这样可以获得更确定的结果。

获取测试结果后,还要对结果进行分析,也就是说,要把“数字”变成“知识”。最终的目的是会带在设计测试时的问题。理想情况下,可以或者诸如“升级到4核CPU可以保持相应时间不变的情况下获得超过50%的吞吐量增长”或者“增加说因可以使查询更快”的结论。如果需要更加科学化,建议在测试前读读null hypothesis一书,但大部分情况不会要求做这么严格的基准测试。

如何冲数据中抽象出有意义的结果,依赖于如何收集数据。通常需要写一些脚本来分析数据,这不仅能减轻分析的工作量,而且和自动化基准测试意义可以重复运行,并易于文档化。下面是一个非常简单的shell脚本,演示了如何从前面的数据采集到的数据中抽取时间维度信息。脚本的输入产生就是采集到的数据文件的名字。

#!/bin/sh
# This script converts SHOW GLOBAL STATUS into a tabulated format, one line
# per sample in the input, with the metrice divided by the time slapsed between samples.

awk '

BEGIN{

print "#ts date time load QPS";

fmt = " %.2f"

}

/^TS/ { # The timestamp lines begin with TS.

ts = substr($2, 1, index($2, ".") - 1);

load = NF -2;

diff = ts - prev_ts;

prev_ts = ts;

printf "\n%s %s %s %s", ts, $3, $4, substr($load, 1, length($load)-1);

}

/Queries/ {

printf fmt, ($2-Queries)/diff;

Queries=$2

}

' "$@"


假设该脚本名为analyse,当前的脚本生成状态文件后,就可以运行该脚本,可能会得到如下结果:

第一行是列的名字;第二行的数据应该忽略,因为这是测试实际启动前的数据。接下来的行包含Unix时间戳、日期、时间(注意时间数据是每5s更新一次,前面脚本说明时曾提过)、系统负载、数据库的QPS(每秒查询次数)五列,这应该是用户分析系统性能的最少数据需求了。接下来将演示如何根据正西数据快速绘成图形,并分析基准测试过程中发生了什么。

2.3.6 绘图的重要性

最简单有效的图形,就是讲性能指标按照时间顺序绘制。通过图形可以立刻发现一些问题,而这些问题在原始数据中却很难被注意到。或许你会坚持看测试工具打印出来的平均值或其他汇总过的信息,但平均值有时候是没有用的,他会掩盖掉一些真实情况。幸运的是,前面写的脚本输出都可以定制作为gnuplot或者R绘图的数据来源。假设使用gunplot,假设输出的数据文件名是QPS-per-5-seconds:

gnuplot> plot "QPS-per-5-seconds" using 5 w lines title "QPS"

该gnuplot命令将文件的第五列qps数据绘成图形,图标题是QPS。图2-2是绘制出来的结果图

图2-2 : 基准测试的QPS图形

下面我们讨论一个可以更加体现图形价值的例子。假设MySQL数据正在遭受“疯狂刷新(furious flushing)”的问题,在刷线鹿后与检查点时会阻塞所有的活动,从而导致吞吐量严重下跌。95%的相应时间和平均响应时间都无法发现这个问题,也就是说这两个指标掩盖了问题。但图形会显示出这个周期性的问题,请参考图2-3.

图2-3显示的是每秒钟新清点的交易量(NOTPM,new-order transactions per minute)。从曲线可以看到明显的周期性下降,淡如果从平均值(点状虚线)来看波动很小。一开始的低谷是由于系统缓存是空的,而后面其他的下跌是由于系统刷线脏快到磁盘导致。如果没有图形,要发现这个趋势会比较困难。

2-3: 一个30分钟的dbt2测试结果

这种性能尖刺在压力大的兴中比较常见,需要调查原因。在这个案例中,是由于使用了旧版本的InnoDB引擎,脏块的刷新算法很差。但是这个结论是不能想当然的,需要认真地分析详细的性能统计。在性能下跌时,SHOW ENGINE INNODB STATUS的输出是什么?SHOWFULL PROCESSLIST的输出是什么?应该可以发现InnoDB在持续地刷新脏块,并去阻塞了许多状态是“waiting on query cache lock”的线程,或者其他类似的现象。在执行基准测试的时候要尽可能地收集更多的细节数据,然后将数据绘制成图形,这样可以帮助快熟地发现问题。

2.4 基准测试工具

没必要开发自己的基准测试系统,除非现有的工具确实无法满足需求。下面的章节会介绍一些可用的工具。

2.4.1 集成测试工具

回忆一下前面提供的两种测试类型:集成式测试和单组件测试。毫不奇怪,有些工具是针对整个应用进行的测试,也有些工具是针对MySQL或者其他主键单独进行测试的。集成式测试,通常获得整个应用概况的最佳手段。已有的集成式测试工具如下所示。

ab

ab是一个Apache HTTP服务器基准测试工具。他可以测试HTTP服务器每秒可以满足多少请求。这个是非常简单的工具,用途也优先,只能针对单个URL进行尽可能快的压力测试。关于ab更多信息可以参考http://httpd.apache.org/docs/2.4/programs/ab.html

http_load

这个工具概念上与ab类似,也被设计对Web服务器进行测试,但比ab要更加灵活。可以通过输入文件提供多个URL,http_load在这些URL中随机选择进行测试。也可以定制http_load,使其按照时间比率进行测试,而不仅仅是测试最大请求处理能力。更多信息可以参考http_load

JMeter

JMeter是一个Java应用程序,可以加载其他应用并测试其性能。它虽然是设计用来测试Web应用的,淡也可以用于测试其他诸如FTP服务器,或者通过JDBC进行数据库查询测试。

JMeter比ab和http_load都要复杂的多。例如,他可以通过控制预热时间等参数,更加灵活地模拟真实用户的访问。JMeter用于绘图接口(带有内置的图形化处理功能),还可以对测试进行记录,然后离线重演测试结果。更新内容可以参考Apache JMeter - Apache JMeter™

2.4.2 单组件式测试工具

有一些有用的工具可以测试MySQL和基于MySQL的系统性能。2.5节将演示如何利用这些工具进行测试。

mysqlslap

mysqlslap(MySQL :: MySQL 8.0 Reference Manual :: 4.5.8 mysqlslap — A Load Emulation Client)可以模拟服务器的负载,并输出计时信息。测试时可以执行并发链接数,并指定SQL语句(可以在命令行上执行,也可以报SQL语句写入到参数文件中)。如果没有指定SQL语句,mysqlslap会自动生成查询schema的SELECT语句。

MySQL Benchmark Suit(sql-bench)

在MySQL的发行包中也提供了一款自己的基准测试套件,可以用于在不同数据库服务前上进行比较测试。他说单线程的,主要用于测试服务器执行查询的速度。结果会显示那种类型的查询会在服务器上执行得更快。

这个参数套件的主要好处是包含了大量的预定义测试,容易使用,所以可以很轻松地比较不同存储引擎或者不同配置的性能测试。其也可以用于高层次测试,比较两个服务器总体性能。当然也可以只执行预定义测试的子集(例如只测试UPDATE的性能)。这些测试大部分是CPU密集型的,淡也有些短时间的测试需要大量的磁盘I/O操作。

这个套件的最大缺点主要有:他说单用户模式的,测试的数据集很小且用户无法使用指定的数据,并且同一测试多次运行的结果可能会相差很大。因为是单线程且串行测试的,所以同一个测试多次运行的结果可能会相差比较大。因为是单线程并且串行执行的,所以无法测试多CPU的能力,只能用于比较单CPU服务器的性能差别。使用这个套件测试数据库服务器还需要Perl和BDB的支持,相关参考MySQL :: MySQL 8.0 Reference Manual :: 8.13.2 Using Your Own Benchmarks

Super Smack

Super Smack(MySQL Super Smack)是一款用于MySQL和PostgreSQL的基准测试工具,可以提供压力测试和负载生成。这是一个复杂而强大的工具,可以模拟多用户访问,可以加载测试数据到数据库,并支持水机数据填充测试表,测试定义在smack文件夹中,smack文件使用一种简单的语法定义测试的客户端、表、查询等测试要素。

Database Test Suite

DataBase Test Suite是由开源软件开发实验室(OSDL, Open Source Development Labs)设计的,发布在SourceForge网站(Database Test Suite download | SourceForge.net)上,这是一款类似某些工业标准测试的测试工具集,例如事务处理性能委员会(TPC,Transaction Processing Performance Council)制定的各种标准。其中dbt2就是一款免费的TPC-C OLTP测试工具(未认证)。

Percona's TPCC-MySQL Tool

本书作者开发的一个类似于TPC-C的基准测试工具集,其中有部分是专门为了MySQL测试开发 的。在评估大压力选的MySQL的一些行为时,会经常利用这个工具进行测试(简单的测试一般会采用sysbench替代)。工具源码可以GitHub - Percona-Lab/tpcc-mysql下载,里面有一个鸡蛋的说明。

sysbench

sysbench(GitHub - akopytov/sysbench: Scriptable database and system performance benchmark)是一款多线程系统压力测试工具,他可以根据影响数据库服务器性能的各种因素来评估系统的性能。例如用来测试文件I/O、数据库系统调度器、内存分配和传输速度、POSIX线程,以及数据库服务器等。sysbench支持Lua脚本语言(The Programming Language Lua),Lua对于各种测试场景的设置可以非常灵活。sysbench是我们非常喜欢的一种全能测试工具,支持MySQL、操作系统和硬件的硬件测试。

MySQL的BENCHMARK()函数

MySQL有一个内置的BENCHMARK()函数,可以测试某些特定操作的执行速度。参数可以是需要执行的次数和表达式。表达式可以是任何的标量表达式,比如返回值是标量的子查询或者函数。该函数可以很方便地测试某些特定操作的性能,比如通过测试可以发现,MD5()函数比SHA1()函数要快:

mysql> set @input := 'hello World';

mysql> SELECT BENCHMARK(1000000, MD5(@input));

mysql> SELECT BENCHMARK(1000000, SHA1(@input));

执行后的返回值永远是0,但可以通过客户端返回的时间来判断执行的时间。在这个例子中可以看到MD5()执行的比SHA1()要快。使用BENCHMARK()函数来测试性能,需要清楚地知道原理,否则很容易误用。资格函数只是简单地返回服务器执行表达式的时间,而不会涉及分析和优化的开销。而且表达式必须像例子一样包含用户定义的变量,否则多长执行通盈的表达式会因为系统缓存命中而影响查询结果。

虽然BENCHMARK()函数用起来很方便,但不适合用来做真正的基准测试,因为很那理解真正要测试的是什么,而且测试的只是整个执行周期中的一部分环节。

2.5 基准测试案例

本章节将演示一些利用上面提到的基准测试工具进行测试的真实案例。这些案例可以作为入门的开端。

2.5.1 http_load

下面通过一个简单的例子来演示如何使用http_load.首先创建一个urls.txt文件,输入如下url:

百度一下,你就知道

http_load最简单的方法就是循环请求URL列表。测试程序将以最快的速度请求这些URL:

$ http_load -parallel 1 -seconds 10 urls.txt

测试结果很容易理解,只是简单地输出了请求的统计信息。下面的另外一个稍微复杂的测试,还是尽可能地玄幻请求给定URL列表,bug模拟同时又五个并发用户在进行请求:

$ http_load -parallel 5 -seconds 10 urls.txt

另外,除了测试最快的速度,也可以根基预估的访问请求率(比如每秒5次)来做压力模拟测试

$ http_load -rate 5 -seconds 10 urls.txt

最后还可以模拟更大的负载,可以将访问请求率提高到每秒20次请求。请注意,链接和请求的响应时间都会随着负载的提高而增加。

$ http_load -rate 20 -seconds 10 urls.txt

2.5.2 MySQL基准测试套件

MySQL基准测试套件(MySQL Benchmark Suite)是由一组Perl开发的基准测试工具组成。在MySQL安装路径下的sql-bench子目录包含了该工具。比如在Debian GNU/Linux系统上,默认路径是/usr/share/mysql/sql-bench

如果要运行全部测试,可以使用如下命令:

$ cd /usr/share/mysql/sql-bench/

sql-bench$ ./run-all-tests --server=mysql --user=root --log --fast

Test finished. You can find the result in:

output/RUN-mysql_fast-Linux_2.4.18_686_smp_i686

运行全部测试需要比较长的时间。如果指定--log命令行,则可以监控到测试的进度。测试的结果都保存在output子目录中,每项测试的结果文件中都会包含一系列冯操作计时信息。下面的一些例子:

count_distinct_group_on_key(1000:6000)测试花费34S(wallclock secs),这是客户端运行测试花费的总时间;其他值(包括usr,sys,cursr,csys)则占用的测试的0.28s开销,这是运行客户端测试代码所花费的时间,而不是等待MySQL服务器的相应时间。而测试者真正需要关心的测试结果,是除客户端控制的部分,即实际运行时间33.72秒。

除了运行的全部测试集外,也可以选择单独执行其中的部分测试项。例如可以选择只执行insert测试,这样会比运行全部测试集是得到的会中信息给出更多的详细信息。

2.5.3 sysbench

sysbench可以执行多种类型的基准测试,他不仅设计用来测试数据库的性能,也可以测试运行数据库的服务器的性能。实际上,Peter和Vadim最初设计这个工具是用来执行MySQL性能测试的(尽管不能完成所有的MySQL基准测试)。线面先演示一些非MySQL的测试场景,来测试各个子系统的性能,这些测试可以用来评估系统的整体性能瓶颈,后面在演示如果测试数据库的性能。

在使用MySQL用户的工具包中,这应该是最有用的工具之一。尽管有其他测试工具也可以代替sysbench的某些功能,但那些工具有时候并不可靠,获取到的结果也不一定和MySQL性能相关。例如,I/O性能测试可以用iozone、bonnie++等一系列工具,但是需要注意设计场景,以便模拟InnoDB的磁盘I/O模式。而sysbench的I/O测试则和InnoDB的I/O模式非常类似,所以fileio选项是非常好用的。

sysbench的cpu基准测试

最典型的子系统测试就是CPU基准测试。该测试使用64位整数,测试计算素数直到某个最大值锁需要的时间。下面的例子将比较两台不同的GNU/Linux服务器上的测试结果。

第一台机器的CPU配置如下:

这台服务器上测试如下

第二台服务器配置了不同的CPU:

测试结果如下:

测试的结果简单打印出了计算出素数的时间,在上面的测试中第二台服务器参数结果显示比第一台快两倍。

sysbench的文件I/O基准测试

文件IO(fileio)基准测试可以测试系统在不同的I/O负载下的性能。这对于比较不同的磁盘驱动器、不同的RAID卡、不同的RAID模式,都是很有帮助。可以依据测试结果类调整I/O子系统。文件I/O基准测试模拟了很多InnoDB的I/O特性。

测试的第一步准备(prepare)阶段,生成测试用到的数据文件,生成的数据文件至少要比内存大。如果文件中数据能完全放入年内存中,则操作系统缓存大部分数据,导致测试记过无法重现I/O密集型的工作负载。首先通过下面命令创建一个数据集

$ sysbench --test=fileio --file-total-size=150g prepare

这个命令会在当前工作目录下创建测试文件,后续的运行(run)阶段将通过读写这些文件进行测试。第二步就是运行(run)阶段,针对不用的I/O类型有不同的测试选项:

seqwr

顺序写入。

seqrewr

顺序重写。

seqrd

顺序读取。

rndrd

随机读取

rndwr

随机写入。

rdnrw混合随机读/写

下面的命令运行文件I/O混合随机读/写基准测试:

$ sysbench --test=fileio --file-total-size=150G --file-test-model=rndrw --init-rng=on --max-time=300 --max-requests=0 run

结果如下:

输出结果中包含了大量的信息。和I/O子系统密切相关的包括每秒请求数和吞吐量。上面的例子中每秒的请求数是3.4948MB/sec。另外,时间信息也非常有用,尤其是大约95%的时间分布。这些数据对于评估磁盘性能十分有用。

测试完成后运行清除(cleanup)操作删除第一步生成的测试文件:

$ sysbench --test=fileio --file-total-size=150G cleanup

sysbench的OLTP基准测试

OLTP基准测试模拟了一个鸡蛋的事务处理系统的工作负载。下面的例子使用的是一张超过百万行记录的表,第一步是先生成这张表:

生成测试数据只需要上面么这条简单的命令即可。接下来可以运行测试,这个例子采用了8个并发线程,只读模式,测试时长60秒:

如上所示,结果中包含了相当多的信息。其中最有价值的信息如下:

  • 总的事务数
  • 每秒事务数
  • 时间统计信息(最小、平均、最大相应时间,以及95%百分比相应时间)
  • 线程公平性统计信息(thread-faireness),用于表示模拟负载的公平性。

这个例子使用的是sysbench第四版,在SourceForget.net可以下载到这个版本编译好的可执行文件。也可以从Launchpad下载最新的第五版的源代码自行编译。

sysbench的其他特性

sysbench还有一些其他的基准测试,淡和数据库性能没有直接的关系。

内存(memory)

测试内存的连续读写性能。

线程(thread)

测试线程调度器的性能。对于高负载情况下测试线程调度器的行为很有用。

互斥锁(mutex)

测试互斥锁的的性能,方式是mini所有线程在同一时刻并发运行,并都短暂请求互斥锁(互斥锁mutex是一种数据结构,用来对某些资源进行排他性访问控制,防止因并发访问导致问题)。

顺序写(seqwr)

测试顺序写的性能。这对于测试系统的实际性能瓶颈很重要。可以用来测试RAID控制器的高速缓存的性能状况,如果测试结果异常则需要引起重视。例如,如果RAID控制器写缓存没有电池保护,而磁盘的压力达到了3000次请求/秒,这就是一个问题,数据可能是不安全的。

另外,处理指定测试参数(--test)外,sysbench还有其他很多参数,比如--num-threads、--max-requests和--max-time参数,更多信息可以查阅相关文档。

2.5.4 数据库超时套件中的dbt2 TPC-C测试

数据库测试套件(Database Test Suit)中的dbt2是一款免费的TPC测试工具。TPC-CsTPC组织发布的一套测试规范,用于模拟测试复杂的在线事务处理系统(OLTP)。他的测试结果包括每分钟事务数(tpmC),以及没食物的成本(Price/tmpC)。这种测试的结果非常依赖硬件配置信息。

测试步骤如下:

  1. 准备测试数据

下面的命令会在指定的目录创建用于10个仓库的数据。每个仓库使用大于700MB磁盘空间,测试所需要的总的磁盘空间和仓库的数量成正比。因此,可以通过-w参数来调整厂库的个数以生成合适大小的数据集。

  1. 加载数据到MySQL数据库

下面的命令创建一个名为dbt2w10的数据库,并将上一步生成的测试数据加载到数据库中(-d参数指定数据库,-f参数指定参数数据所在的目录)。

# scripts/mysql/mysql_load_db.sh -d db2w10 -f /mnt/data/dbt2-w10 -s /var/lib/mysql/mysql.sock

  1. 运行测试

最重要的结果是输出信息中末尾处的一行:

这里显示了系统每分钟可以处理的最大事务数,越大越好(new-order并非一种事务类型的专用于)

可以使用不同的参数定制不同的基准测试。

-c

到数据库的连接数。修改该阐述可以模拟不同程度的并发性以测试系统的可扩展性,

-e

启动零延迟(zero-delay)模式,这意味着在不同的查询之间没有时间延迟。这可以对数据库施加更大的压力,但不符合真实情况。因为真实的用户在执行一个新查询之前总需要一个“思考时间(think time)”。

-t

基准测试的持续时间。这个阐述应该精心设置,否者可能导致测试的结果是无意义的。对于I/O密集型的基准测试,太短的次序时间会导致错误的结果,因为系统可能还没有猪狗的时间对缓存进行预热。而对于CPU密集型的基准测试这个时间不应该设置得太长;否者生成的数据量过大,可能转换成I/O密集型。

这种基准测试的记过,可以比单纯的性能呢测试提供更多的信息。例如,如果发现测试有很多回滚现象,那么就可以判定很可能什么地方出现错误了。

2.5.5 Percona的TPCC-MySQL测试工具

尽管sysbench的测试简单,并且结果也具有可比性,但毕竟无法mini真实的业务压力。相比而言,TPC-C测试则能模拟真实压力。2.5.4节提到dbt2是TPC-C的一个很好的表现,但还有一些不足之处。为了瞒住很多大型基准测试的需求,本书的作者重新开发了一款新的类TPC-C测试工具,代码放在Launchpad上。

使用这个参数工具,需要创建数据库和表结构、加载数据、执行测试三个步骤,。数据库和表结构通过包含子啊源码中的SQL脚本创建。加载数据通过C写的tpcc_load工具完成,该工具需要执行编译。加载数据需要执行一段时间,并且产生大量的输出信息(一般将此信息重定向到文件中,保留历史信息)。下面的例子显示了配置过程,创建了一个小型(五个厂库)的测试数据集,数据库名为tpcc5。

然后使用tpcc_start 工具开始执行基准测试。其同样会产生很多输出信息,还是建议重定向到文件中,下面例子,使用五个线程操作五个仓库,30秒预热时间,30秒测试时间

最后一行就是测试结果:没分钟执行完的事务数。如果紧挨着最后一行前发现有异常结果输出,比如有关约束检查的信息,那么可以检查一下相应时间的直方图,或者通过其他详细输出信息寻找线索。当然最好使用本章起那么提到的脚本,这样可以容易获取测试期间详细的诊断数据和性能数据。

2.6 总结

要熟悉sysbench。可以先学习使用oltp和fileio测试。oltp基准测试可以很方便比较不同系统的性能。另外一方面,文件系统和磁盘的基准测试,则可以在系统出现问题时有效地诊断和隔离异常的主键。通过这样的基准测试,我们嫩多次发信啊一些数据管理员说法存在问题,比如DAN存储真的出现一块坏盘,或者RAID控制器的缓存策略的配置并不是像工具中显示的那样。通过对单块磁盘进行基准测试,如果发现每秒可以执行14000次随机读,那要吗是碰到了严重的错误,要么是配置出现了问题。

如果要经常执行基准测试,那么制定一些原则是很有必要的。可以选择合适的测试工具并深入学习。建立一个脚本库,用于配置基准测试,搜集输出结果、系统性能和状态信息,以及分析结果。熟悉一种绘图工具,尽早和多的使用绘图工具,来发现和测试系统的问题。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值