清理100M+行的数据库表中不必要的数据

问题描述 投票:0回答:1

我有一个名为 Controllers 的表,其中记录了一些具有唯一 IMEI 号码的设备。

田野 类型 钥匙 默认 额外
id int PRI 自动增量
设备名称 varchar(200)
设备位置 文字
imei_number varchar(15)
电压 十进制(10,2) 0.00
当前 十进制(10,2) 0.00
温度 十进制(10,2) 0.00
故障 int 0
创建于 时间戳 当前_时间戳
更新于 时间戳 当前_时间戳 更新 CURRENT_TIMESTAMP 时为 DEFAULT_GENERATED

我有另一个名为Logs的表,它存储每个设备的日志。每个设备每 10 秒插入一次日志,现在 RMSLogs 表有 1 亿多行

田野 类型 钥匙 默认 额外
id int PRI 自动增量
imei_number varchar(15)
电压 十进制(10,2) 0.00
当前 十进制(10,2) 0.00
温度 十进制(10,2) 0.00
故障 小小 0
创建于 时间戳 当前_时间戳
更新于 时间戳 当前_时间戳 更新 CURRENT_TIMESTAMP 时为 DEFAULT_GENERATED

我想删除与imei_number的时间差小于10分钟的行。

我尝试了以下步骤:

第1步 使用简单的删除查询。

DELETE t1
FROM RMSLogs t1
JOIN RMSLogs t2 ON t1.id = t2.id + 1
WHERE TIMESTAMPDIFF(MINUTE, t2.created_at, t1.created_at) < 10;

当我执行查询时,它影响了大约不超过 8500 行,并且花费了大约 2 分钟。

第2步 使用存储过程。

DELIMITER //

CREATE PROCEDURE DeleteLogsLessThan10Minutes()
BEGIN
    DECLARE done INT DEFAULT FALSE;
    DECLARE imei_number VARCHAR(255);
    DECLARE curr_timestamp DATETIME;
    DECLARE previous_timestamp DATETIME;
    
    -- Declare cursor to fetch distinct IMEI numbers from RmsController table
    DECLARE cur CURSOR FOR 
        SELECT imei_number FROM RMSController;
    
    -- Declare continue handler for cursor
    DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
    
    OPEN cur;
    
    read_loop: LOOP
        FETCH cur INTO imei_number;
        IF done THEN
            LEAVE read_loop;
        END IF;
        
        -- Temporary table to hold log entries for the current IMEI number
        CREATE TEMPORARY TABLE IF NOT EXISTS Temp_Logs (
            created_at DATETIME
        );
        
        -- Insert log entries for the current IMEI number into the temporary table
        INSERT INTO Temp_Logs (created_at)
        SELECT created_at
        FROM RMSLog
        WHERE imei_number = imei_number
        ORDER BY created_at;
        
        -- Initialize variables for timestamp comparison
        SET previous_timestamp = NULL;
        
        -- Loop through the log entries for the current IMEI number
        WHILE (SELECT COUNT(*) FROM Temp_Logs) > 0 DO
            SELECT MIN(created_at) INTO curr_timestamp FROM Temp_Logs;
            
            IF previous_timestamp IS NOT NULL AND TIMESTAMPDIFF(MINUTE, previous_timestamp, curr_timestamp) < 10 THEN
                -- Delete the log entry if the time difference is less than 10 minutes
                DELETE FROM RMSLog
                WHERE imei_number = imei_number AND created_at = curr_timestamp;
            END IF;
            
            SET previous_timestamp = curr_timestamp;
            
            -- Remove the processed log entry from the temporary table
            DELETE FROM Temp_Logs WHERE created_at = curr_timestamp;
        END WHILE;
        
        -- Drop the temporary table
        DROP TEMPORARY TABLE IF EXISTS Temp_Logs;
        
    END LOOP;
    
    CLOSE cur;
    
END //

DELIMITER ;

当我执行此存储过程时,导致超时。

mysql performance
1个回答
0
投票

要清除数据,请使用

PRIMARY KEY
以 1000 块为单位浏览表格。对于每 1000 块,删除可以删除的内容。更多:https://mysql.rjweb.org/doc.php/deletebig

对于桌子的其他缩小,

  • 在可行的情况下使用较小的数据类型。当然,温度永远不会高于 99999999.99!这需要 5 个字节。
    DECIMAL(5,2)
    只需 3 个字节。
  • imei_number
    可能是
    DECIMAL(15,0)
    ,它需要 7 个字节,而不是 `VARCHAR(...),它需要 17 个字节。
  • 正常化。我猜
    location
    被重复了很多次。
  • 你真的需要
    updated_at
    吗? (
    TIMESTAMP
    为 5 个字节。)
  • 更多:https://mysql.rjweb.org/doc.php/mysql_sensor

如果您仅保存最后 N 天的数据,请考虑使用 分区 作为删除旧数据的有效方法。

© www.soinside.com 2019 - 2024. All rights reserved.