协助优化以下 MySql 程序
Assistance with optimizing below MySql procedure
MySql - innodb_version 5.7.33
我正在处理一个存储过程,该过程将定期调用(假设每月一次)以填充 table,其中一列是字符串列表,另一列是静态值。
table 还有
- ID 列 (AUTO_INCREMENT) 和
- 时间戳列(CURRENT_TIMESTAMP 更新时 CURRENT_TIMESTAMP)
该字符串是固定字符加上整数(比如 10)的串联。
此整数必须是范围内的非重复随机数。
CREATE DEFINER=`db`@`%` PROCEDURE `InsertRandom`(IN NumRows INT, IN MinVal INT, IN MaxVal INT)
BEGIN
DECLARE i INT;
DECLARE UniqueId INT(15);
SET i = 1;
START TRANSACTION;
WHILE i <= NumRows DO
SET UniqueId = concat('ABC', MinVal + CEIL(RAND() * (MaxVal - MinVal)));
IF NOT EXISTS (SELECT UNIQUE_ID FROM MY_TABLE WHERE UNIQUE_ID = UniqueId) THEN
INSERT INTO MY_TABLE (`UNIQUE_ID`, `STATE`, `RANGE_ID`) VALUES (UniqueId, 'new', '100');
END IF;
SET i = i + 1;
END WHILE;
COMMIT;
END
每次过程调用的范围 (minVal & maxVal) 将为 100 万。
例如,
CALL InsertRandom(1000000, 10000000,11000000);
table 将每 5 个月清除一次,保留 1 个月的数据,因此我们可以假设在执行此过程时将有大约 500 万条记录,并且 select 内部循环不是最优的,因此请提出替代方法。
(来自评论:)
目标是 table 在给定范围内具有唯一 ID。这些不应该按顺序排列。该范围一次最小为一百万,最大为 10 mill。其中的块将加载到服务器的内存中以供进一步处理。我对有效填充此 table 的选项很感兴趣。
您的存储过程将发现试图填补一些漏洞的瓶颈。
如何预先生成以 MinVal 开头的数字并以随机顺序插入这些数字,看看:
CREATE TABLE numbers(
`UNIQUE_ID` INT,
`STATE` VARCHAR(50),
`RANGE_ID` VARCHAR(50)
);
DELIMITER $$
DROP PROCEDURE IF EXISTS InsertRandom $$
CREATE PROCEDURE InsertRandom (IN NumRows INT, IN MinVal INT)
BEGIN
DECLARE i INT;
DECLARE UniqueId INT;
SET i = 0;
CREATE TEMPORARY TABLE IF NOT EXISTS numbers_tmp(
UNIQUE_ID int,
RAND_VAL double,
INDEX(RAND_VAL)
);
START TRANSACTION;
WHILE i < NumRows DO
INSERT INTO numbers_tmp(UNIQUE_ID,RAND_VAL)
VALUES (i + MinVal ,rand());
SET i = i + 1;
END WHILE;
INSERT INTO numbers (`UNIQUE_ID`, `STATE`, `RANGE_ID`)
SELECT `UNIQUE_ID`, 'new', '100'
FROM numbers_tmp
ORDER BY RAND_VAL; /* <-- THIS */
COMMIT;
DROP TEMPORARY TABLE numbers_tmp;
END $$
DELIMITER ;
CALL InsertRandom(100, 500);
mysql> select * from numbers;
+-----------+-------+----------+
| UNIQUE_ID | STATE | RANGE_ID |
+-----------+-------+----------+
| 575 | new | 100 |
| 523 | new | 100 |
| 560 | new | 100 |
| 537 | new | 100 |
| 526 | new | 100 |
| 549 | new | 100 |
| 598 | new | 100 |
| 552 | new | 100 |
| 555 | new | 100 |
| 581 | new | 100 |
...
至此。也许是一个好主意,只需将值插入您的主 table 并按一些随机插入的数字排序。我不知道。
- 插入一串随机数到table.
- 从中删除 table 当前正在使用的所有号码。
- 当你需要一个号码时,从 table 中取出 'next' 个号码,然后删除它。
- 每晚检查 table 是否 'nearly' 是空的。如果是,请重新运行上述步骤。
略有不同:
设置:
CREATE TABLE Numbers (
id INT UNSIGNED NOT NULL AUTO_INCREMENT,
number INT UNSIGNED NOT NULL
PRIMARY KEY(id),
UNIQUE(number) -- avoids dups, see IGNORE below
) ENGINE = InnoDB;
每晚执行此操作,而不是每月执行一次:
INSERT IGNORE INTO Numbers (number)
SELECT FLOOR(9000000 * RAND() + 1000000) -- for range of 1M to 10M
AS number
FROM MainTable -- any table with lots of rows
HAVING NOT EXISTS( SELECT 1 FROM MainTable
WHERE Numbers.number = MainTable.number )
LIMIT 12345; -- enough to last 3 days, not a month
要获得一个新的,独特的,number
(伪代码):
BEGIN;
$number = SELECT number FROM Numbers LIMIT 1 FOR UPDATE;
DELETE FROM Numbers WHERE number = $number;
COMMIT;
然后继续在 MainTable 中使用 $number。
MySql - innodb_version 5.7.33
我正在处理一个存储过程,该过程将定期调用(假设每月一次)以填充 table,其中一列是字符串列表,另一列是静态值。 table 还有
- ID 列 (AUTO_INCREMENT) 和
- 时间戳列(CURRENT_TIMESTAMP 更新时 CURRENT_TIMESTAMP)
该字符串是固定字符加上整数(比如 10)的串联。 此整数必须是范围内的非重复随机数。
CREATE DEFINER=`db`@`%` PROCEDURE `InsertRandom`(IN NumRows INT, IN MinVal INT, IN MaxVal INT)
BEGIN
DECLARE i INT;
DECLARE UniqueId INT(15);
SET i = 1;
START TRANSACTION;
WHILE i <= NumRows DO
SET UniqueId = concat('ABC', MinVal + CEIL(RAND() * (MaxVal - MinVal)));
IF NOT EXISTS (SELECT UNIQUE_ID FROM MY_TABLE WHERE UNIQUE_ID = UniqueId) THEN
INSERT INTO MY_TABLE (`UNIQUE_ID`, `STATE`, `RANGE_ID`) VALUES (UniqueId, 'new', '100');
END IF;
SET i = i + 1;
END WHILE;
COMMIT;
END
每次过程调用的范围 (minVal & maxVal) 将为 100 万。
例如,
CALL InsertRandom(1000000, 10000000,11000000);
table 将每 5 个月清除一次,保留 1 个月的数据,因此我们可以假设在执行此过程时将有大约 500 万条记录,并且 select 内部循环不是最优的,因此请提出替代方法。
(来自评论:)
目标是 table 在给定范围内具有唯一 ID。这些不应该按顺序排列。该范围一次最小为一百万,最大为 10 mill。其中的块将加载到服务器的内存中以供进一步处理。我对有效填充此 table 的选项很感兴趣。
您的存储过程将发现试图填补一些漏洞的瓶颈。 如何预先生成以 MinVal 开头的数字并以随机顺序插入这些数字,看看:
CREATE TABLE numbers(
`UNIQUE_ID` INT,
`STATE` VARCHAR(50),
`RANGE_ID` VARCHAR(50)
);
DELIMITER $$
DROP PROCEDURE IF EXISTS InsertRandom $$
CREATE PROCEDURE InsertRandom (IN NumRows INT, IN MinVal INT)
BEGIN
DECLARE i INT;
DECLARE UniqueId INT;
SET i = 0;
CREATE TEMPORARY TABLE IF NOT EXISTS numbers_tmp(
UNIQUE_ID int,
RAND_VAL double,
INDEX(RAND_VAL)
);
START TRANSACTION;
WHILE i < NumRows DO
INSERT INTO numbers_tmp(UNIQUE_ID,RAND_VAL)
VALUES (i + MinVal ,rand());
SET i = i + 1;
END WHILE;
INSERT INTO numbers (`UNIQUE_ID`, `STATE`, `RANGE_ID`)
SELECT `UNIQUE_ID`, 'new', '100'
FROM numbers_tmp
ORDER BY RAND_VAL; /* <-- THIS */
COMMIT;
DROP TEMPORARY TABLE numbers_tmp;
END $$
DELIMITER ;
CALL InsertRandom(100, 500);
mysql> select * from numbers;
+-----------+-------+----------+
| UNIQUE_ID | STATE | RANGE_ID |
+-----------+-------+----------+
| 575 | new | 100 |
| 523 | new | 100 |
| 560 | new | 100 |
| 537 | new | 100 |
| 526 | new | 100 |
| 549 | new | 100 |
| 598 | new | 100 |
| 552 | new | 100 |
| 555 | new | 100 |
| 581 | new | 100 |
...
至此。也许是一个好主意,只需将值插入您的主 table 并按一些随机插入的数字排序。我不知道。
- 插入一串随机数到table.
- 从中删除 table 当前正在使用的所有号码。
- 当你需要一个号码时,从 table 中取出 'next' 个号码,然后删除它。
- 每晚检查 table 是否 'nearly' 是空的。如果是,请重新运行上述步骤。
略有不同:
设置:
CREATE TABLE Numbers (
id INT UNSIGNED NOT NULL AUTO_INCREMENT,
number INT UNSIGNED NOT NULL
PRIMARY KEY(id),
UNIQUE(number) -- avoids dups, see IGNORE below
) ENGINE = InnoDB;
每晚执行此操作,而不是每月执行一次:
INSERT IGNORE INTO Numbers (number)
SELECT FLOOR(9000000 * RAND() + 1000000) -- for range of 1M to 10M
AS number
FROM MainTable -- any table with lots of rows
HAVING NOT EXISTS( SELECT 1 FROM MainTable
WHERE Numbers.number = MainTable.number )
LIMIT 12345; -- enough to last 3 days, not a month
要获得一个新的,独特的,number
(伪代码):
BEGIN;
$number = SELECT number FROM Numbers LIMIT 1 FOR UPDATE;
DELETE FROM Numbers WHERE number = $number;
COMMIT;
然后继续在 MainTable 中使用 $number。