优化系列
在调优网站 http://www.kdsw.cn/ 的过程中碰到的问题
1 StringBuffer与String的天差地别
例子
这是我在该项目中查导出文件很慢的原因时察觉到的一个问题 当时客户问我们导出怎么这么久 吓死我了
//初始化导出文件内容
String outStr =new String ("");
//数据行数
int lines = data.length;
for (int i = 0; i < lines; i++)
{
//数据列数
int columns = data[i].length;
for(int j = 0; j < columns; j++)
{
if (data[i][j] == null)
{
data[i][j] = "";
}
//将每列数据中的“,”替换
if ( data[i][j].indexOf(',') >= 0)
{
int strLen = data[i][j].length();
int pos = 0;
while (pos >= 0 && pos < strLen)
{
pos = data[i][j].indexOf(',', pos);
if (pos >= 0)
{
data[i][j] = data[i][j].substring(0,pos) + "," + data[i][j].substring(pos + 1);
}
}
}
outStr+= (data[i][j]);
//如果不是每行的最后一列,用“,”号将数据分隔
if (j < columns - 1)
{
outStr+=(",");
}
}
//每行加一个换行
outStr+= ("/n");
}
假如现在data[i][j]的大小事 data[10000][10] 也就是说二维数组有
10000行 10列 假如数据显示如下
Id name sex age uid pwd pic tilte content desc
1 廖敏 男 22 liaomin789 liaomin999 图片1 大家好 你们好情况 好啊
1 胡群 女 21 liaomin789 liaomin999 图片1 大家好 你们好情况 好啊
1 baby 男 22 liaomin789 liaomin999 图片1 大家好 你们好情况 好啊
。。。。。。。
通过执行 上面一段代码 结果就会形成一个很长的字符串
猜一下 上面代码 需要执行多久 结果是 30分钟,客户足足等了30分钟 才出来
仍然是上面的数组 代码改成下面
//初始化导出文件内容
StringBuffer outStr =new StringBuffer("");
//数据行数
int lines = data.length;
for (int i = 0; i < lines; i++)
{
//数据列数
int columns = data[i].length;
for(int j = 0; j < columns; j++)
{
if (data[i][j] == null)
{
data[i][j] = "";
}
//将每列数据中的“,”替换
if ( data[i][j].indexOf(',') >= 0)
{
int strLen = data[i][j].length();
int pos = 0;
while (pos >= 0 && pos < strLen)
{
pos = data[i][j].indexOf(',', pos);
if (pos >= 0)
{
data[i][j] = data[i][j].substring(0,pos) + "," + data[i][j].substring(pos + 1);
}
}
}
outStr.append(data[i][j]);
//如果不是每行的最后一列,用“,”号将数据分隔
if (j < columns - 1)
{
outStr.append(",");
}
}
//每行加一个换行
outStr.append("/n");
}
猜一下 需要执行多久 结果是5秒
不可思议吧 这就是string 与stringbuffer之间的差别
为什么了
因为String类型的字符串本身是不可修改的
你操作一次 内存中就会多了一个String 类型的数据
上面一个数组 循环 10000*10次 操作了将近 10次
所以总共参数 1000000个String对象 内存怎么容得下 何其庞大
而StringBuffer 操作的对象本身 从头到尾就一个对象 所以速度
非常的快
2 与数据库交互的次数
在网站发送短信 时碰到的问题
看下面代码
List<IosSmSendBO> smSendBOList = (List<IosSmSendBO>)sendService.getSendBoxList(queryBO);
for (int i = 0; i < smSendBOList.size(); i++)
{
IosSmSendBO sendBO = smSendBOList.get(i);
// add by ZouJing at Jun 17, 2009
// 操作黑名单短信
query.setGroupId(sendBO.getGroupId());
query.setMobileNo(sendBO.getReceiverCode());
if(iosSmBlackAddrService.isBlackAddr(query))
{
sendBO.setStatus("0");
// 失败原因
sendBO.setSendFailReason("号码:"
+ sendBO.getReceiverCode()
+ "是黑名单号码,禁止发送!");
Logger.getLogger(this.getClass()).info(
"号码:" + sendBO.getReceiverCode()
+ "是黑名单号码,禁止发送!");
// 发送时间
sendBO.setSendTime(DateTimeFormat.FormatDateToString(
new Date(), DateTimeFormat.DATE_WITH_TIME));
// 新增发送短信记录及在发送后修改后改变状态
sendService.updateIosSmSend(sendBO);
continue;
}
// 设置内外网号码
IosBuyInfoBO buyInfoBO = new IosBuyInfoBO();
buyInfoBO.setGroupId(sendBO.getGroupId());
List<IosBuyInfoBO> buyList = buyInfoService.getIosBuyInfoBOs(buyInfoBO);
sendBO.setExternalValueIn(buyList.get(0).getExternalValueIn());
sendBO.setExternalValueOut(buyList.get(0).getExternalValueOut());
//取出发送用户的信息
IosPersonInfoBO userInfo = new IosPersonInfoBO();
userInfo.setOid(sendBO.getCreatedBy());
List<IosPersonInfoBO> personInfoList = personInfoService.queryCorpLists(userInfo, false);
if (personInfoList != null && personInfoList.size() > 0) {
IosPersonInfoBO personInfo = personInfoList.get(0);
//集团签名
String groupId = personInfo.getGroupId();
IosGroupInfoBO groupInfoBO = new IosGroupInfoBO();
groupInfoBO.setGroupId(groupId);
List<IosGroupInfoBO> groupInfoBOList = groupInfoService.getIosGroupInfoBOs(groupInfoBO);
if (groupInfoBOList != null && groupInfoBOList.size() > 0)
{
sendBO.setGroupName(groupInfoBOList.get(0).getGroupName());
//add by hezhenbo at 2009-06-13
sendBO.setBossId(groupInfoBOList.get(0).getBossId());
}
}
假如 List<IosSmSendBO> smSendBOList = (List<IosSmSendBO>)sendService.getSendBoxList(queryBO);
取出smSendBOList的数据是 2000条
在循环取出smSendBOList数据 进行验证
在循环体内部代码
iosSmBlackAddrService.isBlackAddr(query) //判断是否是黑名单
List<IosPersonInfoBO> personInfoList = personInfoService.queryCorpLists(userInfo, false); //取出接受信息人
List<IosGroupInfoBO> groupInfoBOList = groupInfoService.getIosGroupInfoBOs(groupInfoBO);//取出接受人组信息
这些都是在进行数据库查询 每次调用一次都会打开一次数据库连接
打开数据库连接 一次需要 50ms 尚且不考虑内存消耗,调用三次有 150ms
2000次循环 需要300000ms 需要300s
兄弟 你想 加入 批量发送2000条信息 需要300s 也就是5分钟 人家等 5分钟啊
所以 是行不通的
假如我将那些数据批量取出放到内存中
只需要调用三次
测试 调用一次 取出所有满足条件数据3s
调用 三次 9s 内存中循环操作处理 大概 2s
只需 11s
11s 相对于 300s 是何其壮观啊
这就是批量与不批量的差别
3 内外连接
加入数据库中 20w条数据 在plsql中执行
查询所有数据
内连接
select * from ios_self_addresslist isa, ios_self_team_rel istr, ios_self_team ist
where isa.oid = istr.self_adl_id and ist.oid = istr.team_id
and isa.stop_flag = 0 and istr.stop_flag=0
耗时 0.046s
左连接
select * from ios_self_addresslist isa left join ios_self_team_rel istr on isa.oid = istr.self_adl_id
left join ios_self_team ist on ist.oid = istr.team_id
where isa.stop_flag = 0 and istr.stop_flag=0
耗时1.813s
查询所有总数
内连接
select * from ios_self_addresslist isa, ios_self_team_rel istr, ios_self_team ist
where isa.oid = istr.self_adl_id and ist.oid = istr.team_id
and isa.stop_flag = 0 and istr.stop_flag=0
耗时 1.078s
左连接
select * from ios_self_addresslist isa left join ios_self_team_rel istr on isa.oid = istr.self_adl_id
left join ios_self_team ist on ist.oid = istr.team_id
where isa.stop_flag = 0 and istr.stop_flag=0
耗时 1.063