特别关注下mysqlslap的假并发现象,因为压力上不去浪费了一些时间。
主要就是–concurrency 参数所指的并发是线程并发,在开头再??乱槐椤?/p>
1.mysql性能测试工具
mysql的性能测试工具常用的有四种:The MySQL Benchmark Suite、MySQL super-smack、MyBench和自带的Mysqlslap. 除了第一个为MySQL性能测试工具,其他三个都为压力测试工具
(1)The MySQL Benchmark Suite : 基于Perl语言和其中的两个模块:DBI和Benchmark,因为不支持多CPU而且不是压力工具,抛弃该软件
(2)MySQL super-smack 业界口碑较好,安装出了压缩包编译安装外,还需要yacc以及lex支持,安装报错找不到 lex和yacc啊啥的。 linux下使用flex及bison代替,可以用apt-get install bison flex 或类似的命令安装,问题是还要找能连接外网的机器,能连接外网的机器不一定支持压力,放弃该软件
(3)MyBench
也是个perl模块,虽然安装方便,但是测试数据的使用比较麻烦,drop it.
(4) Mysqlslap
最终选用了mysqlslap。 安装成本小:原因是mysql5.1.4 版本自带,安装mysql后就可以使用,不需要额外的配置。 场景部署多:带有多个参数选项,测试数据的使用也方便。通过模拟多个并发客户端并发访问MySQL来执行压力测试, 结果分析全:同时提供了较详细的SQL执行数据性能报告,并且能很好的对比多个存储引擎(MyISAM,InnoDB等)在相同环境下的相同并发压力下的性能差别。
mysqlslap的官方网站: http://dev.mysql.com/doc/refman/5.6/en/mysqlslap.html
2.mysqlslap的安装
1) 查看当前的mysql版本
./bin/mysql -V
2)如果版本低于5.1.4 请安装mysql高版本
安装过程中首先要关闭现在的客户端。用netstat -pan|grep mysql查看当前mysql 的端口是否在运行。 为避免端口冲突,安装前最好将mysql的进程关闭
3.mysqlslap的使用
(1) 使用步骤
看下官网对于使用步骤的解释:
Create schema, table, and optionally any stored programs or data to use for the test. This stage uses a single client connection.
Run the load test. This stage can use many client connections.
Clean up (disconnect, drop table if specified). This stage uses a single client connection
mysqlslap的使用步骤主要是: 1. 创建schema、准备测试语句 (在MySQL中,schema就是database); 2. 运行负载测试,可以使用多个并发客户端连接;(编写脚本,可用多进程并发) 3. 测试环境清理 ,关闭进程,清理数据等。
(2) 参数说明
以下是mysqlslap的参数含义
view plaincopy to clipboardprint?
./bin/mysqlslap --help 可以看到帮助的参数解释,下面介绍一些比较常用的
--no-defaults 表示不使用默认参数文件中的设置.
--debug-info, -T 打印内存和CPU的信息;
--auto-generate-sql, <span style="color: purple;">-a</span> 自动生成测试表和数据;
--auto-generate-sql-load-type=type 测试语句的类型. 取值包括read, key, write, update和mixed(默认);
--number-char-cols=N, <span style="color: purple;">-x</span>
N 自动生成的测试表中包含多少个字符类型的列, 默认1;
--number-int-cols=N, -y N 自动生成的测试表中包含多少个数字类型的列, 默认1;
--number-of-queries=N 总的测试查询次数(并发客户端数×每客户端查询次数);
--query=name,<span style="color: purple;">-q</span> 使用自定义脚本执行测试, 例如可以调用自定义的一个存储过程或者sql语句来执行测试.
--create-schema 测试的schema, MySQL 中schema也就是database;
--commint=N 多少条DML后提交一次;
--compress, -C 如果服务器和客户端支持都压缩, 则压缩信息传递;
--concurrency=N, <span style="color: purple;">-c</span> N 并发量, 也就是模拟多少个客户端同时执行select. 可指定多个值, 以逗号或者--delimiter参数指定的值做为分隔符;
--engine=engine_name, <span style="color: purple;">-e</span> engine_name 创建测试表所使用的存储引擎, 可指定多个;
--iterations=N, <span style="color: purple;">-i</span> N 测试执行的迭代次数;
--detach=N 执行N条语句后断开重连;
--only-print 只打印测试语句而不实际执行;
连接参数:
<span style="color: purple;">-u</span>, --user=name User <span style="color: #a52a2a;">for</span> login <span style="color: #a52a2a;">if</span> not current user.
<span style="color: purple;">-p</span>, --password=name Password to use when connecting to server. 如果密码没有给,会通过tty终端请求。
<span style="color: purple;">-h</span>, --host=name Connect to host.
-P, --port= Port number to use <span style="color: #a52a2a;">for</span> connection.
需要说明的是 –concurrency 在实际测试调参中发现,该参数是个假并发参数。 并非是多个客户端并发,而是一个客户端进程中指定使用多个线程的参数。 增加该值并不能使压力上去,也许你发现了。增加线程数,有时候反而会使落到每一台mysql机器上的压力降下来。
实际测试中如果要压力上去,需要启动多个mysqlslap进程进行测试。
(3) 实例说明
在测试前用脚本批量处理了数据,生成不带cache的select语句
view plaincopy to clipboardprint?
<pre>select SQL_NO_CACHE url_key, value from structqa where url_key <span style="color: #a52a2a;">in</span> (5793176823383938934,1549359593866465909,46398175249572291 39,6858231871203830826);</pre>
<pre>DB加缓存和不加缓存的压力大概是1.5倍。</pre>
<pre>通过监控图可以看出单进程+cache 和 单进程不加cache的对比:图中是四台机器的曲线拟合,考虑到dbproxy分片的压力不均</pre>
<pre><a href="/wp-content/uploads/2013/06/mysql+cache.png"></a>
<pre><a href="/wp-content/uploads/2013/06/mysql+cache.png"><img title="mysql+cache" src="/wp-content/uploads/2013/06/mysql+cache-300x160.png" alt="" width="300" height="160"></a> <a style="font-size: 1.5em;" href="/wp-content/uploads/2013/06/mysql-cache1.png"><img title="mysql-cache" src="/wp-content/uploads/2013/06/mysql-cache1-300x141.png" alt="" width="300" height="141"></a></pre>
</pre>
使用时候启动单个进程的语句如下:
<pre>/home/iknow/local/mysql/bin/mysqlslap -u***** -p***** -h***.***.***.*** -P3300 --create-schema=**** --query=select_ddbs.sql --concurrency=2 --number-of-queries=6000000 --iterations=2000 --debug-info --engine=innodb ;</pre>
参数说明: –create-schema 指明要测试的数据库,否则会报错 ./bin/mysqlslap: Error when connecting to server: 1045 Auth failed, check your username, password or db
–query 生成的sql语句的文件 –concurrency 使用两个线程处理 –number-of-queries 本次压力使用的query数目 –iterations 循环2000次,因为要压力一段时间,可以手动kill掉 –engine 是innodb的数据库引擎 –debug-info 会在执行结束的时候打印一些cpu和内存信息。
4. 性能测试
(1)性能因素
1,测试环境
如果是线下机器要尽可能与线上一致
2,测试数据
mysql分为带缓存和不带缓存的查询两种,线下构造不带缓存的请求
3,压力请求
(2)性能指标
【数据库性能指标】
QPS(TPS):每秒钟request/事务 数量
并发数: 系统同时处理的request/事务数
响应时间: 一般取平均响应时间
【系统性能指标】
CPU:
idle: 显示了cpu处在空闲状态的时间百分比
wa值:wa 列显示了IO等待所占用的CPU时间的百分比。
这里wa的参考值为30%,如果wa超过30%,说明IO等待严重,这可能是磁盘大量随机访问造成的,也可能磁盘或者磁盘访问控制器的带宽瓶颈造成的
内存: mysql不用过多关注内存,主要是读写操作,涉及到C模块的要特别关注下,内存泄露最直观的的表现就是内存随时间上涨。
IO: 关注下面的指标即可: 上面说的CPu的wa值,以及
util%: 一秒中有百分之多少的时间用于 I/O 操作,或者说一秒中有多少时间 I/O 队列是非空的.即 delta(use)/s/1000 (因为use的单位为毫秒)如果 %util 接近 100%,说明产生的I/O请求太多,I/O系统已经满负荷,该磁盘可能存在瓶颈.
(3) 性能场景
综合性能因素和性能的衡量指标,以及当前的测试目的,构造测试场景 需要模拟线上的场景,本次构造的场景
1,模拟线上小流量
-----简单压一下,看当前的系统性能指标,如果小流量都不行,说明系统存在严重的性能瓶颈。
2,模拟线上全流量
--- 全流量的模拟要细致,该测试出了测试全流量之外,还可以压力一晚上,查看系统稳定性,补充稳定性测试。
3, 模拟线上双倍流量
--考虑到峰值是普通流量的两倍,以及线上切机房的时候的流量
4,极限压力测试
需要特别跟DBA和RD确认当前机器没有对外提供服务,仅供测试使用,而且极限压力时间不宜过长,一般1~2小时左右
下面来看mysqlslap对MySQL进行压力测试
我解释一下一些常用的选项。
这里要注意的几个选项:
--concurrency代表并发数量,多个可以用逗号隔开,当然你也可以用自己的分隔符隔开,这个时候要用到--delimiter开关。
--engines代表要测试的引擎,可以有多个,用分隔符隔开。
--iterations代表要运行这些测试多少次。
--auto-generate-sql 代表用系统自己生成的SQL脚本来测试。
--auto-generate-sql-load-type 代表要测试的是读还是写还是两者混合的(read,write,update,mixed)
--number-of-queries 代表总共要运行多少次查询。每个客户运行的查询数量可以用查询总数/并发数来计算。比如倒数第二个结果2=200/100。
--debug-info 代表要额外输出CPU以及内存的相关信息。
--number-int-cols 代表示例表中的INTEGER类型的属性有几个。
--number-char-cols 意思同上。
--create-schema 代表自己定义的模式(在MySQL中也就是库)。
--query 代表自己的SQL脚本。
--only-print 如果只想打印看看SQL语句是什么,可以用这个选项。
现在来看一些我测试的例子。
1、用自带的SQL脚本来测试。
MySQL版本为5.1.23
[root@localhost ~]# mysqlslap --defaults-file=/usr/local/mysql-maria/my.cnf --concurrency=50,100,200 --iterations=1 --number-int-cols=4 --number-char-cols=35 --auto-generate-sql --auto-generate-sql-add-autoincrement --auto-generate-sql-load-type=mixed --engine=myisam,innodb --number-of-queries=200 --debug-info -uroot -p1 -S/tmp/mysql_3310.sock
Benchmark
Running for engine myisam
Average number of seconds to run all queries: 0.063 seconds
Minimum number of seconds to run all queries: 0.063 seconds
Maximum number of seconds to run all queries: 0.063 seconds
Number of clients running queries: 50
Average number of queries per client: 4
Benchmark
Running for engine myisam
Average number of seconds to run all queries: 0.070 seconds
Minimum number of seconds to run all queries: 0.070 seconds
Maximum number of seconds to run all queries: 0.070 seconds
Number of clients running queries: 100
Average number of queries per client: 2
Benchmark
Running for engine myisam
Average number of seconds to run all queries: 0.092 seconds
Minimum number of seconds to run all queries: 0.092 seconds
Maximum number of seconds to run all queries: 0.092 seconds
Number of clients running queries: 200
Average number of queries per client: 1
Benchmark
Running for engine innodb
Average number of seconds to run all queries: 0.115 seconds
Minimum number of seconds to run all queries: 0.115 seconds
Maximum number of seconds to run all queries: 0.115 seconds
Number of clients running queries: 50
Average number of queries per client: 4
Benchmark
Running for engine innodb
Average number of seconds to run all queries: 0.134 seconds
Minimum number of seconds to run all queries: 0.134 seconds
Maximum number of seconds to run all queries: 0.134 seconds
Number of clients running queries: 100
Average number of queries per client: 2
Benchmark
Running for engine innodb
Average number of seconds to run all queries: 0.192 seconds
Minimum number of seconds to run all queries: 0.192 seconds
Maximum number of seconds to run all queries: 0.192 seconds
Number of clients running queries: 200
Average number of queries per client: 1
User time 0.06, System time 0.15
Maximum resident set size 0, Integral resident set size 0
Non-physical pagefaults 5803, Physical pagefaults 0, Swaps 0
Blocks in 0 out 0, Messages in 0 out 0, Signals 0
Voluntary context switches 8173, Involuntary context switches 528
我来解释一下结果的含义。
拿每个引擎最后一个Benchmark示例。
对于INNODB引擎,200个客户端同时运行这些SQL语句平均要花0.192秒。相应的MYISAM为0.092秒。
2、用我们自己定义的SQL 脚本来测试。
这些数据在另外一个MySQL实例上。版本为5.0.45
先看一下这两个表的相关数据。
1)、总记录数。
mysql> select table_rows as rows from information_schema.tables where table_schema='t_girl' and table_name='article';
+--------+
| rows |
+--------+
| 296693 |
+--------+
1 row in set (0.01 sec)
mysql> select table_rows as rows from information_schema.tables where table_schema='t_girl' and table_name='category';
+------+
| rows |
+------+
| 113 |
+------+
1 row in set (0.00 sec)
2)、总列数。
mysql> select count(*) as column_total from information_schema.columns where table_schema = 't_girl' and table_name = 'article';
+--------------+
| column_total |
+--------------+
| 32 |
+--------------+
1 row in set (0.01 sec)
mysql> select count(*) as column_total from information_schema.columns where table_schema = 't_girl' and table_name = 'category';
+--------------+
| column_total |
+--------------+
| 9 |
+--------------+
1 row in set (0.01 sec)
3)、调用的存储过程
DELIMITER $$
DROP PROCEDURE IF EXISTS `t_girl`.`sp_get_article`$$
CREATE DEFINER=`root`@`%` PROCEDURE `sp_get_article`(IN f_category_id int,
IN f_page_size int, IN f_page_no int
)
BEGIN
set @stmt = 'select a.* from article as a inner join ';
set @stmt = concat(@stmt,'(select a.aid from article as a ');
if f_category_id != 0 then
set @stmt = concat(@stmt,' inner join (select cid from category where cid = ',f_category_id,' or parent_id = ',f_category_id,') as b on a.category_id = b.cid');
end if;
if f_page_size >0 && f_page_no > 0 then
set @stmt = concat(@stmt,' limit ',(f_page_no-1)*f_page_size,',',f_page_size);
end if;
set @stmt = concat(@stmt,') as b on (a.aid = b.aid)');
prepare s1 from @stmt;
execute s1;
deallocate prepare s1;
set @stmt = NULL;
END$$
DELIMITER ;
4)、我们用mysqlslap来测试
以下得这个例子代表用mysqlslap来测试并发数为25,50,100的调用存储过程,并且总共调用5000次。
[root@localhost ~]# mysqlslap --defaults-file=/usr/local/mysql-maria/my.cnf --concurrency=25,50,100 --iterations=1 --query='call t_girl.sp_get_article(2,10,1);' --number-of-queries=5000 --debug-info -uroot -p -S/tmp/mysql50.sock
Enter password:
Benchmark
Average number of seconds to run all queries: 3.507 seconds
Minimum number of seconds to run all queries: 3.507 seconds
Maximum number of seconds to run all queries: 3.507 seconds
Number of clients running queries: 25
Average number of queries per client: 200
平均每个并发运行200个查询用了3.507秒。
Benchmark
Average number of seconds to run all queries: 3.742 seconds
Minimum number of seconds to run all queries: 3.742 seconds
Maximum number of seconds to run all queries: 3.742 seconds
Number of clients running queries: 50
Average number of queries per client: 100
Benchmark
Average number of seconds to run all queries: 3.697 seconds
Minimum number of seconds to run all queries: 3.697 seconds
Maximum number of seconds to run all queries: 3.697 seconds
Number of clients running queries: 100
Average number of queries per client: 50
User time 0.87, System time 0.33
Maximum resident set size 0, Integral resident set size 0
Non-physical pagefaults 1877, Physical pagefaults 0, Swaps 0
Blocks in 0 out 0, Messages in 0 out 0, Signals 0
Voluntary context switches 27218, Involuntary context switches 3100
看一下SHOW PROCESSLIST 结果
mysql> show processlist;
+------+------+--------------------+--------------------+---------+-------+--------------------+------------------------------------------------------------------------------------------------------+
| Id | User | Host | db | Command | Time | State | Info |
+------+------+--------------------+--------------------+---------+-------+--------------------+------------------------------------------------------------------------------------------------------+
…………
| 3177 | root | % | t_girl | Query | 0 | NULL | select a.* from article as a inner join (select a.aid from article as a inner join (select cid from |
| 3178 | root | % | t_girl | Query | 0 | NULL | select a.* from article as a inner join (select a.aid from article as a inner join (select cid from |
| 3179 | root | % | t_girl | Query | 0 | NULL | select a.* from article as a inner join (select a.aid from article as a inner join (select cid from |
| 3181 | root | % | t_girl | Query | 0 | NULL | select a.* from article as a inner join (select a.aid from article as a inner join (select cid from |
| 3180 | root | % | t_girl | Query | 0 | NULL | select a.* from article as a inner join (select a.aid from article as a inner join (select cid from |
| 3182 | root | % | t_girl | Query | 0 | NULL | select a.* from article as a inner join (select a.aid from article as a inner join (select cid from |
| 3183 | root | % | t_girl | Query | 0 | NULL | select a.* from article as a inner join (select a.aid from article as a inner join (select cid from |
| 3187 | root | % | t_girl | Query | 0 | removing tmp table | select a.* from article as a inner join (select a.aid from article as a inner join (select cid from |
| 3186 | root | % | t_girl | Query | 0 | NULL | select a.* from article as a inner join (select a.aid from article as a inner join (select cid from |
| 3194 | root | % | t_girl | Query | 0 | NULL | select a.* from article as a inner join (select a.aid from article as a inner join (select cid from |
| 3203 | root | % | t_girl | Query | 0 | NULL | deallocate prepare s1 |
…………
| 3221 | root | % | t_girl | Query | 0 | NULL | select a.* from article as a inner join (select a.aid from article as a inner join (select cid from |
| 3222 | root | % | t_girl | Query | 0 | NULL | select a.* from article as a inner join (select a.aid from article as a inner join (select cid from |
| 3223 | root | % | t_girl | Query | 0 | NULL | select a.* from article as a inner join (select a.aid from article as a inner join (select cid from |
| 3224 | root | % | t_girl | Query | 0 | removing tmp table | select a.* from article as a inner join (select a.aid from article as a inner join (select cid from |
| 3225 | root | % | t_girl | Query | 0 | NULL | select a.* from article as a inner join (select a.aid from article as a inner join (select cid from |
| 3226 | root | % | t_girl | Query | 0 | NULL | select a.* from article as a inner join (select a.aid from article as a inner join (select cid from |
+------+------+--------------------+--------------------+---------+-------+--------------------+------------------------------------------------------------------------------------------------------+
55 rows in set (0.00 sec)
上面的测试语句其实也可以这样写
[root@localhost ~]# mysqlslap --defaults-file=/usr/local/mysql-maria/my.cnf --concurrency=25,50,100 --iterations=1 --create-schema='t_girl' --query='call sp_get_article(2,10,1);' --number-of-queries=5000 --debug-info -uroot -p -S/tmp/mysql50.sock
小总结一下。
mysqlslap对于模拟多个用户同时对MySQL发起“进攻”提供了方便。同时详细的提供了“高负荷攻击MySQL”的详细数据报告。
而且如果你想对于多个引擎的性能。这个工具再好不过了。