一、前提:
在原有的项目中做迭代,发现一个sql执行效率特别慢,总费事16秒时间,触发了生产环境的报警机制。根据获取日志拿到mybatis的动态sql,在本地创建库表,执行效率不到1秒,执行效率并不慢;所以想着是否时sprinAop机制没有写好,导致执行的时候费时间。
排查了aop机制的代码,发现并不是这个问题,而是在记录打印log日志时,生成的log日志慢,因为前面开发人员是从度娘上找的(从网上可以找到),代码没有任何问题,但是当sql长度超过一定时,会存在用时过长问题,这是开发人员没有考虑到的,这里我将该处做了一些优化。
二、问题排查:
2.1、getSql 获取动态sql:
public static String getSql(Invocation invocation) {
MappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0];
Object parameter = "";
if (invocation.getArgs().length > 1) {
parameter = invocation.getArgs()[1];
}
BoundSql boundSql = mappedStatement.getBoundSql(parameter);
Configuration configuration = mappedStatement.getConfiguration();
Object parameterObject = boundSql.getParameterObject();
List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
String sql = boundSql.getSql().replaceAll("[\\s]+", " ");
if (parameterMappings.size() > 0 && parameterObject != null) {
TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry();
if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
sql = sql.replaceFirst("\\?", Matcher.quoteReplacement(getParameterValue(parameterObject)));
} else {
MetaObject metaObject = configuration.newMetaObject(parameterObject);
// begin : 程序慢的出处
for (ParameterMapping parameterMapping : parameterMappings) {
String propertyName = parameterMapping.getProperty();
if (metaObject.hasGetter(propertyName)) {
Object obj = metaObject.getValue(propertyName);
sql = sql.replaceFirst("\\?", Matcher.quoteReplacement(getParameterValue(obj)));
} else if (boundSql.hasAdditionalParameter(propertyName)) {
Object obj = boundSql.getAdditionalParameter(propertyName);
sql = sql.replaceFirst("\\?", Matcher.quoteReplacement(getParameterValue(obj)));
} else {
sql = sql.replaceFirst("\\?", "");
}
}
// end
}
}
return sql;
}
2.2、getParameterValue 对SQL进行格式化方法
private static String getParameterValue(Object obj) {
String value = null;
if (obj instanceof String) {
value = "'" + obj.toString() + "'";
} else if (obj instanceof Date) {
DateFormat formatter = DateFormat.getDateTimeInstance(DateFormat.DEFAULT, DateFormat.DEFAULT, Locale.CHINA);
value = "'" + formatter.format(obj) + "'";
} else {
return obj != null ? obj.toString() : "";
}
return value;
}
2.3、 单元测试
/**
* 批量插入数据花费时间测试
*/
@Test
public void insertBatchUser_test(){
List<User> userLists = new ArrayList<User>();
//总数
int count =600 ;
Date date = new Date();
for (int i = 0; i <count ; i++) {
User user = new User();
user.setCompanyId(67L);
user.setCompanyName("测试公司");
user.setOrgCode("GWDS");
user.setOrgName("国网典设");
user.setUserId(194382L);
user.setUserName("测试人");
user.setCode("110200199001012345");
user.setAge(32);
// ... 还有十几个就不展示了
userLists.add(user);
}
try {
System.out.println("开始:==========================================================");
long start = System.currentTimeMillis();
userDao.insertBatch(userLists);
long end = System.currentTimeMillis();
System.out.println("插入"+count+"条数据,总计花费"+(end-start)+"毫秒");
System.out.println("结束:==========================================================");
} catch (Exception e) {
e.printStackTrace();
}
}
2.4、 测试结果:
开始:==========================================================
插入600条数据,总计花费16675毫秒
结束:==========================================================
三、 优化的代码
使用数组下标赋值的方式替换
replaceFirst
方法实现的,实现代码的优化
public static String getSql(Invocation invocation) {
// ... 代码省略,同2.1
Object parameterObject = boundSql.getParameterObject();//获取参数
List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
String sql = boundSql.getSql().replaceAll("[\\s]+", " ");
if (parameterMappings.size() > 0 && parameterObject != null) {
TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry();
if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
...
} else {
MetaObject metaObject = configuration.newMetaObject(parameterObject);
// 改进的部分
String[] boundSqlArrays = sql.split("\\?");
for (int i = 0; i < parameterMappings.size(); i++) {
ParameterMapping parameterMapping = parameterMappings.get(i);
String propertyName = parameterMapping.getProperty();
String objStr = null;
if (metaObject.hasGetter(propertyName)) {
Object obj = metaObject.getValue(propertyName);
objStr = Matcher.quoteReplacement(getParameterValue(obj));
} else if (boundSql.hasAdditionalParameter(propertyName)) {
//该分支是动态sql
Object obj = boundSql.getAdditionalParameter(propertyName);
objStr = Matcher.quoteReplacement(getParameterValue(obj));
} else {
objStr = "";
}
// 下标元素
String elementIndex = boundSqlArrays[i];
String replacement = elementIndex + objStr;
boundSqlArrays[i] = replacement;
}
sql = Arrays.stream(boundSqlArrays).collect(Collectors.joining(""));
}
}
return sql;
}
3.1、 执行结果:
开始:==========================================================
插入600条数据,总计花费875毫秒
结束:==========================================================