系统发布后一段时间,对系统留下的一些log,特别是所执行的sql语句进行分析,来获取用户使用习惯的第一手数据,以便以后我们的改进。
今天就做了这样一次简单的分析,由于系统中几乎所有的查询都使用我的一个统一的借口来进行查询,所以SQL的log有统一的标志,收集起来相对容易。
下面一步一步的描述一下整个过程:
一、搜集log
1. 从服务器上获取log文件,无需多说
2. 用程序分析log,并将sql解析出来,做一些处理,保存到数据库中。
要保存一条数据到数据库里,需要配置一个保存的SQL:
<parameter-map name="insert-params"> <property name="exeTime" /> <property name="sql"/> <property name="parsedSql"/> <property name="sqlvalues"/> </parameter-map> <mapped-statement name="insertSql" parameter-map="insert-params" > insert into SQL_STMT ( ID, EXE_TIME, SQL, PARSED_SQL, SQL_valueS) values ( <!--注意这里,可以使用数据库本身的功能,不受限制--> seq_sql_stmt.nextval, ?, ?, ?,? ) </mapped-statement> |
程序里只需要提供一个简单的bean,这个bean只需要带有"insert-params"配置的那几个field就可以了,在程序里我们只需要:
SqlLog log = parseSqlLog(sqlString);//set 相关字段 SqlMapConfig.getSqlMap().executeUpdate("insertSql", log); |
这样就保存好了,简单吧。
3. 用程序跑,把所有log分析完,都写到数据库里去
三、分析LOG
也许保存数据上,看不出多少和其他ORM工具的差别,甚至还会觉得更麻烦一点,下面用一个查询的例子来说明iBATIS的灵活与强大。
为了分析这些log,我在往数据库里写的时候,把sql里的值都去掉了,替换成prepared类似的sql,以便于分析sql的普遍性。在这里提一句题外话,在设计DAO框架的时候,最好考虑以后可能的扩展,使用比较好的框架是很重要的,就象这里搜集log,由于我们使用了统一的接口,不仅log搜集容易,对于以后的扩展与修改也很重要,比如现在我就在修改我们的一个UTIL,让所有的sql都用绑定的方式执行,如果没有以前好的设计,现在要修改几乎是不可能的!
log分析出来大概有80多万记录,我们需要从中获取一些有用的信息,就必须提供一个简单的查询。经过简单的了解后,我们大致需要几个条件:执行时间,sql语句(模糊查询),统计的条数。
于是,就开始写配置文件:
[code]
<dynamic-mapped-statement name="getSqlLogStatistics" cache-model="sqllog-cache"
result-map="sqllog-hashmap-result" >
SELECT * FROM (SELECT ROWNUM count_row_num, w_o_l_f_w.* FROM (
select * from (
select PARSED_SQL,COUNT(*) CNT from SQL_STMT
<dynamic prepend="WHERE">
<isNotNull prepend="AND" property="exetimestart">
<![CDATA[EXE_TIME >= #exetimestart#]]>
</isNotNull>
<isNotNull prepend="AND" property="exetimeend">
<![CDATA[ EXE_TIME <= #exetimeend# ]]>
</isNotNull>
<isNotEmpty prepend="AND" property="sql">
(SQL like '%'||#sql#||'%' or PARSED_SQL like '%'||#sql#||'%' )
</isNotEmpty>
</dynamic>
<dynamic prepend="HAVING">
<isNotEmpty property="countfrom">
<![CDATA[ COUNT(*) >= #countfrom# ]]>
</isNotEmpty>
<isNotEmpty prepend="AND" property="countto">
<![CDATA[ COUNT(*) <= #countto# ]]>
</isNotEmpty>
</dynamic>
GROUP by PARSED_SQL
<![CDATA[
) order by cnt desc ) w_o_l_f_w
WHERE ROWNUM < #__EndPoint#)
WHERE (count_row_num >= #__StartPoint#)
]]>
</dynamic-mapped-statement>
[/code]
以上的就是一个动态的sql,看起来可能比较难看,但是他管用。
这样,程序里就简单了,在action里,我们把提交的数据简单的处理一下:
[code]
ParameterParser pp = data.getParameters();
Map params = new HashMap(pp.getKeys().length-2);
Object[] keys = pp.getKeys();
for(int i=0;i<keys.length;i++){
String key = (String) keys[i];
if(!"template".equals(key) && !"action".equals(key)){
Date tmp = TemplateUtil.getDateFromPicker(pp,key);
if(tmp!=null){
params.put(key,tmp);
}else{
params.put(key,pp.getString(key).trim());
}
}
}
[/code]
这段代码简单的处理了提交的数据,把他从Parameters里拿出来放到一个Map里,
做为我们查询的条件,在我们的bean里,只需要:
return SqlMapConfig.getSqlMap().executeQueryForList( "getSqlLogStatistics", params); |
很简单吧?
其实,就开发上来说,我们只是把一些以前要在java程序里写的逻辑,比如某个字段是不是有值啊什么的放到了sqlmap的xml里去,工作量并没有减少多少,但是,修改和调整的时候,工作量就大大的减少了,我们不需要去修改程序,编译,发布,再运行,我们只需要简单的修改sqlmap的的xml就可以做到这一点,对于项目后期的调整(比如dba要将sql进行tuning),项目发布后的维护等等,可以说是大大的减轻了工作量。
四、后记
这只是一个非常简单应用,简单的使用了iBATIS 的 sqlmap功能,我想我们以后可以考虑在项目中使用它,但是任何一个工具都有他最适用的地方,我的意见是以后的添加修改和删除还是可以继续使用一些ORM工具,至于查询么,单表或者简单的有规律的查询可以使用代码生成工具中生出来,对于十分复杂,而且用户又可能经常修改或者说很有可能需要在后期进行性能调整的查询,我们可以考虑使用iBATIS.