天天看點

基于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,如需轉載請自行聯系原作者