一、簡介
自動化實施的過程中,我們通常都面臨一個棘手的問題:數據的準備和恢復。即在成功執行一個自動化用例時,我們可能需要一定的數據前提,而為了使得整個前提不至于被其他的用例破壞,以至于我們有時不得不在自動化用例編寫過程中手工的添加許多Insert、Update、Delete語句來保證數據被恢復到我們執行用例前的狀態。?
使用phpunit等工具框架進行php自動化單元測試的過程中也同樣面臨數據準備和恢復的問題。單元測試開發人員開發過程中不得不編碼中顯示編寫數據準備,驗證,恢復的代碼,這樣會帶來以下幾個缺點:
1、用例閱讀性降低,難于辨別主要的測試邏輯?
2、用例可維護性降低,當數據庫的表結構略微有變化或者業務底層邏輯有變動,數據的準備和恢復的代碼都將有可能面臨修改甚至重寫?
3、極大消耗開發人員開發成本,用例編寫過程中不得不額外花很多的時間去思考數據的準備和恢復。
基于以上三點,考慮設計實現一個mysql server及client的中間層,通過此中間層,可以將開發人員的數據恢復操作透明化,且在數據環境穩定的前提下,適度的降低開發人員的數據準備工作。?
基于Hunter擴展,將來可能帶來:?
1、自定義Mysql樁(模擬、修改結果集)?
2、實時監控Query(已經實現,目前可以監控query性能)?
3、性能測試輔助,集成query性能瓶頸分析反饋
二、Amoeba和Hunter的角色
Amoeba for mysql位于Application與數據庫之間,為應用提供透明的代理服務。Amoeba對外接口采用 mysql protocol,沒有定義自己的協議和接口,使得任何mysql的應用都能方便的使用Amoeba,諸如php、java、c等等的cennector。Amoeba本身沒有Build 任何 SQL語句,而是直接接收client的SQL、命令等進行代理轉發,Amoeba采用 SQL、命令等 mysql protocol規范作為統一的接口,完備性得到了保證,能夠完成mysql的所有功能操作。
三、 安裝
Mysql Hunter不需要編譯,僅需要Java運行環境(JRE1.5以上)。
先決條件 :
1、Java SE 1.5或以上Amoeba 框架是基于JDK1.5開發的,采用了JDK1.5的特性 支持Mysql協議版本10(mysql 4.1以后的版本)?
2、網絡環境至少運行有一個mysql 4.1以上的服務?
操作步驟 :
1、 設置環境變量,在~/.bashrc中加入JAVA_HOME=/JRE的安裝路徑 /?
2、 將程序目錄解壓或拷貝出來,直接運行mysql-hunter/bin/amoeba即可啟動?
參考參數配置進行配置后啟動amoeba,此時proxy應連接到Mysql Server,同時對于client而言,mysql servers對于client是透明的,client認為proxy為mysql服務器直接連接proxy的ip和端口即可正常操作數據庫。
四、 功能介紹
4.1 Amoeba for mysql
Mysql-hunter是基于amoeba框架做的mysql代理擴展如圖。
amoeba for mysql本身實現的功能包括:
讀寫/分離;
失敗重連 ;
負載均衡 ;
連接池 ;
身份認證
4.2 Mysql hunter擴展
Mysql-Hunter的功能則主要在于SQL/SQL Result的inject、rewrite以及reverse,如圖:?
從上圖可以看出,mysql hunter可以實現如下功能:
Client連接server的階段進行自定義命令發送或返回?
Client發送query cmd至server的階段,進行query截取,修改?
同理,Server返回給Client的結果集也能被hunter監控,修改甚至重寫?
特點:使用起來很簡單,只需要設置start點和end點,那么兩者之間所有執行的sql query都可以被捕獲、并生成逆命令
?
4.3 基本擴展BasicMysqlHunter
BasicMysqlHunter?是一個基本且實用的擴展實現,繼承自Hunter類,實現了Hunter的抽象方法 ,可以在client發送cmd至server前對query進行截獲處理,也可以對server返回的結果集進行修改。?
BasicMysqlHunter?下的basic handlers這里不再介紹,詳細請參考《Mysql Hunter總體設計》,BasicMysqlHunter目前定位于對DML query進行如下操作:?
分析識別?
逆向語句及序列生成?
逆向語句的回放,即顯示命令恢復數據庫數據
舉例說明?
Client通過mysql hunter向server發送了一條query:?
INSERT INTO tblLemma SET title = ‘北京’, content = ‘我愛北京天安門’;?
此時tblLemma表的自增字段ID變為了100?
Mysql Hunter將分析識別這條語句,最終生成逆向的恢復語句序列:?
Stack = (
‘DELETE FROM tblLemma WHERE title = ‘北京’ AND content = ‘我愛北京天安門’ AND ID = 100;’,
‘ALTER TABLE tblLemma SET AUTO_INCREMENT = 99;’,
)
通過順序執行stack中的語句,數據庫中的數據就將恢復到insert命令之前的狀態。
4.4 Sql檢查擴展SqlCheckHunter
繼承自Hunter類,實現了Hunter的抽象方法,用來截獲SQL語句,并對sql語句進行分析,最終給出分析報告。?
主要用于Sql query的性能分析,極大省去人力的比對分析,配置簡單,對功能測試透明。?
支持SELECT、INSERT、UPDATE、DELETE語句的常規經驗性檢查。?
規則主要包含:?
mapIssueReason.put(SELECT_REASON_1, "使用的索引為Null,建議檢查SQL涉及表的索引字段是否合理");
mapIssueReason.put(SELECT_REASON_2, "Filesort,所使用的索引對排序支持不好,如果檢索排序結果集大建議增強索引");
mapIssueReason.put(SELECT_REASON_3, "查詢結果集可能對應超過50條記錄,而SQL非翻頁查詢語句");
mapIssueReason.put(SELECT_REASON_4, "備選索引數大于1,可以考慮采用force index優化");
mapIssueReason.put(INSERT_REASON_1, "連續采用單語句插入相同的表大于100次,建議考慮:1. 寫合并;2. 采用LOAD DATA INFILE;3. 調節bulk_insert_buffer_size變量");
mapIssueReason.put(INSERT_REASON_2, "INSERT...SELECT...語句,請查看該語句的SELECT性能是否可接受");
mapIssueReason.put(UPDATE_REASON_1, "UPDATE語句,未包含where條件字段,進行全表更新操作效率低");
mapIssueReason.put(UPDATE_REASON_2, "UPDATE語句,WHERE字句不能使用索引找到被更新記錄或修改范圍過大");
mapIssueReason.put(UPDATE_REASON_3, "UPDATE語句連續更新相同表大于100次,建議考慮:1. 寫合并;2. 如果有索引,索引更新可能過于頻繁,適當修改縮減索引");
mapIssueReason.put(DELETE_REASON_1, "DELETE語句刪除全表,建議使用truncate語句");
mapIssueReason.put(DELETE_REASON_2, "DELETE語句,WHERE字句不能使用索引找到被更新記錄或修改范圍過大");
mapIssueReason.put(DELETE_REASON_3, "DELETE語句連續更新相同表大于100次");
4.5 參數配置
下面介紹conf文件中所有的配置項。
amoeba.xml :
1. <server> //Amoeba 代理server,即proxy server的配置
/* port: 綁定端口
* ipAddress: 綁定ip
* readThreadPoolSize: 網絡IO最大線程數
* user: amoeba 對外驗證所需用戶名
* password: amoeba 對外驗證所需密碼
*/
2. <connectionManagerList> 所使用的連接管理類,通常不需要修改
3. <dbServerList> 后端所連接的數據庫服務器配置,包括主從所有的DB服務器
* <dbServer>
/* port: 真實mysql數據庫端口
* ipAddress: 真實mysql數據庫ip
* schema: 默認的數據庫schema
* user: 連接該db所需的用戶名
* password: 連接該db所需的相應用戶的密碼
*/
* <poolConfig>: 對象池,通常不需要修改
4. <dbServer name="multiPool" virtual="true"> 設定參與負載均衡的服務器
/* loadbalance: 負載均衡設定:1為輪詢 2為權重
* poolNames: 以逗號分隔如,server1,server2,server3…
*/
5. <queryRouter> 所用的query分發路由相關配置
/* writePool: 寫操作池包含的數據庫服務器
* readPool: 讀操作池包含的數據庫服務器
* needParse: 是否需要進行query parse
*/
高亮的配置項是重新部署DB環境的時候通常需要關注的配置項。<br>
// 擴展配置
<extension>
<hunter class="com.meidusa.amoeba.extend.BasicMySqlHunter"></hunter>
</extension>
設定使用的hunter擴展是BasicMysqlHunter。
?
log4j.xml?
日志打印的配置,通常不需要修改。如果需要額外的增加日志格式,或者修改日志格式,級別可以參考log4j的文檔,或者參考本配置文件的其他配置方法。
BasicMysqlHunter?目前提供三條自定制命令:?
1 select start:開始啟用擴展,擴展將開始監控select start執行之后流經Hunter的DML命令并求逆;Hunter直接返回Ok packet?
2 select print_stack:打印當前Hunter上下文中的逆命令堆棧?
3 select end:結束擴展監控,并將Hunter上下文中的逆向命令棧依順序pop出棧并發送Server恢復DB數據;Hunter直接返回Ok packet
$link = mysql_connect('127.0.0.1:3307', 'mysql_user', 'mysql_password');
if (!$link) {
die('Could not connect: ' . mysql_error());
}
// Case執行前
$result = mysql_query("select start");
/*
* Case 執行
* 執行過程中可以自身或用其他客戶端發送select print_stack命令check當前逆命令棧
*/
// Case執行后
$result = mysql_query("select end");
// 此時Case執行過程中的所有DML操作將被還原
五、使用注意
5.1 Mysql Hunter不支持的操作
Mysql Hunter(確切的說是BasicMysqlHunter)已經支持了絕大部分常用的DML語句,目前已知的不支持的query類型包括:?
DML語句的多表操作,例如一個query中同時修改update表A和表B的字段,暫時沒有實現逆語句生成,僅能轉發?
未實現UPDATE語句中包含order by limit的語句的逆語句生成,僅能轉發?
暫不支持改變表結構和用戶屬性權限的語句,ALTER TABLE、TRUNCATE TABLE、CREATE TABLE、DROP TABLE、GRANT、ALTER USER等,僅能轉發?
不支持非DML語句,僅能轉發?
內置函數支持有限,根據實際應用過程中的使用情況增加對內置函數的擴展并求逆(目前已支持now())
5.2 Mysql Hunter日志使用
Mysql Hunter運行時日志記錄在:?
log/hunter.log 正常日志?
log/hunter.log.wf 錯誤日志?
正常日志會打印所有流經proxy層的query cmds,以及hunter的關鍵執行步驟的log,通常不應出現錯誤日志,如果出現了錯誤日志有兩種情況:?
hunter不支持的操作?
hunter自身的bug?
利用hunter.log有時可以輔助我們定位問題,甚至可以拋棄mysql的query.log
(作者:weichenxi)
?
【本文轉自百度測試技術空間】http://hi.baidu.com/baiduqa/blog/item/69fc0553d19d8f9b8c543004.html