基准测试

0x00 基准测试的目的及策略

基准测试(benchmark)主要用来观察系统在不同压力下的行为,评估系统的容量,就像你新买了个电脑或手机总要跑跑分跟别人比一比这样。

0x00 目的

  • 重现异常行为

  • 测试系统当前的运行状况和分析系统性能

  • 模拟更高的负载,以确定当前系统会不会承受未来业务的增长

  • 优化/调整后的性能对比,例如:新的索引会不会比旧的索引要快

0x01 策略

主要有两种策略集成式(full-stack)单组件式(single-component)

集成式测试所应用的场景:

  • 想知道整个系统真实的性能表现,比如一整套的web服务,到底当前的配置可以支撑得了多少的并发访问量

  • 想知道某个应用具体是哪一部分拖累了性能,因为MySQL并不总是某个应用性能的瓶颈,有可能是你后端代码写的不行等等

单组件式测试所应用的场景:

  • 调整/优化了一些代码,想快速知道调整后的效果

  • 针对某一具体问题的测试

  • 需要对不同的查询进行性能比较

测试对象:

如果能够在真实的数据集上执行重复的查询,那么得到的MySQL基准测试结果就应该是真实的,换句话说就是采用真实生产环境上的数据快照,当然这是不太可能的

所以大部分情况下我们只能使用模拟的大量数据(随机生成)来进行

0x01 基准测试的指标

测试指标的选择要依赖于测试的需求,打个比方,我想知道新的索引是不是比旧的索引执行速度快,那我我测MySQL服务器的并发连接数,这肯定是没有意义的,我更应该是去测他的吞吐量以及响应时间和延迟等等

0x00 吞吐量(throughput)

吞吐量指的是单位时间内的事务处理数,比如每秒钟处理的事务量(TPS)或每分钟处理的事务量(TPM

吞吐量测试非常适用于多用户的交互式系统

0x01 响应时间或延迟(latency)

这个指标用于测试任务所花费的整体的时间,经过N轮的测试,我们可以得出某个任务的最小响应时间、平均响应时间和最大响应时间以及百分比响应时间

最大响应时间:这个用处不大,因为测试的时间越长,最大响应时间可能就越大,而且结果通常不可重复

百分比响应时间: 概念很难说,就比如:95%的请求可以在5ms之内完成(95%的请求的响应时间为5ms)

0x02 并发性(concurrency)

首先会话数不等于Web服务器的并发数!!!,解释如下:

有N个用户在同一时间浏览同一个web站点,这个指标叫做会话,但是HTTP协议是无状态的,这个情况下可能很多用户只是简简单单地浏览网站上的内容,而不是向服务器发送一个请求

其次Web服务器的并发性不等同于数据库的并发性!!!

Web服务器的并发性只能代表服务器的会话存储机制可以处理多少数据

举个例子,有些页面是静态的,他不需要访问数据库,而有些页面是动态的,他需要访问数据库获取信息,web服务器的并发性相当于访问静态的和访问动态的页面的总和,但是访问静态页面是不需要访问数据库的,所以Web服务器的并发性不等同于数据库的并发性

还有啊不能用连接数来衡量并发性!!!

应用程序在连接MySQL时往往会先打开一个连接,不能用这个连接数来衡量并发性,因为有的连接是空闲的,也就是这个连接虽然建立了,但他没有向服务器提出查询或其他请求

并发性关注的是正在工作连接数或线程数

这个具体是什么意思呢,就比如说我用sysbench指定128个线程发起请求,然后在MySQL中看Threads_running的状态值的浮动,这个状态值的浮动反应的就是MySQL的并发性

0x03 可扩展性(Scalability)

可扩展性主要用于测试业务压力发生变化的情况下系统的各项指标,就比如给系统增加一倍的工作量,理想状况下系统的吞吐量也应该增加一倍,或者给系统增加一倍的CPU性能,理想状况下系统吞吐量也会增加一倍,但是现实中不可能发生这样的线性扩展关系,往往是随着压力的变化,吞吐量和性能都可能越来越差

0x02 基准测试的方法

0x00 原则

  • 使用与真实数据量同等大小的数据集避免使用估算的方法,比如应用需要处理几TB的数据,我从中选取出1GB来测一测

  • 使用与真实数据相同的数据分布,有些数据可能是热点数据经常访问,但有些数据也可能很长时间不动,测试时不能仅测试热点数据访问的指标

  • 使用真实的分布参数,要避免分布参数不真实,就比如说微博的个人信息页面,微博大V的个人信息可能一天被浏览数百万次,而我的微博个人信息页面几年都有可能没人访问,如果我测试的时候假定每个人的个人信息都是平等的都是一天被访问1000次,这样肯定是不合理的

  • 匹配真实的用户行为,我相信,除了爬虫之外没有人会在1s之内一个一个地点开不同的链接,真实的用户往往会阅读一段时间,再打开下一个链接

  • 匹配真实的场景,不能在单服务器上测试分布式应用

  • 不能忽略系统预热(warm up)的过程,服务器刚重启完,缓存是冷的,这个时候做基准测试是不合适的,有的时候需要了解服务器重启后需要多长时间才能到达正常的性能容量

  • 测试时间不能太短,测10s往往是没有意义的,要让他一直跑,直到观察者认为系统已经稳定,就比如说一开始他的I/O吞吐量会逐步上升,要一直跑到他的吞吐量稳定在一个小的区间,不会有很大的起伏波动为止

  • 一定要检查错误,基准测试完成后一定要查看一下错误日志

  • 如果测试的过程会修改数据或schema,那么每次测试前用快照还原这些数据

0x01 单组件测试工具mysqlslap

mysqlslap是MySQL自带的基准测试工具,5.7版本官方手册

在此仅介绍几个常用的参数,其他的请自行查阅官方手册

# 操作设置
--concurrency=number 并发数量
--engine=name 需要测试的存储引擎
-i, --iterations=number 执行多少遍测试
--number-of-queries=number 总共要执行多少遍查询
--create=name 指定用来创建数据库的SQL文件或语句
--create-schema=name 指定用于测试的数据库名
--detach=number 指定number条语句后重新打开一个MySQL连接
--no-drop 测试完成后不删除自动生成的数据库
--only-print 不连接数据库仅输出测试生成的SQL等
--silent 沉默模式, 无任何输出
-q, --query=name 执行自己的SQL, 可以指定语句或SQL文件地址
# 数据库连接设置
-h, --host=name 主机名或IP地址
-P,--port=number MySQL连接端口
-u, --user=name MySQL连接用户名
-p, --password[=name] MySQL连接密码
# 自动生成选项设置
-a, --auto-generate-sql 指定使用自动生成查询所需的SQL语句
--auto-generate-sql-add-autoincrement
自动生成的数据库表中含有AUTO_INCREMENT字段
--auto-generate-sql-load-type=name
指定测试类型, 值有read只读, write只写
update仅更新, mixed混合操作
--auto-generate-sql-write-number=number
每个线程insert操作的数量
--commit=number 每指定number条语句做一次commit操作
-x, --number-char-cols=number 自动生成的数据库中有多少VARCHAR类型的字段
-y, --number-int-cols=number 自动生成的数据库中有多少INT类型的字段

TL;DR

开启20个并发线程,总共执行1000条查询,操作的数据库为world,执行的SQL语句写在/tmp/query.sql,操作完成后不删除数据库,语句如下:

mysqlslap --concurrency=20 --number-of-queries=1000 --create-schema=world --query=/tmp/query.sql --no-drop -uroot -p

0x02 收集MySQL状态及性能数据

我用bash写了一个脚本,然后将其发布到GitHub上了,代码传送门

如果你喜欢,或对你有帮助,不要忘记给我点个Star哦,谢谢,谢谢

这个脚本可以每隔5s收集一次数据,这个间隔可以通过变量INTERVAL来设置

本程序会自动在/home/下创建benchmarks文件夹以保存信息,可以通过变量STATUS_DIRECTORY来设置

使用Ctrl+C来退出脚本

收集的数据包括

  • Linux系统的load average, 用户数

  • MySQL数据库GLOBAL STATUS, INNODB STATUS以及FULL PROCESSLIST