3

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

系统信息:

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

存储过程创建一个 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);

结果(按持续时间排序) - 以秒为单位的状态(总和)|| percentage
freeing items 0.00005
50.00000 starting 0.00002 20.00000
executing 0.00001 10.00000
init 0.00001 10.00000
cleaning up 0.00001 10.00000
Total 0.00010 100.00000

Change Of STATUS VARIABLES Due To Execution Of Query
variable value description
Bytes_received 21 Bytes sent from the client to the server
Bytes_sent 97 Bytes sent from the server到客户端
Com_select 1 已执行的 SELECT 语句
数 Questions 1 服务器执行的语句数

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

附加信息:


问题:

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

    mysql -u mysqluser -p 数据库名<numbers.sql

  • MySQL 似乎只为一个 SQL 查询使用一个内核nginx或其他数据库系统:多线程数据库、CassandraRedisMongoDB ..) 是否可以通过存储过程实现更好的性能,并为一个查询使用多个 CPU?(因为我的单个查询只使用了大约 150 个线程的总 CPU 的 20%)。

更新:

4

2 回答 2

6

不要在 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 秒 vs 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 倍以上。

于 2013-06-17T02:44:27.103 回答
0

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

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

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

它工作得非常快。我的意思是将它们切成小块,然后像批量数据一样写入数据库。

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

于 2020-08-28T12:35:49.900 回答