Mysql批量插入事务插入性能对比

Mysql批量插入事务插入性能对比。对于一些数据量较大的系统数据库面临的问题除了查询效率低下,还有就是数据入库时间长。特别像报表系统,可能每天花费在数据导入上的时间就会长达几个小时之久。因此,优化数据库插入性能是很有意义的。

网络上的牛人很多,总会有一些手段可以提高insert效率,大家跟我一起分享一下吧:

1. 一条SQL语句插入多条数据。

我们常用的插入语句大都是一条一个insert,如:

INSERT INTO `insert_table` (`datetime`, `uid`, `content`, `type`)

VALUES ('0', 'userid_0', 'content_0', 0);

INSERT INTO `insert_table` (`datetime`, `uid`, `content`, `type`)

VALUES ('1', 'userid_1', 'content_1', 1); 现在我们将它修改成:

INSERT INTO `insert_table` (`datetime`, `uid`, `content`, `type`)

VALUES ('0', 'userid_0', 'content_0', 0), ('1', 'userid_1', 'content_1', 1);

【数据对比】

下面是网上牛人提供一些测试对比数据,分别是进行单条数据的导入与转化成一条SQL语句进行导入,分别测试1百、1千、1万条数据记录。通过对比,可以发现修改后的插入操作能够提高程序的插入效率。

\

【缘由分析】

这里第二种SQL执行效率高的主要原因是合并后日志量(MySQL的binlog和innodb的事务让日志)减少了,降低日志刷盘的数据量和频率,从而提高效率。通过合并SQL语句,同时也能减少SQL语句解析的次数,减少网络传输的IO。

2. 在事务中进行插入处理。

在操作数据的时候,事务也是很常用的。现在我们把上面的插入语句修改成:

START TRANSACTION;

INSERT INTO `insert_table` (`datetime`, `uid`, `content`, `type`)

VALUES ('0', 'userid_0', 'content_0', 0);

INSERT INTO `insert_table` (`datetime`, `uid`, `content`, `type`)

VALUES ('1', 'userid_1', 'content_1', 1);

...

COMMIT;

【数据对比】

这里也提供了测试对比,分别是不使用事务与使用事务在记录数为1百、1千、1万的情况。亦发现数据的插入效率提高了。

\

【缘由分析】

这是因为进行一个INSERT操作时,mysql内部会建立一个事务,在事务内才进行真正插入处理操作。通过使用事务可以减少创建事务的消耗,所有插入都在执行后才进行提交操作。

3. 数据有序插入。

数据有序的插入是指插入记录在主键上是有序排列,例如datetime是记录的主键:

原始插入语句如下:

INSERT INTO `insert_table` (`datetime`, `uid`, `content`, `type`)

VALUES ('1', 'userid_1', 'content_1', 1);

INSERT INTO `insert_table` (`datetime`, `uid`, `content`, `type`)

VALUES ('0', 'userid_0', 'content_0', 0);

INSERT INTO `insert_table` (`datetime`, `uid`, `content`, `type`)

VALUES ('2', 'userid_2', 'content_2',2); 修改成:

INSERT INTO `insert_table` (`datetime`, `uid`, `content`, `type`)

VALUES ('0', 'userid_0', 'content_0', 0);

INSERT INTO `insert_table` (`datetime`, `uid`, `content`, `type`)

VALUES ('1', 'userid_1', 'content_1', 1);

INSERT INTO `insert_table` (`datetime`, `uid`, `content`, `type`)

VALUES ('2', 'userid_2', 'content_2',2);

【数据对比】

下面提供随机数据与顺序数据的性能对比,分别是记录为1百、1千、1万、10万、100万。从测试结果来看,该优化方法的性能有所提高,但是提高并不是很明显。

\

【缘由分析】

由于数据库插入时,需要维护索引数据,无序的记录会增大维护索引的成本。我们可以参照innodb使用的B+Tree 索引,如果每次插入记录都在索引的最后面,索引的定位效率很高,并且对索引调整较小;如果插入的记录在索引中间,需要B+tree进行分裂合并等处理,会消耗比较多计算资源,并且插入记录的索引定位效率会下降,数据量较大时会有频繁的磁盘操作。

性能综合测试:

这里提供了同时使用上面三种方法进行INSERT效率优化的测试。

\

从测试结果可以看到,合并数据+事务的方法在较小数据量时,性能提高是很明显的,数据量较大时(1千万以上),性能会急剧下降,这是由于此时数据量超过了innodb_buffer的容量,每次定位索引涉及较多的磁盘读写操作,性能下降较快。而使用合并数据+事务+有序数据的方式在数据量达到千万级以上表现依旧是良好,在数据量较大时,有序数据索引定位较为方便,不需要频繁对磁盘进行读写操作,所以可以维持较高的性能。

注意事项:

SQL语句是有长度限制,在进行数据合并在同一SQL中务必不能超过SQL长度限制,通过max_allowed_packet配置可以修改,默认是1M,测试时修改为8M。事务需要控制大小,事务太大可能会影响执行的效率。MySQL有innodb_log_buffer_size配置项,超过这个值会把innodb的数据刷到磁盘中,这时,效率会有所下降。所以比较好的做法是,在数据达到这个这个值前进行事务提交。

所以原来的代码可以这么改写

$b  = 14;
for ( $a =0; $a <100; $a ++){
   if ( $a ==0)
      $sql  =  "INSERT INTO `roles` (`uid`,`rid`) VALUES (" . $a . "," . $b . ")" ;
   else
     $sql . =  ",(" . $a . "," . $b . ")" ;
}
    START TRANSACTION;
mysql_query( $sql );
COMMIT;

  1. java:
  2. public static void insert() {  
  3.         // 开时时间  
  4.         Long begin = new Date().getTime();  
  5.         // sql前缀  
  6.         String prefix = "INSERT INTO tb_big_data (count, create_time, random) VALUES ";  
  7.         try {  
  8.             // 保存sql后缀  
  9.             StringBuffer suffix = new StringBuffer();  
  10.             // 设置事务为非自动提交  
  11.             conn.setAutoCommit(false);  
  12.             // Statement st = conn.createStatement();  
  13.             // 比起st,pst会更好些  
  14.             PreparedStatement pst = conn.prepareStatement("");  
  15.             // 外层循环,总提交事务次数  
  16.             for (int i = 1; i <= 100; i++) {  
  17.                 // 第次提交步长  
  18.                 for (int j = 1; j <= 10000; j++) {  
  19.                     // 构建sql后缀  
  20.                     suffix.append("(" + j * i + ", SYSDATE(), " + i * j  
  21.                             * Math.random() + "),");  
  22.                 }  
  23.                 // 构建完整sql  
  24.                 String sql = prefix + suffix.substring(0, suffix.length() - 1);  
  25.                 // 添加执行sql  
  26.                 pst.addBatch(sql);  
  27.                 // 执行操作  
  28.                 pst.executeBatch();  
  29.                 // 提交事务  
  30.                 conn.commit();  
  31.                 // 清空上一次添加的数据  
  32.                 suffix = new StringBuffer();  
  33.             }  
  34.             // 头等连接  
  35.             pst.close();  
  36.             conn.close();  
  37.         } catch (SQLException e) {  
  38.             e.printStackTrace();  
  39.         }  
  40.         // 结束时间  
  41.         Long end = new Date().getTime();  
  42.         // 耗时  
  43.         System.out.println("cast : " + (end - begin) / 1000 + " ms");  
  44.     }  
php:
$comlus = 'username,bonus';
$sql  = 'insert into '.$table_name.' ( '.$comlus.')'.''.' value( ';
$arr  = [
    ['张三',150],
    ['张三',50],
    ['张四',250],
    ['张四',20],
];
foreach($arr as $v){
    foreach ($v as $vv){
        $sql .= '\''.$vv.'\',';
    }
    $sql = trim($sql,',');
    $sql .= '),(';
}
$sql = trim($sql,',(');
mysqli_query($link,$sql)



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值