MySQL 基准测试

用户头像
多选参数
关注
发布于: 2020 年 08 月 16 日
MySQL 基准测试



前言:第二章基准测试的内容有理论也有实践,这里村民并没有时间和精力为大家展现一些实践数据,仅摘录理论部分。如果大家对实践有兴趣,可以自行学习,肯定也是不错的经验。



基准测试 ( benchmark ) 是针对系统设计的一种压力测试,通常的目标是为了掌握系统的行为,但也有其他原因,如重现某个系统状态,或者是做新硬件的可靠性测试。本章将讨论 MySQL 和基于 MySQL 的应用的基准测试的重要性、策略和工具。



2.1 为什么需要基准测试



基准测试是唯一方便有效的、可以学习系统在给定的工作负载下会发生什么的方法。基准测试可以观察系统在不同压力下的行为,评估系统的容量,掌握哪些是重要的变化,或者观察系统如何处理不同的数据。基准测试可以在系统实际负载之外创造一些虚构场景进行测试。基准测试可以完成以下工作,或者更多:



  • 验证基于系统的一些假设,确认这些假设是否符合实际情况。

  • 重现系统中的某些异常行为,以解决这些异常。

  • 测试系统当前的运行情况。

  • 模拟比当前系统更高的负责的负载,以找出系统随着压力增加而可能遇到的扩展性瓶颈。

  • 规划未来的业务增长。

  • 测试应用适应可变环境的能力。

  • 测试不同的硬件、软件和操作系统配置。

  • 证明新采购的设备是否配置正确。



基准测试还可以用于其他目的,比如为应用创建单元测试套件。



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



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



在使用基准测试进行容量规划时也要掌握技巧,不能只根据测试结果做简单的推断。基本上,我们只能进行大概的测试来确定系统大致的余量有多少。当然也可以做一些真实压力测试(和基准测试有区别),但在构造数据集和压力的时候要特别小心,而且这样就不再是基准测试了。基准测试要尽量简单直接,结果之间容易相互比较,成本低且易于执行。尽管有很多限制,基准测试还是非常有用的。



2.2 基准测试的策略



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



  • 测试整个应用系统,包括 Web 服务器、应用代码、网络和数据库是非常有用的,因为用户关注的并不仅仅是 MySQL 本身的性能,而是应用整体的性能。

  • MySQL 并非总是应用的瓶颈,通过整体测试可以揭示这一点。

  • 只有对应用做整体测试,才能发现各部分之间的缓存带来的影响。

  • 整体应用的集成式测试更能揭示应用的真实表现,而单独组建的测试很难做到这一点。



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



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



  • 需要比较不同的 schema 或查询的性能。

  • 针对应用中某个具体问题的测试。

  • 为了避免蛮长的基准测试,可以通过一个短期的基准测试,做快速的 ” 周期循环 “ 来检测出某些调整后的效果。



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



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



2.2.1 测试何种指标



在开始执行甚至是在设计基准测试之前,需要先明确测试的目标。测试目标决定了选择什么样的测试工具和技术,以获得精确而有意义的测试结果。有时候需要用不同的方法测试不同的指标。比如,针对延迟 ( latency ) 和吞吐量 ( throughput ) 就需要采用不同的测试方法。请考虑以下指标,看看如何满足测试的需求。



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

  • 响应时间或者延迟:这个指标用于测试任务所需的整体时间。根据具体的应用,测试的时间单位可能是微秒、毫秒、秒或者分钟。根据不同的时间单位可以计算出平均响应时间、最小响应时间、最大响应时间和所占百分比。最大响应时间通常意义不大,因为测试时间越长,最大响应时间也可能越大,而且其结果通常不可重复,每次测试都可能得到不同的最大响应时间。因此,通常可以使用百分比响应时间 ( percentile response time ) 来替代最大响应时间。例如,如果 95% 的响应时间都是 5 毫秒,则表示任务在 95% 的时间段内都可以在 5 毫秒内完成。使用图标有助于理解测试结果,可以将测试结果绘制成折线图或者散点图,直观地表现数据结果集的分布情况,也可以通过这些图发现长时间测试的趋势。

  • 并发性:并发性基准测试需要关注的是正在工作中的并发操作,或者是同时工作中的线程数或者连接数。当并发性增加时,需要测量吞吐量是否下降,响应时间是否变长,如果是这样,应用可能就无法处理峰值压力。并发性的测量完全不同于响应时间和吞吐量,它不像是一个结果,而更像是设置基准测试的一种属性。并发性测试通常不是为了测试应用能达到的并发度,而是为了测试应用在不同并发下的性能。当然,数据库的并发性还是需要测量的,可以通过 sysbench 指定 32、64 或者 128 个线程的测试,然后在测试期间记录 MySQL 数据库的 Threads_running 状态值。

  • 可扩展性:在系统的业务压力可能发生变化的情况下,测试可扩展性就非常必要了。简单来说,可扩展性指的是给系统增加一倍的工作,在理想情况下就能获得两倍的结果(即吞吐量增加一倍),或者说给系统增加一倍的资源(比如两倍的 CPU 数),就可以获得两倍的吞吐量。当然,同时性能(响应时间)也必须在可以接受的范围内。大多数系统是无法做到如此理想的线性扩展的。随着压力的变化,吞吐量和性能都可能越来越差。可扩展性指标对于容量规范非常有用,它可以提供其他测试无法提供的信息来帮助发现应用的瓶颈。



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



2.3 基准测试方法



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



  • 使用真实数据的子集而不是全集。例如应用需要处理几百 GB 的数据,但测试只有 1GB 数据,或者只使用当前数据进行测试,却希望模拟未来业务大幅度增长后的情况。

  • 使用错误的数据分布。例如使用均匀分布的数据测试,而系统的真实数据有很多热点区域(随机生成的测试数据通常无法模拟真实的数据分布)。

  • 使用不真实的分布参数。例如假定所有用户的个人信息都会被平均地读取。

  • 在多用户场景中,只做单用户的测试。

  • 在单服务器上测试分布式应用。

  • 与真实用户行为不匹配。

  • 反复执行同一查询。真实的查询是不尽相同的,这可能会导致缓存命中率降低,而反复执行同一查询在某种程度上会全部或者部分缓存结果。

  • 没有检查错误。如果测试的结果无法得到合理的解释,比如一个本应该很慢的查询突然变快了,就应该检查是否有错误产生,否则可能只是测试了 MySQL 检测语法错误的速度了。基准测试完成后,一定要检查一下错误日志,这应当是基本的要求。

  • 忽略了系统预热 ( warm up ) 的过程。例如系统重启后马上进行测试,有时候需要了解系统重启后需要多长时间才能达到正常的性能容量,要特别留意预热的时长。反过来说,如果想要分析正常的性能,需要注意的是,若基准测试在重启以后马上启动,则缓存是冷、还没有数据,这时即使测试的压力相同,得到的结果也和缓存已经装满数据时是不同的。

  • 使用默认的服务器配置。

  • 测试时间太短。基准测试需要持续一定的时间。



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



2.3.1 设计和规划基准测试



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



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



设计专用的基准测试是很复杂的,往往需要一个迭代的过程。首先需要获得生产数据集的快照,并且该快照很容易还原,以便进行后续的测试。然后针对数据运行查询。可以建立一个单元测试集作为初步的测试,并运行多遍,但是这和真是的数据库环境还是有差别的,更好的办法是选择一个有代表性的时间段,比如高峰期的一个小时或者一整天,记录生产系统上的所有查询。如果时间段选得比较小,则可以选择多个时间段。这样有助于覆盖整个系统的活动状态,例如每周报表的查询或者非峰值时间运行的批处理作业。



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



即使不需要创建专用的基准测试,详细地写下测试规划也是必需的。测试可能要多次反复运行,因此需要精确地重现测试过程,而且也应该考虑到未来,执行下一轮测试时可能已经不是同一个人了。即使还是同一个人,也有可能不会确切地记得初次运行时的情况。测试规划应该记录测试数据、系统配置的步骤、如何测量和分析结果,以及预热的方案等。应该建立将参数和结果文档化的规范,每一轮测试都必须进行详细记录。文档规范可以很简单,比如采用电子表格或者记事本形式,也可以是复杂的自定义的数据库。需要记住的是,经常要写一写脚本来分析测试结果,因此如果能够不用打开电子表格或者文本文件等额外操作,当然是更好的。



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



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



2.3.3 获取系统性能和状态



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



2.3.4 获得准确的测试结果



获得准确测试结果的最好办法是回答一些关于基准测试的基本问题:是否选择了正确的基准测试?是否为问题收集了相关的数据?是否采用了错误的测试标准?



接着确认测试结果是否可重复。每次重新测试之前要确保系统的状态是一致的。如果是非常重要的测试,甚至有必要每次测试都重启系统。一般情况下,需要测试的是经过预热的系统,还需要确保预热的时间足够长、是否可重复。如果预热采用的是随机查询,那么测试结果可能就是不可重复的。



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



很多因素,包括外部的压力、性能分析和监控系统、详细的日志记录、周期性作业以及其他一些因素,都会影响到测试结果。在每次测试中,修改的参数应该尽量少。如果必须要一次修改多个参数,那么可能会丢失一些信息。有些参数依赖其他参数,这些参数可能无法单独修改。有时候甚至都没意识到这些依赖,这给测试带来了复杂性。一般情况下都是通过迭代逐步地修改基准测试的参数,而不是每次运行时都做大量的修改。举个例子,如果要通过调整参数来创造一个特定行为,可以通过使用分治法 ( divide-and-conquer, 每次运行时将参数对分减半 ) 来找到正确的值。



另外,基于 MySQL 的默认配置的测试没有什么意义,因为默认配置是基于消耗很少内存的极小应用的。最后,如果测试中出现异常结果,不要轻易当做坏数据点而丢弃。应该认真研究并找到产生这种结果的原因。测试可能会得到有价值的结果,或者一个严重的错误,抑或基准测试的设计缺陷。



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



一旦准备就绪,就可以着手基准测试,收集和分析数据了。通常来说,自动化基准测试是个好主意。这样做可以获得更精确的测试结果,因为自动化的过程可以防止测试人员偶尔遗漏某些步骤,或者误操作。另外也有助于归档整个测试过程。



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



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



基准测试通常需要运行多次。具体需要运行多少次要看对结果的记分方式,以及测试的重要程度。要提高测试的准确度,就需要多运行几次。一般在测试的实践中,可以取最好的结果值,或者所有结果的平均值,抑或从五个测试结果里取最好三个值的平均值。可以根据需要更进一步精确化测试结果,还可以对结果使用统计方法,确定置信区间等。不过通常来说,只要测试的结果能满足目前的需求,简单地运行几轮测试,看看结果的变化就可以了。如果结果变化很大,可以再多运行几次,或者运行更长的时间,这样都可以获得更准确的结果。



获得测试结果后,还需要对结果进行分析,也就是说,要把 “ 数字 ” 变成 “ 知识 ”。最终的目的是回答在设计测试时的问题。如何从数据中抽象出有意义的结果,依赖于如何收集数据。通常需要写一写脚本来分析数据,这不仅能减轻分析的工作量,而且和自动化基准测试一样可以重复运行,并易于文档化。



发布于: 2020 年 08 月 16 日 阅读数: 56
用户头像

多选参数

关注

微信公众号:多选参数,不关注一波? 2018.09.20 加入

多选参数专注于各种技术的分享,目前专注于基础知识(数据结构与算法、操作系统)、通用技术(Git、MySQL、Redis、可读代码编写)、后端技术(Java)的分享。

评论

发布
暂无评论
MySQL 基准测试