我正在运行此过程数百万次,尽管每次都需要花费数毫秒,但最终要花几周的时间才能运行所有这些过程。我想知道是否有人可以帮助我优化或改善其性能。任何改进都可以节省几天!
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
中,其中parameter1
或paramter2
可以是NULL
(但绝不能两者都与此同时)。我将type
存储在combinations
中,以便以后知道哪个parameter
具有价值。为了确保组合是唯一的,我添加了一个MD5字段(主键(parameter1,parameter2)
由于与NULL
的比较总是返回NULL
而无法工作)。每个parameter
都有一个单独的表(分别为collection1
和collection2
)来存储其唯一的id
。有成千上万的唯一parameter1
和parameter2
,但是它们的组合被高度重复,并且远低于基数乘法。
例如,("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 | |
+-----------+--------------+------+-----+---------+----------------+
任何帮助将不胜感激!
答案 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,因此变得更加复杂。您需要将事物分为collection1
和collection2
; 然后在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个字节。