Java与MySQL优化之旅(一)——从14小时到40秒

来源:互联网 发布:centos 7 kvm 桥接 编辑:程序博客网 时间:2024/04/29 18:33

今天开始写几篇关于Java  Performance Tuning 与 MySQL Performance Tuning的文章,作为最近几天自己学习的一个总结与备忘。

我现在有这么一个txt文本(貌似是一个词库),txt中的内容大约有20万行的数据,每一条数据的格式大致为:连续的几个E文字母开头,后接n(1<=n)个词组,例如:

aaab 或散或聚 葡萄牙队 七七节 芳菲节 斯蒂芬阶

我现在要做的就是把这些有空格隔开的词组全部都单独提取出来,存放进我的MySQL数据库中,另外,文本当中存在一些重复出现的词组

当然,我首先要在MySQL数据库中建立一个表word,表中只有一个字段wordname (PK)

另外,我使用的是5.1.26-rc版本的MySQL,采用的是InnoDB store engine,编码格式采用UTF8,其他的参数暂时全部采用默认值。

完成这个事情的方法有很多。首先来看一下第一种方法。

 

方法一:

1、程序启动;

2、把txt文本当中的数据全部装载进入内存当中

3、利用BufferReader.readLine()方法每次从文本当中读取一行数据,放入一个临时String变量str_temp当中;

4、对str_temp跟据“空格符”进行分割,没分割出一个词组,就执行一行SQL语句:

  1. executeUpdate("insert into word(wordname) values(/"" + value +"/")");

5、回到步骤3,知道所有的数据全部装入MySQL中为止。

以上步骤实现起来十分的简单,我们只需要在程序当中加上一个try……catch快处理插入失败抛出的SQLException即可避免因为往数据库插入了重复值而导致的程序运行终止。

但是,方法一虽然实现起来十分简单,但是其效率确实十分的低下。我耐着性子等待程序跑完,结果这段小小的代码足足跑了将近14个小时才结束……这样的效率实在是让人无法接受!

如何提高这个程序的运行数度呢?我们来看看方法二。

 

方法二:

我们首先来分析一下方法一。他为什么会这么慢?

我们知道,往MySQL当中插入N行数据大概有这么两种方式:

一:

insert into tablename(fieldname) values(value1);

insert into tablename(fieldname) values(value2);

     ……

insert into tablename(fieldname) values(valueN);

即连续执行N次insert语句。在方法一中我才用的就是这种插入方式。

 

二:

insert into tablename(fielname) values(value1),(value2),……,(valueN);

即使用values列表批量往MySQL中插入数据。

 

一般情况下,采用第二种方式的数据插入效率要远远高于采用第一种方式。但是,他存在一个问题,就是如果在values列表当中,其中只要出现一个非法的value,就会导致这个插入操作失败,列表当中的其他合法的value都将不会被插入。

现在,我考虑使用第二种数据插入方式来从写我的程序。首先,我要解决掉上面提到的非法value问题。就我的这个txt文本数据的实际情况来说,出现的非法value应该都是属于“重复值”问题(我设置的字段属性为PK)。解决方法也不难,我当时采用的步骤是:

1、对这些无序的数据进行排序;

2、便利这些数据,去除掉重复出现的数据。

 

在这里由于我把数据全部放在一个String[] 当中,因此,我并没有执行严格意义上的删除操作,因为那样会引发数组的一位操作,那将会是一场灾难。我的方法是仅仅将重复值标记为null。另外,关于排序,我当时使用的“快排”。事实上,在我的这个程序中,“快排”的效率还是比较令人满意的。测试了一下,对我这一百多万个词组进行排序与去重两步操作,耗时不到10秒,相比于方法一的14个小时,这10时间几乎可以忽略。当然,更好的排序方式还是有的。

 

于是,这个方法的具体实现步骤变为:

1、程序启动;

2、把txt文本当中的数据全部装载进入内存当中

3、对数据根据“空格符”与“换行符”进行分割,存入一个String[]当中

4、对这个String[]进行排序与去重操作

5、利用这个有序的String[]拼装SQL语句,把数据批量插入到MySQL当中。

 

这里有个地方需要注意:执行第5步操作时,MySQL可能“报错”,原因是MySQL服务器默认把max_allowed_packet值设为1M,而这在我的这个应用当中显然是不够的(我估算一下,这个值至少需要将近40M的内存空间才能满足)。解决方法是,在MySQL的my.ini文件当中的[mysqld]区域加入一行:

max_allowed_packet=64M

然后重启一下MySQL,执行show variables like '%max_allowed_packet%';  进行查询,如果max_allowed_packet值已经改变,则可以重新执行依据方法二实现的程序。

 

我测试了一下,方法二的程序运行时间仅仅为不到40秒,在多次测试下,最快时间能够达到30秒!(至于多次测试下的时间差距产生的原因,我将在后续的文章中讨论)

 

从14个小时到不需40秒,这样大幅度的性能提高仅仅是因为对数据插入的SQL语句做了一些改变!so crazy!

 

问题:这个时间还能提高吗?

回答:of course!

下一篇文章,将讨论如何继续提高这个程序的效率。那将会设计到一些在性能优化上用到的一些工具以及对JVM参数、MySQL参数的一些调整。

原创粉丝点击