mysql数据导入慢,mysql 突然变慢

  mysql数据导入慢,mysql 突然变慢

  如何解决写爬虫IP受阻的问题?立即使用。

  推荐:《mysql视频教程》 《java教程》

  近期项目需要导入大量数据,查询时需要插入。插入的数据量在100w左右。一开始觉得100w的数据量不大,就插上插下。吃完饭回来,看到插了50多条数据后,每秒只能插10条。很奇怪。为什么越来越慢?于是我开始分析插入时间损失,想出了以下解决方案:(mysql使用的INNODB引擎)

  1.分析是否是由主码,外码,索引造成的插入效率降低

  主密钥:因为每个表都需要主密钥,所以不能删除它。Mysql会自动建立主代码的索引,默认是Btree索引,所以每次插入数据都要多插入一次Btree。这个额外的插入时间复杂度大约是log(n)。该索引无法删除,因此无法优化。但是我们每次插入的时候都需要检查是否出现了主键,因为主键约束,需要log(n)。我们能减少这种开销吗?答案是肯定的。我们可以将主密钥设置为auto-increment id _ increment,这样当前的自动增量值就会自动记录在数据库中,这样就保证了不会插入重复的主密钥,从而避免了主密钥的重复检查。

  外来代码:因为我的项目的插入表中存在外来代码,所以每次插入时都需要检查另一个表中是否存在外来代码。这个约束关系到业务逻辑,不能随便删除。而且这个时间开销应该是一个与另一个表的大小成正比的常数,不应该插入得更慢。所以排除它。

  索引:为了减少Btree插入的时间消耗,我们可以在创建表之前插入所有数据,而不用索引。然后我们向表中添加索引。这种方法确实减少了时间开销。

  经过上面的折腾,然后测试,发现速度快了一点,但是到了50w就开始变慢了。看来问题的关键不在这里。于是继续查资料,发现了一个关键问题:

  2.将单条插入改为批量插入(参考:点击打开链接)

  因为java中的executeUpdate(sql)方法只执行一个sql操作,所以需要调用sql中的各种资源。如果使用for循环来执行这个方法进行插入,无疑会耗费很大。因此,mysql提供了一个解决方案:批量插入。也就是说,每个sql不是直接提交的,而是首先存在于批处理任务集中。当任务集的大小达到指定的阈值时,这些sql被一起发送到mysql。在100w的数据规模中,我将阈值设置为10000,即我一次提交10000条sql。最终效果相当不错,插入速度比之前快了20倍左右。批量插入代码如下:

  公共静态void insertRelease() {

  长开始=新日期()。getTime();

  string SQL= INSERT INTO TB _ big _ data(count,create_time,random)值(?SYSDATE(),);

  尝试{

  conn . set auto commit(false);

  prepared statement PST=conn . prepare statement(SQL);

  for(int I=1;i=100i ) {

  for(int k=1;k=10000k ) {

  pst.setLong(1,k * I);

  pst.setLong(2,k * I);

  PST . add batch();

  }

  PST . execute batch();

  conn . commit();

  }

  PST . close();

  conn . close();

  } catch (SQLException e) {

  e . printstacktrace();

  }

  长结束=新日期()。getTime();

  system . out . println( cast:(end-begin)/1000 ms );

  }3.一条UPDATE语句的VALUES后面跟上多条的(?,?,?,?)

  一开始我以为这个方法和上面的差不多,但是看了别人做的实验,发现用这个方法改进上面的批量插入可以快5倍。后来发现mysql导出的sql文件中的那些insert语句也是这样写的。即更新table _ name (a1,a2)值(xx,xx),(xx,xx),(xx,xx).也就是说,我们需要在后台自己拼接一个字符串。请注意,因为字符串只是不断地插入到末尾,所以使用StringBuffer可以更快地插入。代码如下:

  公共静态void insert() {

  //开放时间

  长开始=新日期()。getTime();

  //sql前缀

  字符串前缀=插入到tb_big_data (count,create_time,random)值中;

  尝试{

  //保存sql后缀

  StringBuffer后缀=new string buffer();

  //将事务设置为非自动提交

  conn . set auto commit(false);

  //语句ST=conn . create Statement();

  //pst会比st好。

  prepared statement PST=conn . prepare statement(“”);

  //外部循环,已提交事务的总数

  for(int I=1;i=100i ) {

  //第一次提交的步长

  for(int j=1;j=10000j ) {

  //构建sql后缀

  后缀. append(( j * I ,SYSDATE(), i * j

  * Math.random()),);

  }

  //构建完整的sql

  string SQL=prefix suffix . substring(0,suffix . length()-1);

  //添加执行sql

  PST . add batch(SQL);

  //执行操作

  PST . execute batch();

  //提交事务

  conn . commit();

  //清除最后添加的数据

  suffix=new string buffer();

  }

  //第一类连接

  PST . close();

  conn . close();

  } catch (SQLException e) {

  e . printstacktrace();

  }

  //结束时间

  长结束=新日期()。getTime();

  //耗时

  system . out . println( cast:(end-begin)/1000 ms );

  }做了上面的优化,发现了一个很痛苦的问题。虽然最初的插入速度确实快了几倍,但是插入50w条数据后,插入速度总会突然变得很慢。这种慢插就是断崖式的突变,于是我苦苦思索,不小心打开了系统的资源管理器。第一眼就发现java占用的内存在飙升。突然想到:是不是内存溢出了?

  4.及时释放查询结果

  在我的数据库查询语句中,pres=con.prepareStatement(sql)用于保存一个sql执行状态,resultSet=pres.executeQuery用于保存查询结果集。在搜索和插入的过程中,我的代码一直没有释放查询结果,导致它不断占用内存空间。我的插入在50w左右的时候,内存空间已经满了,所以数据库的插入开始用磁盘代替内存,所以插入速度开始变得很低。所以每次使用pres和resultSet后,我都添加了释放它们空间的语句:resultSet . close();pres . close();再次测试,果然内存没有飙升,插数据到50w后速度也没有下降。原来问题的本质就在这里!以上是mysql插入数据慢的原因的详细内容。更多请关注我们的其他相关文章!

郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。

留言与评论(共有 条评论)
   
验证码: