8

我有一个如下所示的 MySQL 表:

MySQL 表:状态

创建结构的 SQL 是:

CREATE TABLE `status` (
`id` INT(11) NOT NULL,
`responseCode` INT(3) NOT NULL DEFAULT '503',
`lastUpdate` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;

它存储一个唯一idresponseCode、 和lastUpdate。这responseCode是一个 HTTP 请求响应代码:404、500、503、200 等。

我有一个 URL 对应于id我发出 HTTP 请求的每个 URL,并在此表中记录我发出请求和收到响应的时间。

该脚本对status表进行此查询:

SELECT id FROM status WHERE lastUpdate < 'XXXX' OR 
(responseCode != 200 AND responseCode != 404) 
ORDER BY id DESC LIMIT 100

我决定无论响应代码如何XXXX,都需要刷新比该日期更早的任何日期。此外,如果我没有得到200404不管最后lastUpdate日期,我想重新尝试 HTTP 请求。我LIMIT选择 100 次,因为我一次只运行 100 次,然后我让它休眠一段时间,然后再运行 100 次,依此类推。

无论如何,一切都很好,但我想要做的是提前填充表格,说一个像这样的系列:

(1, 503, NOW()), (2, 503, NOW()), (3, 503, NOW()) ... (100000, 503, NOW())

请注意,只有 ID 在递增,但根据我的需要,它可能不一定从 1 开始。我希望像这样预先填充表格,因为上面的查询可以继续抓取id我们需要重新尝试的表格,并且我不想在status表格中插入更多内容,因为id's 是有限的并且不会改变(但有很多)。

我尝试使用 JAVA,(尽管 PHP、C# 或其他任何东西是相同的概念,我在这里使用哪种语言并不重要):

PreparedStatement st = conn.prepareStatement("INSERT INTO status VALUES (?,default,default)");

for( int i = 1; i <= 100000; i++ ) {
    st.setInt(1,i);
    st.addBatch();
}

System.out.println( "Running batch..." );
st.executeBatch();
System.out.println( "Batch done!" );

这将启动插入,但问题是填充表需要大量时间(我没有确切的时间,但它运行了几个小时)。所以,我的问题归结为:是否有一种简单有效的方法来填充具有大量这样行的 MySQL 表?

4

4 回答 4

14

一般来说,您可以使用以下任何一项或多项:

  • 启动事务,插入,提交
  • 将多个值打包到单个插入查询中
  • 在进行插入之前删除任何约束并在批量插入之后恢复约束(可能主键除外,但不太确定)
  • insert into ... select合适时使用

第一个(使用事务)最有可能有帮助,但我不确定它是否适用于 myisam 表,使用 innodb 做得很好 - 我只在被迫使用 mysql 时使用那些,我更喜欢 postgresql .

在您的特定情况下,插入 100000 行数据,您可以执行以下操作:

INSERT INTO status(id, responseCode, lastUpdate) SELECT @row := @row + 1 as row, 503, NOW() FROM 
(select 0 union all select 1 union all select 2 union all select 3 union all select 4 union all select 5 union all select 6 union all select 7 union all select 8 union all select 9) t,
(select 0 union all select 1 union all select 2 union all select 3 union all select 4 union all select 5 union all select 6 union all select 7 union all select 8 union all select 9) t2, 
(select 0 union all select 1 union all select 2 union all select 3 union all select 4 union all select 5 union all select 6 union all select 7 union all select 8 union all select 9) t3, 
(select 0 union all select 1 union all select 2 union all select 3 union all select 4 union all select 5 union all select 6 union all select 7 union all select 8 union all select 9) t4, 
(select 0 union all select 1 union all select 2 union all select 3 union all select 4 union all select 5 union all select 6 union all select 7 union all select 8 union all select 9) t5, 
(SELECT @row:=0) t6;

在我的机器上测试了这个,得到:

Query OK, 100000 rows affected (0.70 sec)
Records: 100000  Duplicates: 0  Warnings: 0

我很确定你不能比 100000 行快得多。

于 2012-10-08T20:36:34.630 回答
9

如何设置AUTO_INCREMENT主键。

然后以您喜欢的方式插入前一百(或一千)行(您的示例或 DocJones 给您的示例)。

然后使用

INSERT INTO table SELECT NULL, '503', NOW() FROM table;

……反复几次。这应该使表格每次都变大一倍。

NULL第一个槽SELECT确保AUTO_INCREMENT踢入和增量id

如果你想让桌子变得更快,你可以做

INSERT INTO table SELECT NULL, '503', NOW() FROM table AS t1 CROSS JOIN table t2;

...重复几次,这将使表格的大小增加,前一个大小的两倍 + 前一个大小 (100^2+100) 的幂。

这也允许您自定义插入的值,例如,如果您想创建“随机” responseCodes,您可以使用类似的东西CONCAT(ROUND(1+RAND()*4), '0', ROUND(RAND()*5)),它将为您提供从 100 到 505 的响应代码。

于 2012-10-08T20:27:53.803 回答
2

PHP 解决方案以 100 个批量加载它们:

for ($i = 0; $i < 100000; $i+=100) {
  $vals = implode(', ', 
                  array_map(function($j) { return "($j, default, default)";},
                            range($i, $i+100)));
  mysqli_query($dbh, 'insert into status values ' . $vals) or die mysqli_error($dbh);
}
于 2012-10-08T21:24:26.210 回答
1

您正在创建一个要执行的 LARGE 批处理语句。尝试使用例如将其拆分为较小的包。在循环内每增加 1000 个 i(使用 mod(i) yadddayadda)调用 executeBatch()。这应该加快这个过程:

for( int i = 1; i <= 100000; i++ ) {
    st.setInt(1,i);
    st.addBatch();
    if (mod(i,1000)=0) {
       st.executeBatch();
    }
}
于 2012-10-08T19:56:20.107 回答