可以优化此查询或表模式吗?

时间:2020-08-19 03:07:22

标签: mysql optimization

我正在运行此过程数百万次,尽管每次都需要花费数毫秒,但最终要花几周的时间才能运行所有这些过程。我想知道是否有人可以帮助我优化或改善其性能。任何改进都可以节省几天!

CREATE PROCEDURE process_parameters(IN parameter1 VARCHAR(128), IN parameter2 VARCHAR(128), IN combination_type CHAR(1))
BEGIN

        SET @parameter1_id := NULL, @parameter2_id := NULL;
        SET @parameter1_hash := "", @parameter2_hash := "";

        IF parameter1 IS NOT NULL THEN

                SET @parameter_hash := parameter1;
                INSERT IGNORE INTO `collection1` (`parameter`) VALUES (parameter1);
                SET @parameter1_id := (SELECT `id` FROM `collection1` WHERE `parameter` = parameter1);

        END IF;

        IF parameter2 IS NOT NULL THEN

                SET @parameter2_hash := parameter2;
                INSERT IGNORE INTO `collection2` (`parameter`) VALUES (parameter2);
                SET @parameter2_id := (SELECT `id` FROM `collection2` WHERE `parameter` = parameter2);

        END IF;

        SET @hash := MD5(CONCAT(@parameter1_hash, @parameter2_hash));
        INSERT IGNORE INTO `combinations` (`hash`,`type`,`parameter1`,`parameter2`) VALUES (@hash, combination_type, @parameter1_id, @parameter2_id);

END

其背后的逻辑是:我将(parameter1, parameter2)的唯一组合存储在combinations中,其中parameter1paramter2可以是NULL(但绝不能两者都与此同时)。我将type存储在combinations中,以便以后知道哪个parameter具有价值。为了确保组合是唯一的,我添加了一个MD5字段(主键(parameter1,parameter2)由于与NULL的比较总是返回NULL而无法工作)。每个parameter都有一个单独的表(分别为collection1collection2)来存储其唯一的id。有成千上万的唯一parameter1parameter2,但是它们的组合被高度重复,并且远低于基数乘法。

例如,("A", "1")("A", "2")("B", "1")("A", "1")("A", NULL)(NULL, "2")会产生:

`collection1` (`id`, `parameter`)
1, "A"
2, "B"

`collection2` (`id`, `parameter`)
1, "1"
2, "2"

`combinations` (`type`, `parameter1`, `parameter2`)
"P1andP2", 1, 1,
"P1andP2", 1, 2,
"P1andP2", 2, 1,
"P1Only",  1, NULL
"P2Only",  NULL, 2

这些是表的定义:

DESCRIBE `combinations`;
+-------------+-----------------------------------+------+-----+---------+----------------+
| Field       | Type                              | Null | Key | Default | Extra          |
+-------------+-----------------------------------+------+-----+---------+----------------+
| combination | int(11)                           | NO   | PRI | NULL    | auto_increment |
| hash        | char(32)                          | NO   | UNI | NULL    |                |
| type        | enum('P1andP2','P1Only','P2Only') | NO   |     | NULL    |                |
| parameter1  | int(11)                           | YES  |     | NULL    |                |
| parameter2  | int(11)                           | YES  |     | NULL    |                |
+-------------+-----------------------------------+------+-----+---------+----------------+

DESCRIBE `collection1`; (`collection2` is identical)
+-----------+--------------+------+-----+---------+----------------+
| Field     | Type         | Null | Key | Default | Extra          |
+-----------+--------------+------+-----+---------+----------------+
| id        | int(11)      | NO   | PRI | NULL    | auto_increment |
| parameter | varchar(255) | NO   | UNI | NULL    |                |
+-----------+--------------+------+-----+---------+----------------+

任何帮助将不胜感激!

1 个答案:

答案 0 :(得分:0)

请使用SHOW CREATE TABLE;比DESCRIBE更具描述性。

使用LAST_INSERT_ID()

 SET @parameter1_id := (SELECT `id` FROM `collection1`
                          WHERE `parameter` = parameter1);

可以替换为

 SELECT @parameter1_id := LAST_INSERT_ID();

这将避免往返服务器。

糟糕... OP指出如果该行是dup,则不会返回ID。这是一种可能运行速度更快的解决方法:

INSERT INTO `collection1` (`parameter`)
        VALUES (parameter1)
    ON DUPLICATE KEY UPDATE
        id = LAST_INSERT_ID(id);
SELECT @parameter1 := LAST_INSERT_ID(id);

这是一个笨拙的把戏,在文档中的某个地方进行了记录。但;下面更多...

收缩表

  • 您真的需要combination吗?您还有另一个UNIQUE键可以用作PRIMARY KEY。这可能会减少最后INSERT所花费的时间。

  • 这可能会(或可能不会)加快速度,但仅是因为行大小缩小了:与其将md5存储到CHAR(32)中,不如将UNHEX(md5)存储到BINARY(16)中。 / p>

批量插入

您能一次将一堆东西收集到INSERT吗?如果您收集1000行并将它们放入单个INSERT(实际上是3 INSERTs,因为涉及到3个表),则它的运行速度实际上是原来的10倍。

由于需要ID,因此变得更加复杂。您需要将事物分为collection1collection2然后combinations上工作。

由于“ combination *”表本质上是“规范化”的表,请参见我关于如何非常有效地对其进行批处理的讨论:http://mysql.rjweb.org/doc.php/staging_table#normalization它涉及2条语句,其中一条用于插入新行,另一条用于获取所有id。批处理。

凉爽

完全摆脱@parameter*_hash@hash。将@hash调用的使用更改为:

INSERT IGNORE INTO combinations (...) VALUES
    ( CONCAT(COALESCE(parameter1,''), COALESCE(parameter2, '')),
     ...)

这样想吧...每个语句花费不小的时间。 (这在插入的批处理中显着显示。)由于增加了一条语句的复杂性,我付出了一些代价摆脱了4条语句。

设置

最重要的可能是innodb_flush_log_at_trx_commit = 2

3个流

编写3个过程,每个过程都将代码简化为特定的type。将此与批处理结合起来可以进一步加快速度。

潜在问题

  • 我认为这两个人将获得相同的hash。因此,这两个只有一行:

      ("xyz", NULL)
      (NULL, "xyz")
    
  • 请注意,如果已经存在具有给定唯一键的行,则INSERT IGNORE将刻录id。因此,请密切注意INT(只有20亿)的价值用尽。更改为INT UNSIGNED会将其提高到4B,仍然为4个字节。