协助优化以下 MySql 程序

Assistance with optimizing below MySql procedure

MySql - innodb_version 5.7.33

我正在处理一个存储过程,该过程将定期调用(假设每月一次)以填充 table,其中一列是字符串列表,另一列是静态值。 table 还有

该字符串是固定字符加上整数(比如 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 并按一些随机插入的数字排序。我不知道。

  1. 插入一串随机数到table.
  2. 从中删除 table 当前正在使用的所有号码。
  3. 当你需要一个号码时,从 table 中取出 'next' 个号码,然后删除它。
  4. 每晚检查 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。