优化大记录删除流程

Dan*_*nly 5 mysql performance stored-procedures optimization query-performance

我正在创建一个反映我们生产数据库的数据库,但更轻且匿名 - 用于本地开发目的。

为了确保我们有足够的数据可供工程团队使用,我将删除所有日期设置updated_at为一年多前的客户。简单的过程是保留新用户,但保留旧用户或不活跃用户。

为此,我创建了一个存储过程。

DELIMITER //
CREATE PROCEDURE delete_old_customers()
BEGIN
    SET @increment = 0;
    customer_loop: LOOP

        DELETE FROM customers 
        WHERE id BETWEEN @increment AND @increment+999
        AND updated_at < DATE_SUB(CURRENT_DATE(), INTERVAL 1 YEAR);

        IF @increment > (SELECT MAX(id) FROM customers) THEN
            LEAVE customer_loop;
        END IF;

        SET @increment = @increment + 1000;

    END LOOP customer_loop;
END //
DELIMITER ;

CALL delete_old_customers();

DROP PROCEDURE delete_old_customers;
Run Code Online (Sandbox Code Playgroud)

因此,此过程将删除分批分成 1000 个组,并一直运行,直到没有更多客户需要处理。

我运行这样的程序:

mysql "$MYSQLOPTS" devdb < ./queries/customer.sql
Run Code Online (Sandbox Code Playgroud)

其中$MYSQLOPTS指的是具有以下选项的 my.cnf 文件:

[mysqld]
innodb_buffer_pool_size = 6G
innodb_log_buffer_size = 256M
innodb_log_file_size = 1G
innodb_thread_concurrency = 0
innodb_write_io_threads = 64
innodb_flush_log_at_trx_commit = 0
query_cache_size = 0
Run Code Online (Sandbox Code Playgroud)

问题是,由于该表具有 FK 和引用,此过程可能需要长达 3 小时才能删除约 80 万用户;当然,随着时间的推移,这种情况只会越来越严重。

它在四核、8GB RAM、Digital Ocean Droplet 上运行;所以我的工作手段有限。

因此,鉴于此,我很想有机会开始优化此过程以提高其速度,但我不确定从哪里开始。我也愿意接受其他方法来实现相同的目标。

Kon*_*bas 1

我更喜欢下一个策略:在每个插入记录上填充表的存储例程也会删除一些过期的记录。这看起来像这样:

BEGIN
-- lot of code --
INSERT INTO table ...
-- lot of code --
DELETE FROM table AS w WHERE w.expire < NOW() LIMIT 3;
END
Run Code Online (Sandbox Code Playgroud)

插入/删除比率设置为 1:3,只是为了确保即使输入数据速率由于每日/每周/每月的波动而变低,我也能获得合理的删除率。对于过期记录数量较少的已建立基地来说这是可以接受的。如果您想执行初始清理,那么您必须将 值设置LIMIT为不会损害服务器性能的值。

如果您的传入数据速率较低,那么您可以临时创建特殊例程:

CREATE PROCEDURE table_cleanup()    
BEGIN
main: REPEAT
  DELETE FROM table AS w WHERE w.expire < NOW() LIMIT 1000;
  UNTIL row_count() = 0 END REPEAT main;
END
Run Code Online (Sandbox Code Playgroud)

巨大的DELETE会被分成一系列小的,暂时无法锁定桌子。