一、简介
自动化实施的过程中,我们通常都面临一个棘手的问题:数据的准备和恢复。即在成功执行一个自动化用例时,我们可能需要一定的数据前提,而为了使得整个前提不至于被其他的用例破坏,以至于我们有时不得不在自动化用例编写过程中手工的添加许多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