【问题标题】:MySQL: Fill a table within a Stored Procedure efficientlyMySQL:有效地填充存储过程中的表
【发布时间】:2013-06-12 18:17:07
【问题描述】:

我正在一个 MySQL 服务器中测试性能并填充一个包含超过 2 亿条记录的表。存储过程生成大 SQL 字符串的速度非常慢。非常欢迎任何帮助或评论。

系统信息:

  • 数据库: MySQL 5.6.10 InnoDB 数据库(测试)。
  • 处理器: AMD Phenom II 1090T X6 内核,每个内核 3910Mhz。
  • 内存: 16GB DDR3 1600Mhz CL8。
  • HD: SSD 中的 Windows 7 64 位 SP1,SSD 中安装的 mySQL,机械硬盘中写入的日志。

存储过程创建一个 INSERT sql 查询,其中包含要插入到表中的所有值。

DELIMITER $$
USE `test`$$

DROP PROCEDURE IF EXISTS `inputRowsNoRandom`$$

CREATE DEFINER=`root`@`localhost` PROCEDURE `inputRowsNoRandom`(IN NumRows BIGINT)
BEGIN
    /* BUILD INSERT SENTENCE WITH A LOS OF ROWS TO INSERT */
    DECLARE i BIGINT;
    DECLARE nMax BIGINT;
    DECLARE squery LONGTEXT;
    DECLARE svalues LONGTEXT;

    SET i = 1;
    SET nMax = NumRows + 1;
    SET squery = 'INSERT INTO `entity_versionable` (fk_entity, str1, str2, bool1, double1, DATE) VALUES ';
    SET svalues = '("1", "a1", 100, 1, 500000, "2013-06-14 12:40:45"),';

    WHILE i < nMax DO
        SET squery = CONCAT(squery, svalues);
        SET i = i + 1;
    END WHILE;

    /*SELECT squery;*/
    SET squery = LEFT(squery, CHAR_LENGTH(squery) - 1);
    SET squery = CONCAT(squery, ";");
    SELECT squery;

    /* EXECUTE INSERT SENTENCE */
    /*START TRANSACTION;*/
    /*PREPARE stmt FROM squery;
    EXECUTE stmt;
    DEALLOCATE PREPARE stmt;
    */

    /*COMMIT;*/
END$$
DELIMITER ;


结果:

  1. 处理连接 20000 个字符串大约需要 45 秒:

调用 test.inputRowsNoRandom(20000);

  1. 连接 100000 个字符串大约需要 +5/12 分钟 O_O:

调用 test.inputRowsNoRandom(100000);

结果(按持续时间排序) - 以秒为单位的状态(总和)||百分比
释放物品 0.00005 50.00000
起始 0.00002 20.00000
执行 0.00001 10.00000
初始化 0.00001 10.00000
清理 0.00001 10.00000
总计 0.00010 100.00000

由于执行查询导致状态变量的变化
变量值说明
Bytes_received 21 字节从客户端发送到服务器
Bytes_sent 97 字节从服务器发送到客户端
Com_select 1 已执行的 SELECT 语句数
问题 1 服务器执行的语句数

测试:
我已经测试了从 12 到 64 线程的不同 MySQL 配置,打开和关闭缓存,将日志移动到另一个硬件磁盘...
还使用 TEXT、INT.. 进行了测试。

其他信息:


问题:

  • 代码有问题吗?如果我发送 100000 个字符串来构建最终的 SQL 字符串,则 SELECT squery; 的结果是一个 NULL 字符串。发生了什么? (一定有错误,但我没有看到)。
  • 我可以通过任何方式改进代码以加快速度吗?
  • 我已经阅读了存储过程中的一些操作可能真的很慢,我应该在 C/Java/PHP.. 和send it to mysql 中生成文件吗?

    mysql -u mysqluser -p 数据库名

  • MySQL 似乎对use only one core for one single SQL querynginx 或其他数据库系统:Multithreadted DBsCassandraRedisMongoDB..) 通过存储过程实现更好的性能并使用多个 CPU 来实现一个查询? (因为我的单个查询只使用了大约 150 个线程的总 CPU 的 20%)。

更新:

【问题讨论】:

  • 一条评论!为什么以 Cthulhu 的名义,您使用 DBMS 循环数次以连接字符串。没想到居然这么快,你在这里检查礼物马的牙齿。
  • 这可能是我在这里遇到的信息量最大的问题,好极了
  • 嗨托尼霍普金森,我试图在这里提供帮助 stackoverflow.com/questions/17042760/… 并试图用大量随机数据快速填充表格以检查性能并使用数百万数据进行一些测试,当我发现了这个问题。

标签: mysql database multithreading stored-procedures concat


【解决方案1】:

不要在 RDBMS 中使用这种规模的循环。

尝试用 1m 行的查询快速填充表格

INSERT INTO `entity_versionable` (fk_entity, str1, str2, bool1, double1, date)
SELECT 1, 'a1', 100, 1, 500000, '2013-06-14 12:40:45'
  FROM
(
select a.N + b.N * 10 + c.N * 100 + d.N * 1000 + e.N * 10000 + f.N * 100000 + 1 N
from (select 0 as N 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) a
      , (select 0 as N 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) b
      , (select 0 as N 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) c
      , (select 0 as N 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) d
      , (select 0 as N 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) e
      , (select 0 as N 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) f
) t

我的盒子(MacBook Pro 16GB RAM,2.6Ghz Intel Core i7)大约需要 8 秒才能完成

查询正常,1000000 行受影响(7.63 秒) 记录:1000000 次重复:0 警告:0

UPDATE1现在是使用预准备语句的存储过程版本

DELIMITER $$
CREATE PROCEDURE `inputRowsNoRandom`(IN NumRows INT)
BEGIN
    DECLARE i INT DEFAULT 0;

    PREPARE stmt 
       FROM 'INSERT INTO `entity_versionable` (fk_entity, str1, str2, bool1, double1, date)
             VALUES(?, ?, ?, ?, ?, ?)';
    SET @v1 = 1, @v2 = 'a1', @v3 = 100, @v4 = 1, @v5 = 500000, @v6 = '2013-06-14 12:40:45';

    WHILE i < NumRows DO
        EXECUTE stmt USING @v1, @v2, @v3, @v4, @v5, @v6;
        SET i = i + 1;
    END WHILE;

    DEALLOCATE PREPARE stmt;
END$$
DELIMITER ;

大约 3 分钟内完成:

mysql> CALL inputRowsNoRandom(1000000); 查询正常,0 行受影响(2 分 51.57 秒)

感受 8 秒与 3 分钟的差异

UPDATE2 为了加快速度,我们可以显式地使用事务并批量提交插入。所以这里是 SP 的改进版本。

DELIMITER $$
CREATE PROCEDURE inputRowsNoRandom1(IN NumRows BIGINT, IN BatchSize INT)
BEGIN
    DECLARE i INT DEFAULT 0;

    PREPARE stmt 
       FROM 'INSERT INTO `entity_versionable` (fk_entity, str1, str2, bool1, double1, date)
             VALUES(?, ?, ?, ?, ?, ?)';
    SET @v1 = 1, @v2 = 'a1', @v3 = 100, @v4 = 1, @v5 = 500000, @v6 = '2013-06-14 12:40:45';

    START TRANSACTION;
    WHILE i < NumRows DO
        EXECUTE stmt USING @v1, @v2, @v3, @v4, @v5, @v6;
        SET i = i + 1;
        IF i % BatchSize = 0 THEN 
            COMMIT;
            START TRANSACTION;
        END IF;
    END WHILE;
    COMMIT;
    DEALLOCATE PREPARE stmt;
END$$
DELIMITER ;

不同批次大小的结果:

mysql> CALL inputRowsNoRandom1(1000000,1000); 查询正常,0 行受影响(27.25 秒) mysql> CALL inputRowsNoRandom1(1000000,10000); 查询正常,0 行受影响(26.76 秒) mysql> CALL inputRowsNoRandom1(1000000,100000); 查询正常,0 行受影响(26.43 秒)

您自己也看到了不同。仍然比交叉连接差 3 倍以上。

【讨论】:

  • 非常好的彼得姆,非常感谢,将仔细查看代码并将标题更改为 MySQL:在存储过程中高效填充表。本主题还增加了相关信息:stackoverflow.com/questions/1415602/…
  • 测试了第一种方法(最快的选择方法)并插入了 1 亿行。完成耗时 16 分 37 秒。我打算用同样的方法测试插入随机数据,可能需要更多的时间。
【解决方案2】:

我的任务和你一样。上面的答案很顺利地解释了事情。我想谈谈我的解决方案。 在我的任务中,我有一个查询。首先我生成随机数据,然后入队查询,然后从那里出队并写入数据库。一开始,写 242 M 数据行花了 70 个小时。然后我换了引擎。 MyISAM 比 InnoDB 快得多。在那之后,写作时间变成了 30 个小时。还是太多了...所以我要换smt...

而不是“插入.....VALUES (1,2,3)”,

我做了“插入.... VALUES (1,2,3), (4,5,6), (7,8,9)"

它运行得非常快。我的意思是将它们切成小块并像批量数据一样写入数据库。

编辑:我写这些是因为我遇到了很多麻烦,然后我想通了,现在我想分享它。哈哈

【讨论】:

    猜你喜欢
    • 2014-01-15
    • 1970-01-01
    • 1970-01-01
    • 2010-12-28
    • 1970-01-01
    • 2017-11-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多