前言
有很多人擔心生産系統上新東西的程式怕壓力跟不上和穩定性不行,是的,大家都怕,是以上司要求做一次壓力測試,我個人也覺得是有必要的.
如果按原理來說,mycat如果不做分片,純粹隻是代理的話,他所做的事情,其實更多的是資料轉發,而這個轉發能力,當然就是看他能有多強.
既然理論上轉發能力很強,那麼是不是就可以忽略不計呢,當然不是,是以需要用直連mysql的測試資料來做對比.
測試前準備
伺服器配置為32核cpu(虛拟化後的數值,算上超線程),120G記憶體,16000iops的儲存設備,具體配置設定情況:
10.21.128.156:mycat1.6.5,sysbench0.4
10.21.128.157:mysql5.7.20主庫
10.21.128.158:mysql5.7.20從庫1
10.21.128.159:mysql5.7.20從庫2
簡單說明拓撲關系:
第一步當然是安裝好mysql,這裡就不詳細介紹怎麼安裝了,但是,my.cnf的參數是有些變化的,主要原因是要适應高并發壓測環境,不然就被參數限制,然後程式退出.當然了,如果你想盡量模拟線上環境,那這些限制你得思考在内,更改就需要謹慎一些,我這裡隻是給出一例來參考.
<code>#首先,就是要把系統的連接配接數和打開檔案數搞起來</code>
<code>ulimit</code> <code>-SHn 65535</code>
<code>#想永久生效就要這樣</code>
<code>echo</code> <code>"</code>
<code>* soft nofile 65535</code>
<code>* hard nofile 65535</code>
<code>root soft nofile 65535</code>
<code>root hard nofile 65535</code>
<code>" >> </code><code>/etc/security/limits</code><code>.conf</code>
然後,更改mysql配置檔案參數,其他buffer_pool什麼的就不列出來了,請各自看情況設定,這裡隻說明涉及壓測相關的參數.
<code>#打開配置檔案</code>
<code>vim </code><code>/usr/local/mysql/my</code><code>.cnf</code>
<code>#其他參數我們暫時忽略,隻看這些關乎壓測的參數</code>
<code>[mysqld]</code>
<code>#每台機都需要不一樣的ID,主從環境下</code>
<code>server-</code><code>id</code> <code>= 128157</code>
<code>#全局最大打開檔案數,不可以超過系統設定的最大檔案數,不然無效</code>
<code>open_files_limit = 65530</code>
<code>#innodb引擎限制一次打開表空間檔案.ibd的檔案描述符數量</code>
<code>innodb_open_files = 65530</code>
<code>#允許存到緩存裡的表的句柄數量,壓測自然要打開很多表,設大一點就對了,受系統limit和上面兩個參數一起限制</code>
<code>table_open_cache = 65000</code>
<code>#執行個體可用最大連接配接數,必須設定足夠大,不然連接配接數超出限制,測着報錯不通就麻煩了,最少也比你實際要用到的多三分一</code>
<code>max_connections=30000</code>
<code>#最大每使用者連接配接數,限制每個使用者最大連接配接數,一定要比上面少一點</code>
<code>max_user_connections=20000</code>
<code>#最大資料包大小,做壓測改大一點還是有必要</code>
<code>max_allowed_packet = 128M</code>
<code>#限制了同一時間在mysqld上所有session中prepared 語句的上限,平時不用理這個參數,但是壓測就需要調大一點</code>
<code>max_prepared_stmt_count=100000</code>
其他參數我就不一一列舉,自己看情況來設定就行,然後,重新開機生效待命.
軟體安裝
先說說壓測工具的選擇問題,在MySQL協定上Mycat不相容tpcc,是以放棄tpcc。然後sysbench1.0對mycat相容也比較欠佳,不明原因壓測失敗次數多,是以也隻能放棄.
最後標明sysbench0.4和0.5來使用,可以順利測出結果,而且從壓測的原理來說也比較客觀.
是以,我們需要安裝的軟體有:mysql(預設已裝),mycat,sysbench0.4或0.5
mysql怎麼安裝和授權什麼的,這裡就不細說了,還請各位自己搭建好,配置檔案就上面提到的要加上一下.
安裝mycat:
1)搭建jdk環境
由于mycat是java程式,是以需要安裝JDK,版本至少要jdk1.6以上,
<code>#下載下傳好安裝包後,解壓建立軟連接配接</code>
<code>tar</code> <code>xf jdk-8u144-linux-x64.</code><code>tar</code><code>.gz</code>
<code>mv</code> <code>jdk1.8.0_144/ </code><code>/usr/local/</code>
<code>ln</code> <code>-sf jdk1.8.0_144/ jdk</code>
<code>#建立環境變量</code>
<code>vim </code><code>/etc/profile</code><code>.d</code><code>/java</code><code>.sh</code>
<code>export</code> <code>JAVA_HOME=</code><code>/usr/local/jdk</code>
<code>export</code> <code>JRE_HOME=</code><code>/usr/local/jdk/jre</code>
<code>export</code> <code>CLASSPATH=.:$JAVA_HOME</code><code>/lib/dt</code><code>.jar:$JAVA_HOME</code><code>/lib/tools</code><code>.jar:$JRE_HOME</code><code>/lib</code>
<code>export</code> <code>PATH=$PATH:$JAVA_HOME</code><code>/bin</code>
<code>#重載環境變量</code>
<code>source</code> <code>/etc/profile</code>
<code>#驗證安裝</code>
<code>java -version</code>
<code>java version </code><code>"1.8.0_144"</code>
<code>Java(TM) SE Runtime Environment (build 1.8.0_144-b01)</code>
<code>Java HotSpot(TM) 64-Bit Server VM (build 25.144-b01, mixed mode)</code>
安裝完成,可以使用.
2)配置mycat
了解java程式的同學應該知道,java程式隻要配置好,直接啟動就能用,是以沒有安裝的概念,直接将mycat配置做好就可以用.
因為sysbench的測試沒有涉及分庫分表,是以mycat隻需要設定server.xml和schema.xml即可,具體mycat存放路徑沒規定,我将他放在/usr/local了.
ps:有意向做分庫分表壓測的就要把資料做一些處理,按照sysbench原理來說是可行的,他測試的表的數量是可控的,你把帶數字編号的表通過邏輯庫處理可以集合成一個新的邏輯表.
先看基本環境設定server.xml,大部分設定都可以不動,注意添加修改的是<system>标簽下面配置就可以了,每建立一個使用者<user>是要另外起标簽:
vim /usr/local/mycat/conf/server.xml
<code><</code><code>system</code><code>></code>
<code><</code><code>property</code> <code>name</code><code>=</code><code>"idleTimeout"</code><code>>2880000</</code><code>property</code><code>> </code><code><!--設定逾時時間為28800秒 --></code>
<code><</code><code>property</code> <code>name</code><code>=</code><code>"maxPacketSize"</code><code>>134217728</</code><code>property</code><code>> </code><code><!--設定最大網絡包為128M --></code>
<code><</code><code>property</code> <code>name</code><code>=</code><code>"charset"</code><code>>utf8</</code><code>property</code><code>> </code><code><!--設定預設字元集為utf8 --></code>
<code><</code><code>property</code> <code>name</code><code>=</code><code>"txIsolation"</code><code>>2</</code><code>property</code><code>> </code><code><!--設定隔離級别為RC --></code>
<code><</code><code>property</code> <code>name</code><code>=</code><code>"sqlExecuteTimeout"</code><code>>600</</code><code>property</code><code>> </code><code><!--設定sql執行的逾時時間為600秒 --></code>
<code></</code><code>system</code><code>></code>
<code><!--下面是設定mycat的使用者名/密碼和權限控制,和mysql的使用者名密碼無關 --></code>
<code><</code><code>user</code> <code>name</code><code>=</code><code>"root"</code> <code>defaultAccount</code><code>=</code><code>"true"</code><code>></code>
<code> </code><code><</code><code>property</code> <code>name</code><code>=</code><code>"password"</code><code>>mycat123</</code><code>property</code><code>></code>
<code> </code><code><</code><code>property</code> <code>name</code><code>=</code><code>"schemas"</code><code>>sbtest,testppp</</code><code>property</code><code>></code>
<code></</code><code>user</code><code>></code>
<code><</code><code>user</code> <code>name</code><code>=</code><code>"sysbench"</code><code>></code>
<code> </code><code><</code><code>property</code> <code>name</code><code>=</code><code>"password"</code><code>>sb123</</code><code>property</code><code>></code>
<code> </code><code><</code><code>property</code> <code>name</code><code>=</code><code>"schemas"</code><code>>sbtest</</code><code>property</code><code>></code>
<code> </code><code></</code><code>user</code><code>></code>
<code><</code><code>user</code> <code>name</code><code>=</code><code>"test"</code><code>></code>
<code> </code><code><</code><code>property</code> <code>name</code><code>=</code><code>"password"</code><code>>test</</code><code>property</code><code>></code>
<code> </code><code><</code><code>property</code> <code>name</code><code>=</code><code>"readOnly"</code><code>>true</</code><code>property</code><code>></code>
<code> </code><code><</code><code>privileges</code> <code>check</code><code>=</code><code>"false"</code><code>></code>
<code> </code><code><</code><code>schema</code> <code>name</code><code>=</code><code>"sbtest"</code> <code>dml</code><code>=</code><code>"0001"</code> <code>></code>
<code> </code><code><</code><code>table</code> <code>name</code><code>=</code><code>"sbtest11"</code> <code>dml</code><code>=</code><code>"1111"</code><code>></</code><code>table</code><code>></code>
<code> </code><code></</code><code>schema</code><code>></code>
<code> </code><code></</code><code>privileges</code><code>></code>
設定了一些相關壓測的項目參數,和建立了三個使用者root,sysbench,test.這三個使用者和資料庫的使用者沒有關聯,是獨立的,即使這個使用者密碼被破解,資料庫的密碼依然安全.其中root有完全控制權限,sysbench隻能控制sbtest庫,test也隻能控制sbtest庫,而且限制了讀寫權限.
然後設定邏輯庫配置檔案schema.xml,這裡改動比較多,是以直接貼上整個檔案:
<code><?</code><code>xml</code> <code>version</code><code>=</code><code>"1.0"</code><code>?></code>
<code><!DOCTYPE mycat:schema SYSTEM "schema.dtd"></code>
<code><</code><code>mycat:schema</code> <code>xmlns:mycat</code><code>=</code><code>"http://io.mycat/"</code><code>></code>
<code> </code><code><</code><code>schema</code> <code>name</code><code>=</code><code>"sbtest"</code> <code>checkSQLschema</code><code>=</code><code>"false"</code> <code>sqlMaxLimit</code><code>=</code><code>"100"</code> <code>dataNode</code><code>=</code><code>"dn1"</code><code>></code>
<code> </code><code></</code><code>schema</code><code>></code>
<code> </code><code><</code><code>schema</code> <code>name</code><code>=</code><code>"testppp"</code> <code>checkSQLschema</code><code>=</code><code>"false"</code> <code>sqlMaxLimit</code><code>=</code><code>"100"</code> <code>dataNode</code><code>=</code><code>"dn2"</code><code>></code>
<code> </code><code><!-- <dataNode name="dn1$0-743" dataHost="localhost1" database="db$0-743"</code>
<code> </code><code>/> --></code>
<code> </code><code><</code><code>dataNode</code> <code>name</code><code>=</code><code>"dn1"</code> <code>dataHost</code><code>=</code><code>"10.21.128.157"</code> <code>database</code><code>=</code><code>"sbtest"</code> <code>/></code>
<code> </code><code><</code><code>dataNode</code> <code>name</code><code>=</code><code>"dn2"</code> <code>dataHost</code><code>=</code><code>"10.21.128.157"</code> <code>database</code><code>=</code><code>"testppp"</code> <code>/></code>
<code> </code><code><!--<dataNode name="dn4" dataHost="sequoiadb1" database="SAMPLE" /></code>
<code> </code><code><dataNode name="jdbc_dn1" dataHost="jdbchost" database="db1" /></code>
<code> </code><code><dataNode name="jdbc_dn2" dataHost="jdbchost" database="db2" /></code>
<code> </code><code><dataNode name="jdbc_dn3" dataHost="jdbchost" database="db3" /> --></code>
<code> </code><code><</code><code>dataHost</code> <code>name</code><code>=</code><code>"10.21.128.157"</code> <code>maxCon</code><code>=</code><code>"3000"</code> <code>minCon</code><code>=</code><code>"20"</code> <code>balance</code><code>=</code><code>"0"</code>
<code> </code><code>writeType</code><code>=</code><code>"0"</code> <code>dbType</code><code>=</code><code>"mysql"</code> <code>dbDriver</code><code>=</code><code>"native"</code> <code>switchType</code><code>=</code><code>"-1"</code> <code>slaveThreshold</code><code>=</code><code>"100"</code><code>></code>
<code> </code><code><</code><code>heartbeat</code><code>>select user()</</code><code>heartbeat</code><code>></code>
<code> </code><code><!-- can have multi write hosts --></code>
<code> </code><code><</code><code>writeHost</code> <code>host</code><code>=</code><code>"hostM1"</code> <code>url</code><code>=</code><code>"10.21.128.157:3306"</code> <code>user</code><code>=</code><code>"root"</code> <code>password</code><code>=</code><code>"128157"</code><code>></code>
<code> </code><code><!-- can have multi read hosts --></code>
<code> </code><code><</code><code>readHost</code> <code>host</code><code>=</code><code>"hostS2"</code> <code>url</code><code>=</code><code>"10.21.128.158:3306"</code> <code>user</code><code>=</code><code>"root"</code> <code>password</code><code>=</code><code>"128157"</code> <code>/></code>
<code> </code><code><</code><code>readHost</code> <code>host</code><code>=</code><code>"hostS3"</code> <code>url</code><code>=</code><code>"10.21.128.159:3306"</code> <code>user</code><code>=</code><code>"root"</code> <code>password</code><code>=</code><code>"128157"</code> <code>/></code>
<code> </code><code></</code><code>writeHost</code><code>></code>
<code> </code><code><!-- <writeHost host="hostM2" url="localhost:3316" user="root" password="123456"/> --></code>
<code> </code><code></</code><code>dataHost</code><code>></code>
<code></</code><code>mycat:schema</code><code>></code>
設定了兩個邏輯庫sbtest和testppp(和真實庫同名,其實可以不同),指向的真實資料庫的主從環境dn1和dn2,裡面有一主兩從三台真實伺服器位址,并開啟讀寫分離.但是要注意的是,事務隻走主庫,是以讀寫分離最優方案還是由程式做好一點,用中間件做難免就有點雞肋了,畢竟現在很多開發架構都走事務的.
3) 啟動mycat,
<code>#啟動mycat</code>
<code>Mycat start</code>
<code>#重新開機mycat</code>
<code>Mycat restart</code>
<code>#關閉mycat</code>
<code>Mycat stop</code>
<code>#而Mycat預設的連接配接端口是8066,管理端口是9066,可以在server.xml修改.</code>
<code>ss -ntplu |</code><code>grep</code> <code>java</code>
<code>tcp LISTEN 0 100 :::9066 :::* </code><code>users</code><code>:((</code><code>"java"</code><code>,pid=115779,fd=168))</code>
<code>tcp LISTEN 0 100 :::8066 :::* </code><code>users</code><code>:((</code><code>"java"</code><code>,pid=115779,fd=172))</code>
<code>#看到端口起來了,就可以使用了,因為mycat支援mysql原生協定,是以連上8066端口是不會和一般mysql有什麼差別,直接就能使用了.</code>
<code>#如果更改了任何配置檔案,可以登入進管理端口執行下列指令實時熱重新整理配置,相當友善.</code>
<code>mysql -uroot -pmycat123 -h10.21.128.156 -P9066 </code>
<code>#更改server的配置可以</code>
<code>Mysql>reload @@config</code>
<code>#如果改了schema的配置,需要這個指令</code>
<code>Mysql>reload @@config_all</code>
加載完成就可以使用新配置了,哪怕是改了登入使用者名也能熱加載.
搭建sysbench
下載下傳下來後,隻要有c運作庫就能編譯安裝,但是要建立mysql庫檔案的軟連接配接,不然會報錯找不到庫檔案,
<code>#先安裝依賴包</code>
<code>yum </code><code>install</code> <code>-y automake libtool</code>
<code>#先建立mysql庫檔案的軟連接配接,不然編譯會報錯的</code>
<code>ln</code> <code>-sf </code><code>/usr/local/mysql/lib/libmysql</code><code>* </code><code>/usr/lib64</code>
<code>ln</code> <code>-sf </code><code>/usr/local/mysql/lib/libmysqlclient</code><code>.so </code><code>/usr/lib64/libmysqlclient_r</code><code>.so</code>
<code>#然後執行:</code>
<code>tar</code> <code>xf sysbench-0.4.12-1.1.tgz</code>
<code>cd</code> <code>sysbench-0.4.12-1.1</code>
<code>.</code><code>/autogen</code><code>.sh</code>
<code>.</code><code>/configure</code> <code>--with-mysql-includes=</code><code>/usr/local/mysql/include</code> <code>--with-mysql-libs=</code><code>/usr/local/mysql/lib</code>
<code>Make</code>
<code>#如果 make 沒有報錯,就會在 sysbench 目錄下生成二進制指令行工具 sysbench</code>
<code>cd</code> <code>/tmp/sysbench-0</code><code>.4.12-1.1</code><code>/sysbench</code>
<code>ls</code> <code>-l sysbench</code>
<code>-rwxr-xr-x 1 root root 3293186 Sep 21 16:24 sysbench</code>
在此,環境就搭建完畢了.
開始測試
環境準備好了,就開始測試了,測試前要先準備測試資料,需要使用指令來制造出來,要比較長的時間(可能大半天),重點是要關注硬碟空間是否足夠:
<code>#避免不必要的錯誤,直連資料庫操作</code>
<code>mysql -uroot -p128157 -h10.21.128.157 -P3306</code>
<code>#建立測試資料庫,sysbench預設測試庫名是sbtest,也可以指定庫名</code>
<code>mysql> create database sbtest;</code>
<code>#進入sysbench程式目錄</code>
<code>#開始造資料,</code>
<code>.</code><code>/sysbench</code> <code>--mysql-host=10.21.128.157 --mysql-port=3306 --mysql-user=root --mysql-password=128157 --</code><code>test</code><code>=tests</code><code>/db/oltp</code><code>.lua --oltp_tables_count=15 --oltp-table-size=40000000 --mysql-table-engine=innodb --rand-init=on prepare</code>
<code>#再次強調,時間可能很長,需要耐心等待</code>
參數解析:
--test=tests/db/oltp.lua 表示調用 tests/db/oltp.lua 腳本進行 oltp 模式測試,oltp是啥就不解析了
--oltp_tables_count=15 表示會生成 15 個測試表,數量越多,自然花費時間越長
--oltp-table-size=40000000 表示每個測試表填充資料量為 40000000行 ,數量越多也是越時間長
--rand-init=on 表示每個測試表都是用随機資料來填充的,這樣才客觀
--mysql-table-engine=innodb 表示表的引擎是innodb
prepare 用于準備測試需要的資料,準備完後執行run來測試,測試完成後如果需要清理資料就用cleanup來清除測試資料
是以這裡創造了15個表,裡面每個表有4000萬行的資料,資料為随機輸入,這個資料量大概需要150G硬碟空間,估計可以涵蓋大部分情況了吧,當然,你也可以建立更多資料.
然後就可以進行正式測試了,我們先進行測試普通主從架構:
<code>#執行測試指令</code>
<code>.</code><code>/sysbench</code> <code>--mysql-host=10.21.128.157 --mysql-port=3306 --mysql-user=root --mysql-password=128157 --</code><code>test</code><code>=tests</code><code>/db/oltp</code><code>.lua --oltp_tables_count=15 --oltp-table-size=40000000 --mysql-table-engine=innodb --num-threads=1024 --oltp-</code><code>read</code><code>-only=off --report-interval=10 --rand-</code><code>type</code><code>=uniform --max-</code><code>time</code><code>=3600 --max-requests=0 --percentile=99 run >> </code><code>/tmp/sysbench_oltpX_32_20171113-1</code><code>.log</code>
<code>#我們設定的是3600秒,在此之前終止,也就是測試失敗,需要分析報錯</code>
--num-threads=1024 表示發起1024個并發連接配接
--oltp-read-only=off 表示不要進行隻讀測試,也就是會采用讀寫混合模式測試
--report-interval=10 表示每10秒輸出一次測試進度報告
--rand-type=uniform 表示随機類型為固定模式,其他幾個可選随機模式:uniform(固定),gaussian(高斯),special(特定的),pareto(帕累托)
--max-time=3600 表示最大執行時長為3600秒,測試将在這個時間後結束
--max-requests=0 表示總請求數為 0,因為上面已經定義了總執行時長,是以總請求數可以設定為 0;也可以隻設定總請求數,不設定最大執行時長
--percentile=99 表示設定采樣比例,預設是 95%,即丢棄1%的長請求,在剩餘的99%裡取最大值
根據上面的解析,最後輸出到一個檔案,畢竟需要記錄下來.
然後到mycat代理環境:
<code>.</code><code>/sysbench</code> <code>--mysql-host=10.21.128.156 --mysql-port=8066 --mysql-user=sysbench --mysql-password=sb123 --</code><code>test</code><code>=tests</code><code>/db/oltp</code><code>.lua --oltp_tables_count=15 --oltp-table-size=40000000 --mysql-table-engine=innodb --num-threads=1024 --oltp-</code><code>read</code><code>-only=off --report-interval=10 --rand-</code><code>type</code><code>=uniform --max-</code><code>time</code><code>=3600 --max-requests=40000000 --percentile=99 run >> </code><code>/home/logs/sysbench_oltpX_32_20171113-2</code><code>.log</code>
和上面一樣,設定了并發為1024個線程,測試時間為3600秒即1小時,同時也是用到剛才制造出來的15個4000萬行的表,設定取值采樣平均值為99%的資料,輸出到另一個log檔案.
閱讀測試報告
測試完了,就來看結果了,我們拿其中一個結果來解析說一下:
<code>#忽略掉一些每10秒統計值,隻看最後的統計值</code>
<code>vim </code><code>/tmp/sysbench_oltpX_32_20171113-1</code><code>.log</code>
<code>sysbench 0.5: multi-threaded system evaluation benchmark</code>
<code>Running the </code><code>test</code> <code>with following options:</code>
<code>Number of threads: 1024</code>
<code>Report intermediate results every 10 second(s)</code>
<code>Random number generator seed is 0 and will be ignored</code>
<code>Threads started!</code>
<code>-- 每10秒鐘報告一次測試結果,tps、每秒讀、每秒寫、99%以上的響應時長統計</code>
<code>[ 10s] threads: 1024, tps: 364.69, reads</code><code>/s</code><code>: 6324.66, writes</code><code>/s</code><code>: 1765.66, response </code><code>time</code><code>: 4292.46ms (99%)</code>
<code>[ 20s] threads: 1024, tps: 475.80, reads</code><code>/s</code><code>: 6037.91, writes</code><code>/s</code><code>: 1640.10, response </code><code>time</code><code>: 4088.05ms (99%)</code>
<code>[ 30s] threads: 1024, tps: 439.40, reads</code><code>/s</code><code>: 6349.45, writes</code><code>/s</code><code>: 1808.89, response </code><code>time</code><code>: 3248.44ms (99%)</code>
<code>[ 40s] threads: 1024, tps: 487.70, reads</code><code>/s</code><code>: 6438.46, writes</code><code>/s</code><code>: 1879.72, response </code><code>time</code><code>: 4385.98ms (99%)</code>
<code>[ 50s] threads: 1024, tps: 395.70, reads</code><code>/s</code><code>: 6498.99, writes</code><code>/s</code><code>: 1849.00, response </code><code>time</code><code>: 3845.88ms (99%)</code>
<code> </code><code>.</code>
<code>[3560s] threads: 1024, tps: 385.80, reads</code><code>/s</code><code>: 4949.60, writes</code><code>/s</code><code>: 1503.80, response </code><code>time</code><code>: 19827.73ms (99%)</code>
<code>[3570s] threads: 1024, tps: 249.70, reads</code><code>/s</code><code>: 3679.90, writes</code><code>/s</code><code>: 1009.40, response </code><code>time</code><code>: 12016.58ms (99%)</code>
<code>[3580s] threads: 1024, tps: 328.90, reads</code><code>/s</code><code>: 4511.40, writes</code><code>/s</code><code>: 1301.40, response </code><code>time</code><code>: 7419.06ms (99%)</code>
<code>[3590s] threads: 1024, tps: 196.40, reads</code><code>/s</code><code>: 3058.90, writes</code><code>/s</code><code>: 815.30, response </code><code>time</code><code>: 12092.35ms (99%)</code>
<code>[3600s] threads: 1024, tps: 386.60, reads</code><code>/s</code><code>: 5282.74, writes</code><code>/s</code><code>: 1537.78, response </code><code>time</code><code>: 13614.18ms (99%)</code>
<code>OLTP </code><code>test</code> <code>statistics:</code>
<code> </code><code>queries performed:</code>
<code> </code><code>read</code><code>: 16913554 -- 讀總數</code>
<code> </code><code>write: 4832444 -- 寫總數</code>
<code> </code><code>other: 2416222 -- 其他操作總數(SELECT、INSERT、UPDATE、DELETE之外的操作,例如COMMIT等)</code>
<code> </code><code>total: 24162220 -- 全部總數</code>
<code> </code><code>transactions: 1208111 (335.29 per sec.) -- 總事務數(每秒事務數)</code>
<code> </code><code>deadlocks: 0 (0.00 per sec.) -- 發生死鎖總數</code>
<code> </code><code>read</code><code>/write</code> <code>requests: 21745998 (6035.29 per sec.) -- 讀寫總數(每秒讀寫次數)</code>
<code> </code><code>other operations: 2416222 (670.59 per sec.) -- 其他操作總數(每秒其他操作次數)</code>
<code>General statistics:</code>
<code> </code><code>total </code><code>time</code><code>: 3603.1388s -- 總耗時</code>
<code> </code><code>total number of events: 1208111 -- 共發生多少事務數</code>
<code> </code><code>total </code><code>time</code> <code>taken by event execution: 3688348.3797s -- 所有事務耗時相加(不考慮并行因素)</code>
<code> </code><code>response </code><code>time</code><code>:</code>
<code> </code><code>min: 28.41ms -- 最小耗時</code>
<code> </code><code>avg: 3052.99ms -- 平均耗時</code>
<code> </code><code>max: 48667.93ms -- 最長耗時</code>
<code> </code><code>approx. 99 percentile: 12708.40ms -- 超過99%平均耗時</code>
<code>Threads fairness:</code>
<code> </code><code>events (avg</code><code>/stddev</code><code>): 1179.7959</code><code>/29</code><code>.07</code>
<code> </code><code>execution </code><code>time</code> <code>(avg</code><code>/stddev</code><code>): 3601.9027</code><code>/1</code><code>.01</code>
是以,每秒事務數Tps達335.29,每秒查詢數Qps達6035.29,平均延時3052.99ms.
看完解析,來看結果,下面是直接測主從環境的結果:
<code> </code><code>read</code><code>: 19215238</code>
<code> </code><code>write: 5490068</code>
<code> </code><code>other: 2745034</code>
<code> </code><code>total: 27450340</code>
<code> </code><code>transactions: 1372517 (381.03 per sec.)</code>
<code> </code><code>deadlocks: 0 (0.00 per sec.)</code>
<code> </code><code>read</code><code>/write</code> <code>requests: 24705306 (6858.48 per sec.)</code>
<code> </code><code>other operations: 2745034 (762.05 per sec.)</code>
<code> </code><code>total </code><code>time</code><code>: 3602.1538s</code>
<code> </code><code>total number of events: 1372517</code>
<code> </code><code>total </code><code>time</code> <code>taken by event execution: 3688254.2686s</code>
<code> </code><code>min: 18.33ms</code>
<code> </code><code>avg: 2687.22ms</code>
<code> </code><code>max: 55386.15ms</code>
<code> </code><code>approx. 99 percentile: 12444.89ms</code>
<code> </code><code>events (avg</code><code>/stddev</code><code>): 1340.3486</code><code>/33</code><code>.85</code>
<code> </code><code>execution </code><code>time</code> <code>(avg</code><code>/stddev</code><code>): 3601.8108</code><code>/0</code><code>.44</code>
每秒事務數Tps達381.03,每秒查詢數Qps達6858.48,平均耗時2687.22ms,畢竟總資料量是6億,還是應該要接受.
下面是通過mycat代理的結果
<code> </code><code>read</code><code>: 18078326</code>
<code> </code><code>write: 5165236</code>
<code> </code><code>other: 2582618</code>
<code> </code><code>total: 25826180</code>
<code> </code><code>transactions: 1291309 (358.40 per sec.)</code>
<code> </code><code>read</code><code>/write</code> <code>requests: 23243562 (6451.19 per sec.)</code>
<code> </code><code>other operations: 2582618 (716.80 per sec.)</code>
<code> </code><code>total </code><code>time</code><code>: 3602.9883s</code>
<code> </code><code>total number of events: 1291309</code>
<code> </code><code>total </code><code>time</code> <code>taken by event execution: 3687715.5739s</code>
<code> </code><code>min: 22.45ms</code>
<code> </code><code>avg: 2855.80ms</code>
<code> </code><code>max: 50326.08ms</code>
<code> </code><code>approx. 99 percentile: 13264.21ms</code>
<code> </code><code>events (avg</code><code>/stddev</code><code>): 1261.0439</code><code>/33</code><code>.56</code>
<code> </code><code>execution </code><code>time</code> <code>(avg</code><code>/stddev</code><code>): 3601.2847</code><code>/0</code><code>.96</code>
每秒事務數Tps達358.40,每秒查詢數Qps達6451.19,平均耗時2855.80ms,同樣是總資料量6億.
從結果對比計算,使用mycat後,tps,qps,和耗時都損耗了6%-7%,在我個人看來還是可以接受,因為使用了mycat做代理層,可以很友善的管理後端資料庫,任何切換都可以手動來秒切,使用上觸發腳本後就是一個HA架構了.
題外說明
測試結果示例說明的例子,其實是加上了高可用keepalived的測試結果,每秒事務數Tps達335.29,每秒查詢數Qps達6035.29,平均延時3052.99ms,總的來說算好,也還隻是比純mysql主從損耗10%範圍内,可以接受,畢竟可用性高了很多,而且後續壓力增大也可以随時增加mycat數量來填補.
功能多了,機器也要多了一些,需要特别說明的是,因為涉及網絡資料包轉發關系,keepalived和後端mycat不能在同一台伺服器,是以就必須獨立開來.
10.21.128.208:keepalived主
10.21.128.209:keepalived備
10.21.128.200:mycat1
10.21.128.201:mycat2
10.21.128.199:vip
需要多搭建一個mycat,不過這裡就不細說了,直接複制一份配置到其他機器再啟動就ok了.
然後搭建一套keepalived叢集:
大多數情況下,大夥使用keepalived隻做HA功能,而LVS功能則交給其他軟體實作.但是實際上keepalived+ipvsadm既能實作HA功能,也能實作LVS功能,非常友善.
我們在10.21.128.208和10.21.128.209上安裝keepalived和修改配置.
個人不想糾結功能和版本問題,而這些也是比較常态功能性的軟體,絕大部分yum源都配備,是以直接用yum安裝就很友善,有額外興趣的可以慢慢編譯
<code>#安裝需要的軟體</code>
<code>yum </code><code>install</code> <code>-y keepalived ipvsadm nc </code><code>rsync</code> <code>telnet tcpdump wget</code>
安裝很快,我們直接看配置,yum安裝預設的配置檔案在/etc/keepalived/
<code>#修改配置檔案,注意注釋位置</code>
<code>! Configuration File </code><code>for</code> <code>keepalived</code>
<code>#全局配置</code>
<code>global_defs {</code>
<code> </code><code>notification_email {</code>
<code> </code><code>root@localhost </code><code>#定義收件人郵件位址</code>
<code> </code><code>}</code>
<code> </code><code>notification_email_from root@localhost </code><code>#定義發件人</code>
<code> </code><code>smtp_server 127.0.0.1 </code><code>#如果要使用第三方smtp伺服器,在現實中幾乎沒有意義(需要驗證的原因),設為本地就可以了</code>
<code> </code><code>smtp_connect_timeout 30 </code><code>#smtp逾時時間</code>
<code> </code><code>router_id LVS_HA_MYCAT1 </code><code>#此伺服器keepalived的ID,随便改,注意不同伺服器不一樣就行</code>
<code>}</code>
<code>#vrrp配置(HA配置)</code>
<code>vrrp_instance VI_1 { </code><code>#定義虛拟路由,VI_1 為虛拟路由的标示符,自己定義名稱</code>
<code> </code><code>state MASTER </code><code>#指定目前節點為主節點 備用節點上設定為BACKUP即可 </code>
<code> </code><code>interface eth0 </code><code>#綁定虛拟IP的網絡接口,注意内外網</code>
<code> </code><code>virtual_router_id 19 </code><code>#VRRP組名,兩個節點的設定必須一樣,以指明各個節點屬于同一VRRP組,0-255随便你用,這個ID也是虛拟MAC最後一段的來源</code>
<code> </code><code>priority 100 </code><code>#初始優先級,取值1-254之間,主節點一定要最大,其他從節點則看情況減少</code>
<code> </code><code>advert_int 1 </code><code>#多點傳播資訊發送間隔,兩個節點設定必須一樣</code>
<code> </code><code>authentication { </code><code>#設定驗證資訊,兩個節點必須一緻</code>
<code> </code><code>auth_type PASS</code>
<code> </code><code>auth_pass 199200</code>
<code> </code><code>}</code>
<code> </code><code>virtual_ipaddress {</code>
<code> </code><code>10.21.128.199 </code><code>#指定VIP,兩個節點設定必須一樣,虛拟ip最好和真實ip在同一網段。</code>
<code>#負載均衡配置(LVS配置)</code>
<code>virtual_server 10.21.128.199 8066 { </code><code>#指定VIP和端口,vip就是上面設定那個</code>
<code> </code><code>delay_loop 6 </code><code>#延遲多少個周期再啟動服務,做服務檢測</code>
<code> </code><code>lb_algo rr </code><code>#負載均衡排程算法</code>
<code> </code><code>lb_kind DR </code><code>#負載均衡類型選擇,可選DR|NAT|TUN,DR性能比較高</code>
<code> </code><code>nat_mask 255.255.255.0 </code><code>#vip的掩碼</code>
<code> </code><code>persistence_timeout 0 </code><code>#會話保持時間,一定時間之内使用者無響應則下一次使用者請求時需重新路由,一般設為0,不需要.</code>
<code> </code><code>protocol TCP </code><code>#使用的協定,一般就TCP</code>
<code> </code><code>real_server 10.21.128.200 8066 { </code><code>#定義後端realserver的真實伺服器屬性,ip和端口</code>
<code> </code><code>weight 1 </code><code>#負載均衡權重,數值越大,就負擔更多連接配接</code>
<code> </code><code>MISC_CHECK { </code><code>#定義心跳檢測的方法,因為不是web,而且用tcp_check健康檢測後面的mycat會報錯,是以需要misc_check的方式做心跳檢測</code>
<code> </code><code>misc_path</code><code>"/etc/keepalived/shell/check_mycat_status.sh 10.21.128.200 8066"</code> <code>#自定義心跳檢測shell腳本的路徑、檢測的伺服器ip、檢測的端口。(引号必須要)</code>
<code> </code><code>misc_timeout 3 </code><code>#腳本執行逾時時間</code>
<code> </code><code>}</code>
<code> </code><code>real_server 10.21.128.201 8066 { </code><code>#同上</code>
<code> </code><code>weight 2</code>
<code> </code><code>MISC_CHECK {</code>
<code> </code><code>misc_path</code><code>"/etc/keepalived/shell/check_mycat_status.sh 10.21.128.201 8066"</code>
<code> </code><code>misc_timeout 3</code>
<code> </code><code>real_server 10.21.128.156 8066 { </code><code>#同上</code>
<code> </code><code>misc_path</code><code>"/etc/keepalived/shell/check_mycat_status.sh 10.21.128.156 8066"</code>
裡面涉及一個檢測心跳的腳本,如下所示:
<code>cat</code> <code>/etc/keepalived/shell/check_mycat_status</code><code>.sh</code>
<code>#!/bin/bash</code>
<code>result=`nc -</code><code>v</code> <code>-z $1 $2`</code>
<code>flag=</code><code>"succeeded"</code>
<code>if</code> <code>[[ $result =~ $flag ]]</code>
<code>then</code>
<code> </code><code>exit</code> <code>0</code>
<code>else</code><code>;</code>
<code> </code><code>exit</code> <code>1</code>
<code>fi</code>
<code>#記得要給該腳本賦予執行權限:</code>
<code>chmod</code> <code>755 check_mycat_status.sh</code>
意思很簡單,就是通過nc指令檢測真實伺服器的mycat的端口通不通,如果不通的話,keepalived就把這個mycat剔除出叢集.
然後,在後端真實伺服器上需要做一個操作,綁定vip建立ipvs規則:
在10.21.128.200,10.21.128.201,10.21.128.156上建立并執行下面的腳本,
<code>#建立規則腳本</code>
<code>vim </code><code>/shell/realserver</code>
<code>SNS_VIP=10.21.128.199</code>
<code>#/etc/rc.d/init.d/functions</code>
<code>case</code> <code>"$1"</code> <code>in</code>
<code>start)</code>
<code> </code><code>ifconfig</code> <code>lo:0 $SNS_VIP broadcast $SNS_VIP netmask 255.255.255.255 up</code>
<code> </code><code>sleep</code> <code>5</code>
<code> </code><code>/sbin/route</code> <code>add ${SNS_VIP}</code><code>/32</code> <code>dev lo:0</code>
<code> </code><code>echo</code> <code>"1"</code><code>></code><code>/proc/sys/net/ipv4/conf/lo/arp_ignore</code>
<code> </code><code>echo</code> <code>"2"</code><code>></code><code>/proc/sys/net/ipv4/conf/lo/arp_announce</code>
<code> </code><code>echo</code> <code>"1"</code> <code>></code><code>/proc/sys/net/ipv4/conf/all/arp_ignore</code>
<code> </code><code>echo</code> <code>"2"</code><code>></code><code>/proc/sys/net/ipv4/conf/all/arp_announce</code>
<code> </code><code>sysctl -p ></code><code>/dev/null</code> <code>2>&1</code>
<code> </code><code>echo</code> <code>"RealServer Start OK"</code>
<code>;;</code>
<code>stop)</code>
<code> </code><code>ifconfig</code> <code>lo:0 down</code>
<code> </code><code>/sbin/route</code> <code>del -net $SNS_VIP netmask 255.255.255.255 dev lo:0</code>
<code> </code><code>echo</code> <code>"0"</code> <code>></code><code>/proc/sys/net/ipv4/conf/lo/arp_ignore</code>
<code> </code><code>echo</code> <code>"0"</code><code>></code><code>/proc/sys/net/ipv4/conf/lo/arp_announce</code>
<code> </code><code>echo</code> <code>"0"</code><code>></code><code>/proc/sys/net/ipv4/conf/all/arp_ignore</code>
<code> </code><code>echo</code> <code>"0"</code><code>></code><code>/proc/sys/net/ipv4/conf/all/arp_announce</code>
<code> </code><code>echo</code> <code>"RealServer Stoped"</code>
<code>restart)</code>
<code>/sbin/route</code> <code>del -net $SNS_VIP netmask 255.255.255.255 dev lo:0</code>
<code>/sbin/route</code> <code>add ${SNS_VIP}</code><code>/32</code> <code>dev lo:0</code>
<code>*)</code>
<code> </code><code>echo</code> <code>"Usage: $0 {start|stop|restart}"</code>
<code> </code><code>exit</code> <code>1</code>
<code>esac</code>
<code>exit</code> <code>0</code>
<code>#賦予執行權限</code>
<code>chmod</code> <code>755 </code><code>/shell/realserver</code>
<code>#執行一下</code>
<code>/shell/realserver</code> <code>start</code>
<code>#然後檢視下網卡狀态</code>
<code>ifconfig</code> <code>lo:0</code>
<code>lo:0 Link encap:Local Loopback </code>
<code> </code><code>inet addr:10.21.128.199 Mask:255.255.255.255</code>
<code> </code><code>UP LOOPBACK RUNNING MTU:16436 Metric:1</code>
需要注意,每次伺服器重新開機都必須啟動這個綁定,不然lvs就不生效了,例如把他放到/etc/rc.d/rc.local
<code>echo</code> <code>"/shell/realserver start"</code> <code>>></code><code>/etc/rc</code><code>.d</code><code>/rc</code><code>.</code><code>local</code>
萬事俱備,隻欠東風,那就啟動keepalived吧.
在10.21.128.208和10.21.128.209上執行
<code>#因為是yum安裝,是以有linux服務</code>
<code>/etc/init</code><code>.d</code><code>/keepalived</code> <code>start</code>
<code>Starting keepalived: [ OK ]</code>
<code>#看看vip,因為主是208,是以vip隻會在208出現,除非208挂了</code>
<code>ip addr </code>
<code>1: lo: <LOOPBACK,UP,LOWER_UP> mtu 16436 qdisc noqueue state UNKNOWN </code>
<code> </code><code>link</code><code>/loopback</code> <code>00:00:00:00:00:00 brd 00:00:00:00:00:00</code>
<code> </code><code>inet 127.0.0.1</code><code>/8</code> <code>scope host lo</code>
<code> </code><code>inet6 ::1</code><code>/128</code> <code>scope host </code>
<code> </code><code>valid_lft forever preferred_lft forever</code>
<code>2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000</code>
<code> </code><code>link</code><code>/ether</code> <code>22:87:35:77:ef:38 brd ff:ff:ff:ff:ff:ff</code>
<code> </code><code>inet 10.21.128.208</code><code>/24</code> <code>brd 10.21.128.255 scope global eth0</code>
<code> </code><code>inet 10.21.128.199</code><code>/32</code> <code>scope global eth0 </code><code>#-------VIP在這</code>
<code> </code><code>inet6 fe80::2087:35ff:fe77:ef38</code><code>/64</code> <code>scope link </code>
<code>#然後看看目前LVS狀态</code>
<code>ipvsadm -L</code>
<code>IP Virtual Server version 1.2.1 (size=4096)</code>
<code>Prot LocalAddress:Port Scheduler Flags</code>
<code> </code><code>-> RemoteAddress:Port Forward Weight ActiveConn InActConn</code>
<code>TCP 10.21.128.199:8066 rr</code>
<code> </code><code>-> 10.21.128.156:8066 Route 2 0 0 </code>
<code> </code><code>-> 10.21.128.200:8066 Route 1 0 0 </code>
<code> </code><code>-> 10.21.128.201:8066 Route 2 0 0</code>
一切都正常,那麼,我們可以壓測了.
<code>#和之前指令差不多,隻是ip不一樣</code>
<code>.</code><code>/sysbench</code> <code>--mysql-host=10.21.128.199 --mysql-port=8066 --mysql-user=sysbench --mysql-password=sb123 --</code><code>test</code><code>=tests</code><code>/db/oltp</code><code>.lua --oltp_tables_count=15 --oltp-table-size=40000000 --mysql-table-engine=innodb --num-threads=1024 --oltp-</code><code>read</code><code>-only=off --report-interval=10 --rand-</code><code>type</code><code>=uniform --max-</code><code>time</code><code>=3600 --max-requests=40000000 --percentile=99 run >> </code><code>/home/logs/sysbench_oltpX_32_20171128-3</code><code>.log</code>
我們來看看lvs狀态
<code>#檢視連結狀态</code>
<code> </code><code>-> 10.21.128.156:8066 Route 2 341 340 </code>
<code> </code><code>-> 10.21.128.200:8066 Route 1 341 340 </code>
<code> </code><code>-> 10.21.128.201:8066 Route 2 342 340 </code>
<code>#加上另一個參數</code>
<code>ipvsadm -L --stats</code>
<code>Prot LocalAddress:Port Conns InPkts OutPkts InBytes OutBytes</code>
<code> </code><code>-> RemoteAddress:Port</code>
<code>TCP 10.21.128.199:8066 2045 3658545 0 326481K 0</code>
<code> </code><code>-> 10.21.128.156:8066 682 1131691 0 114278K 0</code>
<code> </code><code>-> 10.21.128.200:8066 681 1266405 0 106344K 0</code>
<code> </code><code>-> 10.21.128.201:8066 682 1260449 0 105858K 0</code>
為什麼會沒有out?是不是很奇怪?因為我們負載均衡類型選擇的是DR模式,這個模式的特點就是當用戶端和真實伺服器建立連結後,真實伺服器會直接把資料發送給用戶端,不再需要keepalived來中轉,是以就沒有out的流量了,也就是為什麼說效率就更高的原因了.
然後來看看壓測的結果如何?
<code> </code><code>read</code><code>: 17043978</code>
<code> </code><code>write: 4869708</code>
<code> </code><code>other: 2434854</code>
<code> </code><code>total: 24348540</code>
<code> </code><code>transactions: 1217427 (338.00 per sec.)</code>
<code> </code><code>read</code><code>/write</code> <code>requests: 21913686 (6084.05 per sec.)</code>
<code> </code><code>other operations: 2434854 (676.01 per sec.)</code>
<code> </code><code>total </code><code>time</code><code>: 3601.8265s</code>
<code> </code><code>total number of events: 1217427</code>
<code> </code><code>total </code><code>time</code> <code>taken by event execution: 3687506.3802s</code>
<code> </code><code>min: 30.17ms</code>
<code> </code><code>avg: 3028.93ms</code>
<code> </code><code>max: 75102.41ms</code>
<code> </code><code>approx. 99 percentile: 13757.56ms</code>
<code> </code><code>events (avg</code><code>/stddev</code><code>): 1188.8936</code><code>/33</code><code>.40</code>
<code> </code><code>execution </code><code>time</code> <code>(avg</code><code>/stddev</code><code>): 3601.0804</code><code>/0</code><code>.40</code>
每秒事務數Tps達338.00,每秒查詢數Qps達6084.05,平均延時3028.93ms,和一開始相差無幾,基本符合現實.
本文轉自arthur376 51CTO部落格,原文連結:http://blog.51cto.com/arthur376/2045596,如需轉載請自行聯系原作者