第9章 测试实践
在第8章中介绍了测试所需要的理论知识,本章将为读者讲述实际的测试过程。
实际测试一般包括硬件测试、MySQL基准测试及应用服务压力测试,下面将分别讲述这三方面的内容。
此外,测试工具的选择也很重要,本章将为读者介绍两个常用的工具sysbench和mysqlslap。
9.1 硬件测试
9.1.1 概述
有时我们出于一些原因,需要进行硬件的测试。
比如,软件架构很复杂,难以模拟,这时我们可以大致测量一些硬件指标,建立比较基本的性能和容量模型。
比如,在升级硬件的时候,往往不会选择升级所有硬件,而是更着重于首先升级系统紧缺的资源,例如I/O,那么这时就需要专门针对不同的硬件配置,来测试I/O的提升效果。
再比如,硬件厂商往往夸大其词,这时我们就需要运用自己认为可靠的工具去实际验证,确认新的硬件在一些关键指标上是否有大幅度的提升。
现实中,硬件和数据库的测试工具并没有划分得很清晰,一些数据库测试工具,本身就可以对各种硬件资源进行压力测试。
比如sysbench,既可以测试数据库,又可以用来测试CPU、内存等硬件资源。
本书将主要关注Linux下的软硬件测试。
一些需要测量的硬件有:内存、CPU、磁盘、网卡、网络等。
网上也有很多优秀的开源测试工具,这里仅列出一些常用的测试工具。
内存测试的工具有sysbench、stream、RamSpeed、stress等。
CPU测试的工具有sysbench、cpuburn、stress等。
磁盘测试的工具有sysbench、iozone等。

9.1.2 CPU测试
sysbench命令通过进行素数运算来测试CPU的性能。
cpu-max-prime选项指定了最大的素数为20000,如下: sysbench --test=cpu --cpu-max-prime=20000 run
对于CPU的测试,我们要重点关注三个指标:上下文切换(context switch)、运行队列(run queue)和使用率 (utilization)。
(1)上下文切换
在操作系统中,若要将CPU切换到另一个进程,需要保存当前进程的状态并恢复另一个进程的状态:
即将当前运行任务转为就绪(或者挂起、删除)状态,让另一个被选定的就绪任务成为当前任务。
上下文切换包括保存当前任务的运行环境,恢复将要运行任务的运行环境等。
过多的上下文切换会给系统造成很大的开销。
(2)运行队列
当Linux内核要寻找一个新的进程在CPU上运行时,需要考虑处于可运行状态的进程,运行队列容纳了系统中所有可运行的进程。
理想情况下,调度器会让队列中的进程不断运行,如果CPU过载,就会出现调度器跟不上系统的情况,从而导致可运行的进程填满队列。
队列越大,程序执行的时间就越长。
“load”用于表示正在等待运行的队列长度,top命令可以让我们看到在一 分钟、5分钟和15分钟内CPU运行队列的大小。
这个值越大则表明系统负荷越大。
(3)使用率
CPU使用率可分为以下几个部分。
User Time:执行用户进程的时间占全部时间的百分比,通常是期望这个值越高越好。
SystemTime:CPU内核运行及中断的时间占全部时间的百分比,通常是希望这个值越低越好,系统CPU占用率过高时,通 常表明系统的某部分存在瓶颈。
Wait I/O:I/O等待的CPU时间占全部时间的百分比,如果I/O等待过高,那么说明系统中存在I/O瓶颈。
Idle:CPU处于Idle状态的时间占全部时间的百分比。
以下是一些很普遍的CPU性能要求,供大家参考。
对于CPU的每一个核来说运行队列不要超过3,例如,如果是双核CPU就不要超过6。
如果CPU正处于满负荷运行状态,那么使用率应该符合下列分布。 User Time:65%~70% SystemTime:30%~35% Idle:0%~5%
对于上下文切换,要结合CPU使用率来看,如果CPU使用率满足上述分布,那么大量的上下文切换也是可以接受的。
常用的监视上下文切换的工具有:vmstat、top、dstat和mpstat。
通过禁用或启用CPU核,可以进行不同CPU核数的性能和压力测试。
一般来说,对于数据库,比如MySQL,一条查询使用一颗CPU核,MySQL还不具备一个查询可以在多颗CPU核中并行运行的能力。
数据库操作中如果有大量的内存读,比如读取索引、读取InnoDB buffer里的数据,那么往往会表现为CPU瓶颈,内存复制也是如此。

9.1.3 内存测试
使用sysbench测试内存的命令如下。 sysbench --test=memory --memory-block-size=8K --memory-total-size=4G run
上述参数指定了本次测试的整个过程是在内存中传输4GB的数据量,每个块(block)的大小为8KB。

9.1.4 I/O测试
1.普通磁盘阵列测试
(1)使用hdparm
磁盘性能测试可采用hdparm命令,对于上线的服务器,为了简便,可用自带的命令hdparm初步判断磁盘的性能,确定工作是否正常。
如果要更可靠地验证磁盘、RAID性能,建议使用专门的测试工具,如iozone或sysbench。
hdparm的使用示例如下。
如下命令可查看某SATA硬盘的设置。
hdparm /dev/sda
/dev/sda:
IO_support = 0 (default 16-bit)
readonly = 0 (off)
readahead = 256 (on)
geometry = 60801/255/63, sectors = 976773168, start = 0
解释:
geometry=60801【柱面数】/255【磁头数】/63【扇区数】,sectors=976773168【总扇区数】,start=0【起始扇区数】。
如下命令可查看SSD的设置。
hdparm /dev/sdc
/dev/sdc:
IO_support = 0 (default 16-bit)
readonly = 0 (off)
readahead = 256 (on)
geometry = 36481/255/63, sectors = 586072368, start = 0
以下命令用于检测硬盘的读取速率(buffered disk reads),需要多运行几次,以便更准确。
hdparm -t /dev/sda
/dev/sda:
Timing buffered disk reads: 426 MB in 3.00 seconds = 141.82 MB/sec
以下命令用于检测硬盘快取时的读取速率(cached reads),需要多运行几次,以便更准确。
ahdparm -T /dev/sda
/dev/sda:
Timing cached reads: 22096 MB in 2.00 seconds = 11070.65 MB/sec
其中的参数解释如下:
-t:衡量顺序读取的能力,不经过操作系统缓存。
-T:衡量系统的吞吐性能,未访问底层的物理设备,直接从操作系统缓存里读取数据。
(2)使用dd
通过dd命令可实现用指定大小的块复制一个文件,并在复制的同时进行指定的转换。
dd命令的语法格式如下。 dd if=<source> of=<target> bs=<byte size> skip=<blocks> seek=<blocks> count=<blocks> conv=<conversion>
以下仅以RHEL 5.4的dd为例来说明参数,其他平台所支持的参数可能不一样。
if=<source>:指定源文件。默认为标准输入。
of=<target>:指定目标文件。默认为标准输出。
bs=<bytes>:同时设置读入/输出的块大小为bytes个字节。也可以指定其他单位,如KB、MB等。
skip=<blocks>:从输入文件的开头跳过blocks个块后再开始复制。
seek=<blocks>:从输出文件的开头跳过blocks个块后再开始复制。
count=<blocks>:仅复制blocks个块。
conv=<conversion>:用指定的参数转换文件,参数如下,可组合使用,中间用逗号分隔。
ascii:转换ebcdic为ascii。
ebcdic:转换ascii为ebcdic。
ibm:转换ascii为alternate ebcdic。
block:使每一行的长度都为cbs,不足部分用空格填充。
unblock:使每一行的长度都为cbs,不足部分用空格填充。
lcase:把大写字符转换为小写字符。
ucase:把小写字符转换为大写字符。
swab:交换输入的每对字节。
noerror:如果发生错误,程序也将继续运行。
notrunc:不截断输出文件。
sync:填充每个块到指定字节,不足部分用空(NUL)字符补齐。
dd命令还有一组参数oflag和iflag,用于控制源文件和目标文件的读写方式。
iflag=flag[,flag]...
oflag=flag[,flag]...
标志(flag)可选的值如下。
append:以追加(append)模式写数据。这个标志仅适用于输出(写文件)。如果和of=file联合使用,则需要同时设置conv=notrunc。
dsync:采用同步I/O读写数据,确保数据刷新到了磁盘。
sync:与上者类似,但同时也对元数据生效,在测试数据库机器的磁盘性能时,为了得到更真实的性能数据,建议使用 sync或dsync的方式读写数据。
direct:使用direct I/O操作数据,可以避免操作系统缓存的影响,即读或写文件时越过操作系统的读写缓存。
如果指定 oflag=direct,则写文件时会忽略缓存的影响;而如果指定iflag=direct,则读文件时会忽略缓存的影响。
fullblock:为输入积累完整块(仅针对iflag)。
nonblock:采用非阻塞I/O模式。
nofollow:不跟随链接文件,即忽略链接文件指向的文件。当从标准输入读取或写入到标准输出时,不要使用此选项。
noctty:不根据文件的指派控制终端。
例1: 测试磁盘写能力,利用操作系统写缓存。
dd if=/dev/zero of=blah.out bs=1M count=2000
或者: time (dd if=/dev/zero of=blah.out bs=1M count=2000 ; sync )
因为/dev/zero是一个伪设备,它只会产生空字符流,对它不会产生I/O,所以,I/O都集中在of文件中,of文件只用于写,所以这个命令相当于测试磁盘的写能力。
dd默认的方式不包括“同步(sync)”命令。
也就是说,dd命令完成之前并没有让系统真正把文件写到磁盘上。
脏数据可能还在操作系统的缓存里,并没有刷新到磁盘上。
如果内存比较大,我们往往可以看到磁盘的写能力很高。
如果以上命令在一块普通sata硬盘上执行,结果如下,可以看到,每秒写入828MB,显然磁盘的写能力远没有这么高。
dd if=/dev/zero of=blah.out bs=1M count=2000
2000+0 records in
2000+0 records out
2097152000 bytes (2.1 GB) copied, 2.53394 seconds, 828 MB/s
我们可以更改系统参数,把可用内存降低到很小,或者使用参数oflag=sync来确保已将数据刷新到了磁盘。
例2: 使用oflag=sync模式测试磁盘写能力,对于数据库机器,一般建议这样测试磁盘。
time dd if=/dev/zero of=blah.out oflag=sync bs=1M count=2000
对比例1的测试结果,本次测试的写入速度大大降低,这才是比较真实的写入速度。
time dd if=/dev/zero of=blah.out oflag=sync bs=1M count=2000
2000+0 records in
2000+0 records out
2097152000 bytes (2.1 GB) copied, 27.861 seconds, 75.3 MB/s
real 0m27.913s
user 0m0.003s
sys 0m3.204s
一般来说,随着bs值的增加,吞吐往往会更高。
例3: 测试磁盘读能力。
time dd if=/dev/sdb1 of=/dev/null bs=8k
因为/dev/sdb1是一个物理分区,对它的读取会产生I/O,而/dev/null是伪设备,相当于黑洞,of到该设备不会产生I/O,
所以, 这个命令的I/O只发生在/dev/sdb1上,也相当于测试磁盘的读能力。
例4: 测试同时读写的能力。
time dd if=/dev/sdb1 of=test1.dbf bs=8k
这个命令下,一个是物理分区,一个是实际的文件,对它们的读写都会产生I/O(对/dev/sdb1是读,对test1.dbf是写),
假设它们都在一个磁盘中,这个命令就相当于测试磁盘同时读写的能力。
可以另开一个会话(session),运行命令“kill -s USR1 pid”,用于显示dd进程的I/O统计,pid为正在执行dd命令的进程id,命令如下。
$ dd if=/dev/zero of=/dev/null & pid=$!
$ kill -USR1 $pid; sleep 1; kill $pid

2.SSD测试
对于SSD,也可以使用dd进行测试和验证,但对于数据库负载,为了更好地模拟负荷,建议使用更智能的工具进行测试, 如sysbench,sysbench的具体使用方法,请参考9.2.2节。
如下是一些SSD的测试建议:
测试要尽可能避免缓存(cache)的影响。
任何磁盘产品,在碰到I/O瓶颈的时候,都不可能会很快,这个时候才是真正的磁盘的性能。
如果我们在测试一些数据库产品的时候,发现有非常高的吞吐率,那么就要思考一下,是不是有什么其他因素影响了测试结果,
网上的很多测评就是陷入了这样一个误区,忽略了缓存的影响,这是一个致命的错误。
要注意碎片、空间占用的影响。
随着使用时间的增长,SSD可能有碎片,空间占用率也可能上升,这个时候性能就可能会下降。
一般来说,针对SSD的测试需要较长时间,因为可能有垃圾回收(Garbage Collection,GC)机制的影响,需要将其考虑进去,笔者个人偏向于每种测试模型的测试时长均大于1小时。
而且,你需要测试不同空间占用比下的性能,现实中,一般如果数据库占用了90%以上的磁盘空间,则必须要考虑扩容,
所以对于企业级应用,可以测试一下几乎写满(>90%)情况下的性能,此时的性能更有参考价值。
RAID卡、I/O控制器、缓存等因素也会影响到SSD的性能。需要留意这一点,高端RAID卡和中低端RAID卡的性能有很大的差别。
可测试单盘性能,如果需要做RAID,那么可测试一下RAID5和RAID1+0。
出于节省成本和空间的考虑,有些人使用RAID5。虽然理论上RAID5的性能会比较差,但RAID卡厂商一般专门提供了优化RAID5的技术,而且很多情况下I/O也不再成为瓶颈了。
如果单盘空间足够大且成本合适的话,那么不做RAID,直接测试单盘性能也是可以的。
不仅要测试吞吐率,还需要测试响应的稳定性,以反映真实的环境。
验证案例是否覆盖了所有的情况,而且要使用成熟稳定的工具来完成,以免破坏数据。
SSD对于温度会比较敏感,需要留意温度的影响。

9.1.5 网络测试
一般网卡出故障的可能性非常低,所以我们可以不用过多地进行验证测试,在系统部署后验证即可,
可用ethtool命令验证网卡,也可以使用专门的网络评测工具进行验证,
还可以通过网络传输文件来验证,比如,直接ftp一个大文件到ftp服务器以验证网络传输是否正常。

9.2 MySQL测试
9.2.1 概述
MySQL测试的范围很广,我们出于不同的目的进行测试,最常见的是性能基准测试。
有时还需要做一些其他测试,比如,验证MySQL的复制特性,在高并发的压力下,不断破坏从库(模拟宕机、磁盘空间满等情况),来查看复制能否顺利进行。
比如,通过测试宕机下的灾难恢复性,来衡量灾难恢复所需要的时间等。
本节将仅叙述MySQL的性能测试。
影响性能测试的因素较多,除了MySQL自身之外,文件系统、操作系统块大小、应用访问模式、RAID阵列条带大小等诸多因素都对性能有或多或少的影响。
本章不会对所有类型都进行测试,但会说明测试的方法及注意事项,读者可以按照自己拟定的方法和步骤测试验证不同软硬件设置下的MySQL性能。

9.2.2 常用测试工具的介绍和使用
MySQL的测试工具,推荐用sysbench。
虽然hammerora、super-mark、tpc-c等一些其他工具也很强大,
但sysbench的文件I/O测试与InnoDB的行为很相似,针对MySQL也有比较完善的测试模型,还可以方便地修改lua脚本,以实现更强大、更灵活的测试功能。
其实,设计sysbench的初衷就是为了衡量MySQL的性能,而很多其他工具,对于MySQL的支持往往只是一个选项,功能还不够强大,难以模拟真实的数据库负荷。
MySQL自带的mysqlslap也是一个不错的工具,它是从5.1.4版开始的一个MySQL官方提供的压力测试工具,可通过模拟多个并发客户端访问MySQL来执行压力测试。
这两个工具可以满足大部分情况下的性能测试和压力测试。
sysbench可以自定义lua脚本,开发人员可以编写适合自己业务逻辑的lua脚本。
当然也可以使用其他高级语言编写测试工具,这样会更灵活,更接近实际业务数据库操作。
1.sysbench的使用
目前sysbench主要支持MySQL、PostgreSQL、Oracle这3种数据库。
它主要包括以下几种方式的测试。
Fileio:文件I/O测试。
Cpu:CPU性能测试。
Memory:内存性能测试。
Threads:线程性能测试。
Mutex:Mutex性能测试。
Oltp:OLTP测试,MySQL一般会选择此种测试类型。
(1)安装
首先,从https://github.com/akopytov/sysbench下载源码包,单击Download Zip。然后,按照如下步骤进行安装。
unzip sysbench-0.5.zip
cd sysbench-0.5
./autogen.sh
./configure --with-mysql-includes=/usr/local/mysql/include --with-mysql-libs=/usr/local/mysql/lib
make
make install
用如上的参数进行编译的话,需要确保你的MySQL lib目录下有对应的库文件,如果没有,则可以下载devel或share包来进行安装。
也可以下载MySQL的二进制安装包解压到/usr/local/mysql下。
(2)开始测试
在sysbench--test=memory命令后添加help可以查看帮助。
sysbench --test=memory help
一些参数解析如下:
--percentile 95%:响应时间,也就是删除5%的响应时间最长的请求,然后从剩余的请求中选取最大的响应时间值。
--max-time:运行时间限制,单位是秒。
--num-threads:线程数。
--max-requests:查询数限制。
下面来举例说明。
1)CPU性能测试。
sysbench --test=cpu --cpu-max-prime=20000 run
CPU测试主要是进行素数的运算,在上面的例子中,指定了最大的素数为20000,也可以根据机器CPU的性能来适当调整数值。
如下命令,执行20s就输出了,而不会等待命令执行完。
sysbench --test=cpu --cpu-max-prime=20000 run --max-time=20
2)线程测试。
sysbench --test=threads --num-threads=64 --thread-yields=1000 --thread-locks=8 run
3)磁盘I/O性能测试。
sysbench --test=fileio --num-threads=16 --file-total-size=12G --file-test-mode=rndrw prepare
sysbench --test=fileio --num-threads=16 --file-total-size=12G --file-test-mode=rndrw run
sysbench --test=fileio --num-threads=16 --file-total-size=12G --file-test-mode=rndrw cleanup
上述代码分为3个步骤,第一条命令初始化文件,第二条命令执行测试,第三条命令清理文件。
--num-threads参数指定了最大创建16个线程,
--file-total-size参数指定创建文档的总大小为12GB,
--file-test-mode指定文档的读写模式为随机读写。
磁盘I/O性能测试是进行数据库基准测试时要着重加以研究的。
我们需要衡量各种因素,比如操作类型、读写的频率、I/O大小、是随机读写还是顺序读写、写的类型是异步还是同步、并发线程情况、操作系统缓存状态及文件系统有哪些调优等因素。
文件测试类型(file-test-mode)有如下几种。
seqwr:顺序写。
seqrewr:顺序重写(rewrite)。
seqrd:顺序读。
rndrd:随机读。
rndwr:随机写。
rndrw:随机读写。
4)内存测试。
sysbench --test=memory --memory-block-size=8K --memory-total-size=4G run
上述参数指定了本次测试的整个过程是在内存中传输4GB的数据量,每个块(block)的大小为8KB。
5)OLTP测试。
在测试之前请预先创建数据库,并给予测试用户足够的权限。
mysql > create database sbtest;
mysql > grant all privileges on sbtest.* to test@’ localhost’ identified by ‘ test’ ;
如下例子演示了多线程如何测试MySQL。
首先初始化数据。
sysbench --test=./sysbench/tests/db/oltp.lua --mysql-table-engine=innodb --oltp-tables-count=256 --oltp-table-size=1000000 --mysql-user=test --mysql- password=test --mysql-socket=/tmp/mysql.sock prepare
上述参数指定了本次测试的表存储引擎类型为InnoDB,指定了表的最大记录数为1000000,初始化生成256个表。
测试OLTP时,可以自己先创建数据库sbtest,或者自己用参数--mysql-db来指定其他数据库。
然后进行实际测试,测试模型是OLTP,并发8个线程,执行1个小时,如下:
sysbench --test=./sysbench/tests/db/oltp.lua --oltp-tables-count=256 --oltp-table-size=1000000 --mysql-user=test --mysql-password=test --mysql- socket=/tmp/mysql.sock --max-time=3600 --max-requests=0 --num-threads=8 --report-interval=10 run
其中,--report-interval=10表示每10s就输出一次数据,输出格式类似如下。
[ 10s] threads: 2, tps: 290.39, reads: 4065.82, writes: 1161.58, response time: 8.65ms (95%), errors: 0.00, reconnects: 0.00
[ 20s] threads: 2, tps: 270.90, reads: 3795.10, writes: 1083.80, response time: 10.14ms (95%), errors: 0.00, reconnects: 0.00
[ 30s] threads: 2, tps: 277.40, reads: 3883.50, writes: 1109.40, response time: 9.82ms (95%), errors: 0.00, reconnects: 0.00
[ 40s] threads: 2, tps: 273.50, reads: 3828.09, writes: 1094.00, response time: 9.93ms (95%), errors: 0.00, reconnects: 0.00
测试完成后,清理数据。
sysbench --test=./sysbench/tests/db/oltp.lua --oltp-tables-count=256 --oltp-table-size=1000000 --mysql-user=test --mysql-password=test --mysql- socket=/tmp/mysql.sock cleanup

2.mysqlslap的使用
mysqlslap是MySQL 5.1.4及以后版本自带的一个用于实现负载性能测试和压力测试的工具。
它可以模拟多个客户端对数据库进行施压,并生成报告以衡量数据库的一些指标。
其工作原理可分为如下三个步骤。
1)首先生成测试数据,即创建表,导入数据。这个步骤将使用单个客户端连接执行。
2)然后运行性能测试,可以使用单线程或多线程。
3)最后清理测试数据。这个步骤将使用单个客户端连接执行。
一些使用示例如下所示。参数的具体说明请参考官方文档。
1)分别并发10个线程或100个线程进行混合测试。
mysqlslap --uroot --engine=innodb --auto-generate-sql
\--auto-generate-sql-unique-query-number=100 --auto-generate-sql-unique-write-number=100 --auto-generate- sql-write-number=1000
\--create-schema=test --auto-generate-sql-load-type=mixed --concurrency=10,100 --number-of-queries=1000 --iterations=1
\--number-char- cols=1 --number-int-cols=8 --auto-generate-sql-secondary-indexes=1 --debug-info --verbose
以上命令的大致步骤是,首先生成1000条数据(--number-of-queries=1000),
然后进行混合测试(--auto-generate-sql-load-type=mixed,SELECT操作和INSERT操作大致各占一半),此时数据会不断增长。
然后,先使用10个并发线程进行测试,再用100个并发线程进行测试(--concurrency=10,100),进行新的并发测试前会清理和初始化测试数据。
需要留意的是,自动生成的SELECT语句是全表扫描,语句如下。
SELECT intcol1,intcol2,intcol3,intcol4,intcol5,intcol6,intcol7,intcol8,charcol1 FROM t1
INSERT语句类似如下。
INSERT INTO t1 VALUES (uuid(),389111603,476395693,1231278962,1952007439,1880139043,1004384052,914532...
2)测试基于主键查找的性能。
time mysqlslap -uroot --engine=innodb --auto-generate-sql-load-type=key
\--auto-generate-sql --auto-generate-sql-write-number=100000 --auto-generate-sql-guid- primary
\--number-char-cols=10 --number-int-cols=10 --concurrency=10,100 --number-of-queries=5000
以上命令的大致步骤是,首先创建一个表,使用单线程初始化插入100000条记录,然后并发10条线程执行基于主键的查询。
接着删除库表,再初始化插入 100000条记录,然后并发100条线程执行基于主键的查询。
基于主键的查询可能被缓存,所以有必要生成不同的SELECT语句。
3)生成一张两千万条记录的表,进行混合型负荷测试(SELECT+INSERT),语句如下。
mysqlslap -uroot -p --engine=innodb --auto-generate-sql --auto-generate-sql-write-number=20000000
--auto-generate-sql-add-autoincrement --auto-generate-sql- secondary-indexes=2 --concurrency=50 --number-of-queries=1000000 --number-char-cols=3 --number-int-cols=2 --debug-info
以上命令的大致步骤是,初始化记录时使用自增主键(--auto-generate-sql-add-autoincrement)并发50个线程进行查询,一共执行100万个查询,
也就是说平均每个线程大概执行2万个查询,如果有自增ID,那么SELECT语句是基于自增ID的,这样更能反映生产环境实际情况。
对于如上的命令,一台普通的数据库服务器(SAS硬盘三块:SAS 15K 300G*3,做成了RAID5),
初始化过程中大概会插入2000万条记录,到达1500万条记录的时候,INSERT速率大概可以达到6000~7000条记录每秒。
iostat命令显示每秒写入20MB~30MB的数据。
插入2000万条记录,初始化完成后,开始并发50条线程进行混合测试,同时有INSERT和SELECT操作。
大概每秒执行INSERT操作600次,SELECT操作500次。

9.2.3 MySQL基准测试模型
1.指引
基准测试的目的之一是在平时做好数据准备,记录标准的数据库软硬件配置下的性能数据,以便在未来更改数据库配置或调整升级数据库主机时有一个参照。
很多人在新机器上线后不想做基准测试,认为基准测试繁琐、耗时,而且难以度量。
如果不是标准化的采购、安装、部署,就很难说主机配置得完全正确,所以,如果有一个工具可以预先针对CPU、磁盘、网络、数据库进行压力测试,验证硬件是否已经正确配置,就省事多了。
我们还可以把基准测试的历史数据记录下来,在以后进行选购或升级软硬件的时候,再重新进行基准测试,从而验证软硬件的升级效果。
基准测试的一个重要环节就是基准测试模型,我们需要一个相对简单、高效,实现起来成本比较低的模型。
想用测试模型来真实反映现实的生产环境是很难做到的,
但是我们可以按照数据库的理论和实践,设置一些比较能够反映生产负荷的输入条件,评估测试的输出指标,从而建立可以用来衡量数据库架构、软硬件配置的基准模型。
以下将介绍下具体的思路和设计方法。
当我们选择硬件的时候,需要考虑到各项成本,对于项目风险、开发成本和维护成本比较难以衡量,而计算机性能相对来说是更好地限定和比较的,所以可以考虑建立一个MySQL的基准测试模型。
性能测试很难模拟真实环境的负荷,一般使用比较简单的模型,因为真实环境下的负荷具有不确定、变化较大、复杂且难以理解等特点,故而难以得出结论,不容易对比。
单个产品的基准测试,主要用于对比版本和衡量软硬件调整的效果,对于整个应用系统的测试没有太大的参考意义,应用系统自己的基准测试模型会比单个 MySQL测试模型更全面更准确。
明确目标后,再进行基准测试,才能更好地选择工具和测试方法。
如果是SSD,建议文件的最大空间不超过磁盘空间的85%,以避免SSD空间占比可能带来的性能下降。
除了关注吞吐率(TPS),还需要关注响应时间(responsetime)。
需要留意并发性(concurrency),如MySQL Server同时运行的线程数(threads_running)和伸缩性(scalability)等。
假如我们增加了一倍线程,那么好的伸缩性就意味着系统的吞吐也可以线性地增加一倍,或者说当我们增加了一倍的硬件资源,那么系统的吞吐也可以翻倍。
基准测试需运行足够长的时间。关于数据预热所需要的时间,一般查看吞吐量的曲线图就可以大致判断出来,很多情况下,可能需要运行半个小时以上才会稳定下来。
尽量确保测试结果在同样的配置下可以重现。
衡量MySQL性能需要考虑诸多因素,包括但不限于以下这些因素。
硬件:CPU速度、CPU架构、CPU个数、CPU核数、总线速度、内存访问速度、设备I/O性能、RAID卡、磁盘条带、块大小、网络设备。
操作系统:原生API性能、线程、锁、内存、I/O调度算法。
客户端连接次数。
数据库服务器处理任务的线程个数。
数据库设计。
数据量。
应用类型。
数据访问模式:一般来说我们的应用热点数据较小,读远大于写。如果你的应用热点数据比较大,访问各种数据比较分散,分布比较均匀,那么这种测试更考验了数据库的原始性能。
数据库版本:社区版、企业版还是第三方分支版本。
引擎。
数据库配置。比如NUMA策略、页块大小(pagesize)、是独立表空间还是共享表空间、顺序访问和随机访问文件的分布、InnoDB buffer pool的大小及其他一 些影响重大的参数。
重要的参数修改,每次尽可能少更改点参数,一次更改太多的参数不容易判断问题的所在。
2.模型简介
以下是一个测试MySQL数据库的简单模型。
## MySQL基准测试模型介绍。
## 组合以下不同条件 :测试类型、线程数、表个数、表记录数 (大小 ) 进行测试
## ================开始测试 =======================
## 测试逻辑 :对每种测试类型、对各种并发线程数、对指定的表个数、对不同表大小
## prepare
## sysbench 测试 ,默认测试 1200s
## cleanup
## ====================结束测试 ========================
测试类型一般选择oltp。对于单个表,将按顺序执行如下操作。
1)几个基于主键的查询。
2)主键范围查找。
3)主键范围查找+聚合函数。
4)主键范围查找+文件排序。
5)主键范围查找+临时表+文件排序。
6)更新操作(基于主键查询)。
7)删除操作(基于主键查询)。
8)插入操作。
9)提交。
由于sysbench官方版本的oltp模型里没有复杂的查询连接(JOIN)操作,但两三个表的连接又是比较普遍的,因此可以考虑更改下官方的oltp测试模型,以反映真 实的生产负荷。
3.测试脚本示例
如下是一个按照上面的模型编写出的脚本示例,这里将叙述它所实现的功能,以及分析几个测试脚本的输出结果。
测试脚本的代码请到www.db1110.com上下载。
测试脚本可实现如下功能:
1)收集操作系统、硬件信息和MySQL脚本信息。
如下是收集到的一些信息。
=========================Host==============================
Hostname | xxxx
Release | Red Hat Enterprise Linux Server release 5.4 (Tikanga)
Processors | physical = 2, cores = 8, virtual = 16, hyperthreading = yes
Models | 16xIntel(R) Xeon(R) CPU E5520 @ 2.27GHz
Total | 15.6G
# RAID Controller ############################################
Controller | No RAID controller detected
# Disk Schedulers And Queue Size #############################
sda | [cfq] 128
sdb | [cfq] 128
============================MySQL===========================
# Report On Port 3306 ########################################
User | root@localhost
Hostname | xxxxxx
Version | 5.1.58-log MySQL Community Server (GPL)
Built On | unknown-linux-gnu x86_64
Databases | 5
# Table cache ################################################
Size | 4096
# InnoDB #####################################################
Version | default
Buffer Pool Size | 2.5G
File Per Table | OFF
Page Size | 16k
Log File Size | 2 * 360.0M = 720.0M
Flush Log At Commit | 2
Thread Concurrency | 16
Txn Isolation Level | READ-COMMITTED
sync_binlog | 20
innodb_max_dirty_pages_pct = 50
2)可以在脚本中调整MySQL的参数设置。
3)收集操作系统的性能信息,在测试期间的内存、CPU、磁盘等各种信息都应该收集起来,不管目前有没有用,收集尽可能多的信息会方便自己以后的分析。
4)能使用sysbench的oltp模型进行测试,并且能够生成图形。
以下是性能测试脚本的运行结果,我们将对这些测试完成后生成的图形做一些分析。
例1: 图9-1是不同线程(thread)数量的时候,事务吞吐率(tps)的变化图,数据小于InnoDB缓冲池(0.7382*buffer)。
图9-1中所示的MySQL实例有256张表,每张表有3万记录,数据小于InnoDB缓冲区。
随着线程数量的增加,事务吞吐率会缓慢下降,这说明等待的时间在增长, 从而导致事务吞吐率下降。
对于线程数量不断增加的数据库测试,性能吞吐率曲线最开始往往是线性增长的,但终将到达一个拐点,可能到达拐点的时候,是因为某项资源出现了瓶颈, 对资源的竞争将开始影响到性能。
比如图9-1的压力测试中,随着线程数量的增加,吞吐率也在增加,但如果存在过多的线程,由于CPU资源不够,将导致频繁的上下文切换,从而导致延时增加。
如果是CPU资源的瓶颈,那么性能下降得不是很快;但如果是内存瓶颈,则属于另外一种情况,性能可能会急剧变差;磁盘I/O瓶颈同样可能导致急剧的性能变差,请参见下面例2的图。
例2: 图9-2和图9-1类似,描述了不同线程数下事务吞吐率的变化,不过数据占用空间远远大于InnoDB缓冲池(4.8573*buffer)。可以看到事务吞吐率已经大大下降了。
例3: 图9-3描述了不同数据量下,事务吞吐率的变化。在8个线程、256张表的情况下,随着数据量的增长,事务吞吐率逐渐下降。
随着数据的不断增长,3万记录也逐渐增长到5万、10万、20、30万,事务吞吐率则从1500tps下降到了400tps。
由此可以证明,热点数据能否缓存在内存中,对事务的吞吐率影响是很大的。
例4: 图9-4由多个子图构成,分别展示了事务吞吐、读、写、响应时间随时间变化的曲线。数据库实例的数据空间占用小于InnoDB缓冲(0.7382*buffer)。
对于我们来说,更有效的数据是性能趋于稳定后的数据,大家做测试的时候,一定要确认自己是否已经运行了足够长时间,可以简单地通过查看事务吞吐率是否趋于稳定来衡量时间的长短。
例5: 图9-5说明了磁盘的I/O瓶颈可能会导致性能急剧变差。
可以看到在数据远大于InnoDB缓冲(4.8573*buffer)的时候,事务吞吐率下降了很多。
且随着时间的增长,有时会突然出现性能急剧下降的情况(见图9-5中长 的“毛刺”)。
这主要是MySQL刷新数据的机制不够完善所导致的。
高并发读写的数据库负载很可能会出现此种情况,且MySQL 5.1很难避免出现这种情况。
必须承认 这是一个固有的缺陷,需要想办法尽可能地避免。
MySQL 5.6对此有一定的改善。

4.基准测试的不足及注意事项
不容易测试系统的其他特性:稳定性、安全性、扩展性、可用性、灾难恢复性等。
没有计算成本(磁盘、内存等),但即使计算了成本,也可能会被厂商欺骗,厂商会使用最优的配置,以尽可能低的成本支撑更大的吞吐。
没有衡量能源消耗。
没有考虑到复杂的网络环境。
用户考虑的是这个产品能够满足何种服务品质协议(service-levelagreement),比如说99.99%的时间是可用的,而基准测试更多考虑的是能够达到的平均分数。
一般测试报告列出的可能是80%~90%的系统资源使用率下的数据,没有考虑系统资源严重瓶颈,而现实中往往并非如此,
在系统资源出现严重瓶颈时,性能、安全性、稳定性可能急剧下降。
一般来说,新版本的功能更强,优化器更复杂,更擅长处理复杂的查询。在高并发和大数据集下会更具备优势。
可能很难模拟复杂的实际访问。
针对不同问题设计负荷,比如,是模拟计算密集型(CPU-bound)的负荷还是I/O密集型(I/O-bound)的负荷呢?
向上扩展(scale-up)可以提高吞吐,但存在一个最佳配置,超过这个配置后,吞吐不会再有明显增加。
即使增加了CPU、增大了内存,增加了更快速的硬盘,往往还是得不到更高的性能,瓶颈点在于数据库自身的等待而不是硬件,
MySQL数据库难以充分利用硬件资源,所以生产环境更倾向于多实例部署,倾向于不那么强劲的服务器主机。
研发、测试人员往往不擅长做数据库的压力测试,可能存在的问题包括不熟悉硬件,
测试时MySQL Server的参数并没有经过优化,没有使用足够多的真实可靠的数据,没有运行足够长时间的测试计划以预热数据等。
如果可能,应该提供生产环境的真实数据供研发、测试人员测试,可以考虑的一个方法是重放日志来模拟负载,但必须也要模拟多线程并发的场景。
生产环境数据库和Web服务器一般分布在不同的主机上,严格来说,测试工具部署在非数据库机器上更好,但是,测试工具、客户端和数据库在同一台机器上 做测试一般也是可行的。

9.3 应用数据库性能测试
数据库的性能测试可能是由其他团队来实施,而不是由DBA来完成的,由于关注的重心不一样,使用的工具不一样,可能分析的结果也会有偏差,
但只要我们了解了测试的本质,熟悉了数据库,就可以阅读其他部门和团队所做的数据库测试报告,从而得出自己认可的结果。
一般研发、测试人员可能会专门使用一些Web测试工具,如Apache自带的工具ab,包括http_load、Webbench、Siege、JMeter等。
这些工具会调用一些Web页面程 序,对数据库间接进行施压,如果选择的测试模型和步骤合适,往往比直接使用工具对数据库进行压力测试更合理。
应用负载往往是复杂的,很难去模拟,即使是企业级的测试工具也难以重现生产环境,
有兴趣的读者可以去试用一下tcpcopy这个工具,它可以把生产环境的流量复制到测试环境中,对于测试数据库的性能很有帮助,尤其是测试只读的MySQL从库。

小结:
本章介绍了MySQL的一个基准测试模型,比较简单,大家可以在上面加入自己的想法。
随着读者MySQL水平的提高,会逐渐意识到性能测试的重要性。
如果需要提高自身的软硬件架构功力,就一定要多做性能测试,通过性能测试,可以对数据架构有更深刻的理解。
由于数据库性能测试很耗时、繁琐,因此尽可能使用自动化测试。
数据库性能测试的方法和结果也应该分享给其他团队,让他们把握当下软硬件的能力和极限,减少沟通成本,避免错误决策。

 

posted on 2019-12-13 16:55  Brad Miller  阅读(286)  评论(0编辑  收藏  举报