为什么插入语句的时间成本范围如此之大?

时间:2017-08-13 07:03:50

标签: java mysql jdbc innodb jdbctemplate

我想导入维基百科数据库。 Wikipedia Dumps

我更改了一些Innodb设置:

innodb_buffer_pool_size = 4G
innodb_log_file_size=8G
innodb_log_buffer_size=512M
innodb_write_io_threads = 16
innodb_flush_log_at_trx_commit = 0
max_allowed_packet=256M

enter image description here

此图显示每个Sql语句的时间成本。

sql语句是这样的:插入templatelinks值(...),(...),(...),(...)......每一个sql语句&#39 ; s大小为1MB。

为什么第1300到第1400个sql会花费这么多时间?

更多信息:

  1. 在此期间,此表的大小几乎为9GB,表行为4000万。
  2. 我使用Spring JdbcTemplate来执行sql语句
  3. 我的电脑内存为8G,数据库存储在硬盘中。
  4. 核心代码:

        JdbcTemplate jdbcTemplate=new JdbcTemplate(mDatasour);
    
        while (it.hasNext()){
            jdbcTemplate.getDataSource().getConnection().setAutoCommit(false);
            jdbcTemplate.execute("SET UNIQUE_CHECKS=0; ");
            jdbcTemplate.execute("SET FOREIGN_KEY_CHECKS=0; ");
            String line=it.nextLine();
            if(line==null||line.length()==0){
                continue;
            }
            jdbcTemplate.execute(line);
            jdbcTemplate.execute("commit;");
    

    table columns

    table index details

2 个答案:

答案 0 :(得分:1)

优化代码

将常量东西拉出循环。使用自动提交以避免需要发出COMMIT

jdbcTemplate.getDataSource().getConnection().setAutoCommit(true); -- Note
jdbcTemplate.execute("SET UNIQUE_CHECKS=0; ");
jdbcTemplate.execute("SET FOREIGN_KEY_CHECKS=0; ");
while (it.hasNext()){
    String line=it.nextLine();
    if(line==null||line.length()==0){
        continue;
    }
    jdbcTemplate.execute(line);
}

较小的块

停止1000行或1MB的构建线,以较小者为准。除此之外,您还可以减少回报,并强调撤消日志。

摆脱多余的独特

Drop UNIQUE(from, namespace, title)对于具有相同列列表的PRIMARY KEY是多余的。这将减少插入工作量,因为必须在INSERT期间检查所有唯一索引。

更改缓冲

阅读“更改缓冲”。它本质上是非唯一索引的延迟(和优化)编写,您似乎有两个。在大量插入期间的某个时刻,更改缓冲区已满,必须刷新。这种冲洗是在后台进行的,所以我无法解释尖峰的严重程度。

我怀疑在加载了大约1/3的表后,更改缓冲区得到了太多的积压,这导致了打嗝。

传入行的顺序

需要考虑的其他事项......正在编写的行的顺序是什么?如果它们是以PK顺序编写的,则每个INSERT进入PK(和数据)(以及冗余UNIQUE索引)所需的I / O非常少。您的图表似乎通过I / O开头很长时间来显示这一点。

但是,二级索引(通过更改缓冲区处理)可能非常随机。 (我不知道from指的是什么。)这导致了很多“读 - 修改 - 写”动作来更新两个二级索引的索引块。

由于PK +数据不需要buffer_pool,但二级索引不需要,考虑调整innodb_change_buffer_max_size,默认为“为更改缓冲区预留的buffer_pool的25%”。

刷新到磁盘

另一个问题:写入PK +数据不需要刷新到磁盘,直到超过“innodb_max_dirty_pages_pct - 默认为buffer_pool的90%”。同样,这与早期的低I / O一致。

了解这两个设置,玩弄它们,然后写一篇博文。你将成为这方面的专家。

答案 1 :(得分:1)

对于innodb_buffer_pool_size = 4G,在为innodb_change_buffer_max_size预留25%后,您的有效池大小为3G。

您的innodb_io_capacity和innodb_io_capacity_max可能会被提升以利用今天的硬盘容量。从Microsoft下载sqlio.exe可以帮助您确定适用于HDD的16384(MySQL)数据块大小的限制。

添加RAM对于将数据保存在内存中更为合适。