天天看点

基于sysbench-0.5的MySQL自动化压测及分析出图

准备测试数据(这步在脚本之外)----运行脚本测试(线程数脚本内指定,每个条件测试三次)----脚本对每次测试输出过滤并格式化后写入数据库----脚本加“分析参数”将结果直观展示出来----亦或直接加“画图参数”画出图形(画图基于gnuplot)

下面为脚本使用说明截图

帮助信息:

<a href="http://s1.51cto.com/wyfs02/M00/88/EC/wKioL1gA9_vBi8sVAAUi_ih9Afw578.png" target="_blank"></a>

进行测试:

<a href="http://s4.51cto.com/wyfs02/M00/7C/CC/wKiom1bYAeOA1GxwAAKll2FSJjs327.png" target="_blank"></a>

查看测试结果:

对结果进行画图:

<a href="http://s5.51cto.com/wyfs02/M00/88/F0/wKiom1gA-i_DtcQhAAB_1VY5wcQ438.png" target="_blank"></a>

效果图展示:

<a href="http://s2.51cto.com/wyfs02/M02/88/EC/wKioL1gA-33y64FqAAAfUIYcEh0352.png" target="_blank"></a>

  接下来说说sysbench-0.5,对于数据库的测试较0.4版本有较大不同,之前有内建的--test=oltp方法,现在改成了外部的lua脚本形式,这样更灵活,也方便用户构建自己的测试模型。

  这些相关的lua脚本位于”/usr/share/doc/sysbench/tests/db/“ 目录,其内脚本如下图所示

<a href="http://s2.51cto.com/wyfs02/M01/7C/BD/wKiom1bW8M-yS4DpAAAoCWAEXJI478.png" target="_blank"></a>

  我们需要了解我们最有可能用到的三个脚本:common.lua(公共脚本)、oltp.lua(oltp测试主脚本)和parallel_prepare.lua(并行准备数据)。common.lua中定义了一些选项的默认值(故而,这些选项的值既可以通过命令行指定也可直接修改该脚本里对应值来更改).

  简单说一下oltp.lua脚本的逻辑:

默认通过显式的使用begin和commit语句将如下模式的sql组合在一起形成一个事务(只读测试的话则没有写请求)

10条    SELECT c FROM sbtest6 WHERE id=5047;

1条    SELECT c FROM sbtest16 WHERE id BETWEEN 5050 AND 5050+99;

1条    SELECT SUM(K) FROM sbtest7 WHERE id BETWEEN 5039 AND 5039+99;

1条    SELECT c FROM sbtest7 WHERE id BETWEEN 4987 AND 4987+99 ORDER BY c;

1条    SELECT DISTINCT c FROM sbtest7 WHERE id BETWEEN 13 AND 13+99 ORDER BY c;

1条    UPDATE sbtest1 SET k=k+1 WHERE id=1234;

1条    UPDATE sbtest2 SET c='78864443858-59732318638' where id=2345;

1条    DELETE FROM sbtest11 WHERE id=4958;

1条    INSERT 语句;

然后将此事务循环执行10000次。也就是只读测试共14w请求,混合测试18w请求。若觉得数量不够,可以修改common.lua中的设置

function set_vars()

   oltp_table_size = oltp_table_size or 10000

   oltp_range_size = oltp_range_size or 100

   oltp_tables_count = oltp_tables_count or 1

   oltp_point_selects = oltp_point_selects or 20 (原来10)

   oltp_simple_ranges = oltp_simple_ranges or 2 (原来1)

   oltp_sum_ranges = oltp_sum_ranges or 2 (原来1)

   oltp_order_ranges = oltp_order_ranges or 2 (原来1)

   oltp_distinct_ranges = oltp_distinct_ranges or 2 (原来1)

   oltp_index_updates = oltp_index_updates or 1

   oltp_non_index_updates = oltp_non_index_updates or 1

这样总的测试请求量会变成28w

以上是通过lua脚本里总结出来的,各位也可查看下这些lua脚本,来更好的理解测试的逻辑过程。

一般来说,对MySQL做压测会基于两种需求:

  一种是通过压测来大致评估MySQL实例的最大能力,这种适合给定时长来测;

  另一种就是来对比某些改动前后的性能变化(如版本升级、参数调整等),这种适合给定请求数来测。

以作者的小经验来看,后者要更多一些,所以我的测试模式也是趋向于后者的。

前提功课做好了,接下来一起看一下本例的测试过程

准备数据:

  在被测的两台mysql上分别执行

1

2

3

4

5

<code>#以8线程并发创建16张50w数据的表</code>

<code>sysbench --</code><code>test</code><code>=</code><code>/usr/share/doc/sysbench/tests/db/parallel_prepare</code><code>.lua \</code>

<code>         </code><code>--mysql-table-engine=innodb --oltp-table-size=500000 --mysql-user=user \</code>

<code>         </code><code>--mysql-password=</code><code>'passwd'</code> <code>--mysql-port=3306 --mysql-host=192.168.1.33 \</code>

<code>         </code><code>--oltp-tables-count=16 --num-threads=8 run</code>

  还有另外一种方式,用oltp.lua脚本以串行方式准备数据

<code>sysbench --</code><code>test</code><code>=</code><code>/usr/share/doc/sysbench/tests/db/oltp</code><code>.lua --mysql-table-engine=innodb \</code>

<code>         </code><code>--oltp-table-size=500000 --mysql-user=user --mysql-password=</code><code>'passwd'</code> <code>\</code>

<code>         </code><code>--mysql-port=3306 --mysql-host=192.168.1.33 --oltp-tables-count=16 prepare</code>

开始测试:

<code>sh </code><code>/root/shells/mysql_oltp_test</code><code>.sh </code><code>test</code> <code>read</code><code>-only 192.168.1.44 3306 user </code><code>passwd</code>

下面为脚本内容,注释挺详细,我想就无需多说了。

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

<code>#!/bin/sh</code>

<code>#通过sysbench测试mysql相关性能,并将关键数据存储于‘test.sysbenc_test’表中</code>

<code>#定义记录测试结果的mysql连接相关参数,本例我在测试机上记录测试结果</code>

<code>m_user=</code><code>'test'</code>

<code>m_passwd=</code><code>'test'</code>

<code>m_port=</code><code>'3307'</code>

<code>m_host=</code><code>'127.0.0.1'</code>

<code>#定义错误日志文件</code>

<code>log=</code><code>/tmp/mysql_oltp</code><code>.log</code>

<code>#定义测试线程</code>

<code>threds_num=</code><code>'8 24 48 64 96 128 160 196 256'</code>

<code>#测试函数</code>

<code>sb_test() {</code>

<code>    </code><code>#定义测试方式相关变量</code>

<code>    </code><code>tables_count=16    </code><code>#测试表的数量</code>

<code>    </code><code>if</code> <code>[ </code><code>"$3"</code> <code>== </code><code>"read-only"</code> <code>];</code><code>then</code> <code>read_only=</code><code>'on'</code><code>;</code><code>else</code> <code>read_only=</code><code>'off'</code><code>;</code><code>fi</code>    <code>#根据脚本参数确定是否read-only</code>

<code>    </code><code>#创建记录测试信息的表</code>

<code>    </code><code>echo</code> <code>-e </code><code>"\n---------------\n创建测测试结果表test.sysbench_test\n---------------"</code>

<code>    </code><code>mysql -u$m_user -p$m_passwd -P$m_port -h$m_host &lt;&lt;EOF</code>

<code>        </code><code>CREATE TABLE IF NOT EXISTS </code><code>test</code><code>.sysbench_test (</code>

<code>        </code><code>scenario varchar(30) NOT NULL DEFAULT </code><code>''</code> <code>COMMENT </code><code>'测试场景'</code><code>,</code>

<code>        </code><code>server_name varchar(15) NOT NULL COMMENT </code><code>'被测DB name'</code><code>,</code>

<code>        </code><code>test_type varchar(15) NOT NULL COMMENT </code><code>'read-only,read-write,insert等'</code><code>,</code>

<code>        </code><code>sb_threads int(11) NOT NULL DEFAULT </code><code>'0'</code> <code>COMMENT </code><code>'sysbench 测试线程'</code><code>,</code>

<code>        </code><code>server_load decimal(12,2) NOT NULL DEFAULT </code><code>'0.00'</code> <code>COMMENT </code><code>'以当前线程测试完后立刻记录一分钟负载值'</code><code>,</code>

<code>        </code><code>request_total int(11) NOT NULL DEFAULT </code><code>'0'</code><code>,</code>

<code>        </code><code>request_read int(11) NOT NULL DEFAULT </code><code>'0'</code><code>,</code>

<code>        </code><code>request_write int(11) NOT NULL DEFAULT </code><code>'0'</code><code>,</code>

<code>        </code><code>request_per_second decimal(12,2) NOT NULL DEFAULT </code><code>'0.00'</code><code>,</code>

<code>        </code><code>total_time decimal(12,2) NOT NULL DEFAULT </code><code>'0.00'</code> <code>COMMENT </code><code>'单位秒'</code><code>,</code>

<code>        </code><code>95_pct_time decimal(12,2) NOT NULL DEFAULT </code><code>'0.00'</code> <code>COMMENT </code><code>'单位毫秒'</code>

<code>        </code><code>) ENGINE=InnoDB DEFAULT CHARSET=utf8;</code>

<code>EOF</code>

<code>    </code><code>if</code> <code>[ $? -</code><code>ne</code> <code>0 ];</code><code>then</code> <code>exit</code> <code>-1;</code><code>fi</code>

<code>    </code><code>#开始测试,每种条件测3次,分析时取平均值</code>

<code>    </code><code>echo</code> <code>-e </code><code>"\n---------------\n场景:$2 模式:$3\n---------------"</code>

<code>    </code><code>for</code> <code>i </code><code>in</code> <code>{1..3};</code><code>do</code>

<code>        </code> 

<code>        </code><code>for</code> <code>sb_threds </code><code>in</code> <code>$threds_num;</code><code>do</code>    <code>#按照指定的sysbench线程测试</code>

<code>            </code><code>printf</code> <code>"  %-10s %s\n"</code> <code>$sb_threds线程 第$i次运行...</code>

<code>            </code> 

<code>            </code><code>#result 作为每次最小测试单元的结果,根据sysbench测试结果各参数的出现顺序,以request_read、request_write、request_total、request_per_second、total_time、95_pct_time为顺序插入表中。下条命令中,egerp之后的操作是为了对sysbench的输出做筛选和格式化,以便插入数据库</code>

<code>            </code><code>sysbench --</code><code>test</code><code>=</code><code>/usr/share/doc/sysbench/tests/db/oltp</code><code>.lua --mysql-user=$6 --mysql-password=$7 --mysql-port=$5 --mysql-host=$4 --num-threads=$sb_threds run --oltp-skip-trx=on --oltp-</code><code>read</code><code>-only=$read_only &gt; $log</code>

<code>            </code><code>if</code> <code>[ $? -</code><code>ne</code> <code>0 ];</code><code>then</code>

<code>                </code><code>echo</code> <code>-e </code><code>"\nSysbench error! For more information see $log"</code>

<code>                </code><code>exit</code> <code>-1</code>

<code>            </code><code>fi</code>

<code>            </code><code>result=$(</code><code>cat</code> <code>$log | </code><code>egrep</code>  <code>"read:|write:|read/write.*:|total:|total\ time:|approx\..*95.*:"</code> <code>|</code><code>sed</code> <code>-r -e </code><code>"s/[0-9]+ \(//g"</code> <code>-e </code><code>"s/\ per sec\.\)//g"</code> <code>-e </code><code>"s/m?s$//g"</code> <code>| </code><code>awk</code>  <code>'{printf("%s ",$NF)}'</code><code>|</code><code>sed</code> <code>"s/\ /,/g"</code> <code>| </code><code>sed</code> <code>"s/,$//g"</code><code>)</code>

<code>            </code><code>#测试完成后立刻记录系统一分钟负载值,可近似认为测试过程中proxy的负载抽样</code>

<code>            </code><code>load=$(</code><code>ssh</code> <code>-p22 $4 </code><code>"uptime|awk -F: '{print \$NF}'|awk -F, '{print \$1}'"</code> <code>2&gt;</code><code>/dev/null</code><code>)</code>

<code>            </code><code>#本次测试结果写入数据库</code>

<code>            </code><code>mysql -u$m_user -p$m_passwd -P$m_port -h$m_host &lt;&lt;EOF 2&gt; $log</code>

<code>                </code><code>INSERT INTO </code><code>test</code><code>.sysbench_test (scenario,server_name,test_type,sb_threads,server_load,request_read,request_write,request_total,request_per_second,total_time,95_pct_time) </code>

<code>                </code><code>VALUES (</code><code>'$2'</code><code>,</code><code>'$4'</code><code>,</code><code>'$3'</code><code>,</code><code>'$sb_threds'</code><code>,</code><code>'$load'</code><code>,$result);</code>

<code>    </code> 

<code>                </code><code>echo</code> <code>-e </code><code>"\n----------$sb_threds线程测试,第$i次插入数据库时失败----------"</code>

<code>                </code><code>echo</code> <code>"INSERT VALUES ('$2','$4','$3',$sb_threds,$load,$result)"</code>

<code>                </code><code>exit</code> <code>-2</code>

<code>            </code><code>sleep</code> <code>60    </code><code>#让库歇一会,也让一分钟负载能够恢复到测试前的值</code>

<code>        </code><code>done</code>

<code>    </code><code>done</code>

<code>}</code>

<code>#结果分析函数</code>

<code>sb_analyse() {</code>

<code>     </code><code>mysql -u$m_user -p$m_passwd -h$m_host -P$m_port &lt;&lt;EOF 2&gt; $log</code>

<code>        </code><code>SELECT</code>

<code>        </code><code>scenario, </code>

<code>        </code><code>server_name,</code>

<code>        </code><code>test_type,</code>

<code>        </code><code>sb_threads,</code>

<code>        </code><code>convert(avg(server_load),decimal(12,2)) as server_load,</code>

<code>        </code><code>convert(avg(request_total),decimal(12,0)) as request_total,</code>

<code>        </code><code>convert(avg(request_read),decimal(12,0)) as request_read,</code>

<code>        </code><code>convert(avg(request_write),decimal(12,0)) as request_write,</code>

<code>        </code><code>convert(avg(request_per_second),decimal(12,2)) as request_per_second,</code>

<code>        </code><code>convert(avg(total_time),decimal(12,2)) as total_time,</code>

<code>        </code><code>convert(avg(95_pct_time),decimal(12,2)) as 95_pct_time</code>

<code>        </code><code>FROM </code><code>test</code><code>.sysbench_test group by scenario,server_name,test_type,sb_threads</code>

<code>#画图函数</code>

<code>sb_chart() {</code>

<code>    </code><code>sb_analyse &gt; </code><code>/tmp/mysql_oltp</code><code>.dat</code>

<code>    </code><code>for</code> <code>chart_type </code><code>in</code> <code>"request_per_second"</code> <code>"total_time"</code> <code>"95_pct_time"</code><code>;</code><code>do</code>    <code>#这里写死了关注的三个指标,也就是会画三张图</code>

<code>        </code><code>col_num=0    </code><code>#该行及下面这个for循环用于取得三个指标在数据中的列号</code>

<code>        </code><code>for</code> <code>col_name </code><code>in</code> <code>`</code><code>cat</code> <code>/tmp/aualyse</code><code>.txt |</code><code>awk</code> <code>'NR&lt;2 {print}'</code><code>`;</code><code>do</code>

<code>            </code><code>let</code> <code>col_num++</code>

<code>            </code><code>if</code> <code>[ $col_name == $chart_type ];</code><code>then</code> <code>break</code><code>;</code><code>fi</code>

<code>        </code><code>if</code> <code>[ $chart_type == </code><code>"request_per_second"</code> <code>];</code><code>then</code>    <code>#根据图表特点为不同的chart_type设置不同的key position</code>

<code>            </code><code>key_pos=</code><code>"bottom right"</code>

<code>            </code><code>unit=</code><code>""</code>

<code>        </code><code>elif</code> <code>[ $chart_type == </code><code>"total_time"</code> <code>];</code><code>then</code>

<code>            </code><code>key_pos=</code><code>"top right"</code>

<code>            </code><code>unit=</code><code>"(s)"</code>

<code>        </code><code>elif</code> <code>[ $chart_type == </code><code>"95_pct_time"</code> <code>];</code><code>then</code>

<code>            </code><code>key_pos=</code><code>"top left"</code>

<code>            </code><code>unit=</code><code>"(ms)"</code>

<code>        </code><code>fi</code>

<code>        </code><code>plot_cmd=</code><code>"set term png size 800,600;set output '/tmp/$chart_type.png';set title '$chart_type $unit';set grid;set key $key_pos;plot "</code>

<code>        </code><code>if</code> <code>[ $</code><code># -eq 0 ];then</code>

<code>            </code><code>#对分析结果中所有场景进行画图</code>

<code>            </code><code>for</code> <code>scenario </code><code>in</code> <code>`mysql -u$m_user -p$m_passwd -h$m_host -P$m_port -s -e </code><code>"select distinct(scenario) from test.sysbench_test"</code> <code>2&gt;</code><code>/dev/null</code><code>`;</code><code>do</code>

<code>                </code><code>sb_analyse | </code><code>awk</code> <code>-</code><code>v</code> <code>scenario=$scenario </code><code>'$1 == scenario {print}'</code> <code>&gt; </code><code>/tmp/</code><code>"$scenario.dat"</code>

<code>                </code><code>plot_cmd=${plot_cmd}</code><code>"'/tmp/"</code><code>$scenario.dat</code><code>"' using $col_num:xtic(4) title '$scenario' with linespoints lw 2,"</code>

<code>            </code><code>done</code>

<code>            </code><code>plot_cmd=$(</code><code>echo</code> <code>$plot_cmd | </code><code>sed</code> <code>'s/,$//g'</code><code>)</code>

<code>            </code><code>echo</code> <code>$plot_cmd | gnuplot</code>

<code>        </code><code>else</code>

<code>            </code><code>#只绘制指定的场景</code>

<code>            </code><code>for</code> <code>scenario </code><code>in</code> <code>$*;</code><code>do</code>

<code>            </code><code>echo</code> <code>"$plot_cmd"</code> <code>| gnuplot</code>

<code>#脚本使用说明/参数判断</code>

<code>if</code> <code>[ $</code><code># -eq 1 ] &amp;&amp; [ $1 == "-h" -o $1 == "--help" ];then</code>

<code>    </code><code>echo</code> <code>-e </code><code>"\nUsage: $0 {test test_scenario test_type mysql_host mysql_port mysql_user mysql_password} | {analyse} | {chart [scenario]...}\n"</code>

<code>    </code><code>echo</code> <code>----------</code>

<code>    </code><code>echo</code> <code>-e </code><code>"测试: 请在脚本后跟上 test test_scenario test_type mysql_host mysql_port mysql_user mysql_password 7个参数 !"</code>

<code>    </code><code>echo</code> <code>-e </code><code>"      test_type: read-only 或 read-write, 表示测试模式"</code>

<code>    </code><code>echo</code> <code>-e </code><code>"      其余4参数表示待测试MySQL连接相关信息,密码若包含特殊字符,将其置于单引号内"</code>

<code>    </code><code>echo</code> <code>-e </code><code>"----------"</code>

<code>    </code><code>echo</code> <code>-e </code><code>"分析: 请在脚本后跟上 analyse"</code>

<code>    </code><code>echo</code> <code>-e </code><code>"画图: 请在脚本后面跟上"</code>

<code>    </code><code>echo</code> <code>-e </code><code>"      会在/tmp/下生成request_per_second.png total_time.png 95_pct_time.png 三张图"</code>        

<code>    </code><code>echo</code> <code>-e </code><code>"      chart (对分析结果中的所有测试场景画图)"</code>

<code>    </code><code>echo</code> <code>-e </code><code>"      chart scenario ... (对指定的测试场景画图,场景名可查看analyse)\n"</code>

<code>    </code><code>exit</code> <code>-1</code>

<code>elif</code> <code>[ </code><code>"$1"</code> <code>== </code><code>"test"</code> <code>-a  $</code><code># -eq 7 ];then</code>

<code>    </code><code>sb_test $1 $2 $3 $4 $5 $6 $7</code>

<code>elif</code> <code>[ </code><code>"$1"</code> <code>== </code><code>"analyse"</code> <code>-a $</code><code># -eq 1 ];then</code>

<code>    </code><code>sb_analyse</code>

<code>elif</code> <code>[ </code><code>"$1"</code> <code>== </code><code>"chart"</code> <code>];</code><code>then</code>

<code>    </code><code>#chart函数可不接参数,也可接任意个'测试场景'作为参数</code>

<code>    </code><code>arg=($*)</code>

<code>    </code><code>arg_len=${</code><code>#arg[@]}</code>

<code>    </code><code>sb_chart ${arg[@]:1:$arg_len-1}</code>

<code>else</code>

<code>fi</code>

<code>### by ljk 2016/10/14</code>

清空测试数据:

<code>--mysql-user=user --mysql-password=</code><code>'passwd'</code> <code>--mysql-port=3306 \</code>

<code>--mysql-host=192.168.1.22 --oltp-tables-count=16 --num-threads=8 cleanup</code>

     本文转自kai404 51CTO博客,原文链接:http://blog.51cto.com/kaifly/1747226,如需转载请自行联系原作者