【问题标题】:Assistance with optimizing below MySql procedure协助优化以下MySql程序
【发布时间】:2021-06-25 22:07:47
【问题描述】:

MySql - innodb_version 5.7.33

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

  • ID 列 (AUTO_INCREMENT) 和
  • 时间戳列(CURRENT_TIMESTAMP ON UPDATE 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);

该表将在 5 个月内清除一次,保留 1 个月的数据,因此我们可以假设在执行此过程时大约有 500 万条记录,并且 select inside loop 的使用不是最佳的,所以请建议一种替代方法。

(来自评论:)

目标是在给定范围内创建一个具有唯一 ID 的表。这些不应该是按顺序排列的。范围在时间最小为一百万,最大为 10 毫米。其中的块将被加载到服务器的内存中以进行进一步处理。我对有效填充此表的选项感兴趣。

【问题讨论】:

  • 转储选择并使用插入忽略?
  • 太好了,谢谢@Shadow。这真的很有帮助。相应地更新了程序。只是它与定义为自动增量的 ID 列混淆。有差距。
  • 那么如果自增列有间隙怎么办?这很正常!
  • 备份。请说明目标,而不是问“如何解决这个设计”。
  • 目标是在给定范围内创建一个具有唯一 ID 的表。这些不应该是按顺序排列的。范围在时间最小为一百万,最大为 10 毫米。其中的块将被加载到服务器内存中以进行进一步处理。我对有效填充此表的选项感兴趣。如原始问题所述,该表将定期清除。提前感谢您的帮助。

标签: mysql stored-procedures random


【解决方案1】:

您的存储过程将发现一个试图填补一些漏洞的瓶颈。 怎么预生成以 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      |
...     

此时。也许是一个好主意,只需在主表中插入值并按一些 rand 插入的数字排序。我不知道。

【讨论】:

  • (有更快的方法来生成整数表,例如numbers_tmp。)
  • 感谢伊万的努力。这确实避免了我在查询中的最小和最大范围之间的序列间隙。感谢您的努力。
  • @RickJames 请给点提示?所以我可以用它来做进一步的即兴创作。谢谢。
【解决方案2】:
  1. 将一堆随机数插入表格中。
  2. 从该表中删除当前正在使用的所有号码。
  3. 当您需要一个数字时,从该表中取出“下一个”数字,然后将其删除。
  4. 每晚检查一下桌子是否“几乎”是空的。如果是,请重新运行上述步骤。

略有不同:

设置:

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。

【讨论】:

    猜你喜欢
    • 2018-03-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多