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 上运行;所以我的工作手段有限。
因此,鉴于此,我很想有机会开始优化此过程以提高其速度,但我不确定从哪里开始。我也愿意接受其他方法来实现相同的目标。
我更喜欢下一个策略:在每个插入记录上填充表的存储例程也会删除一些过期的记录。这看起来像这样:
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
会被分成一系列小的,暂时无法锁定桌子。
归档时间: |
|
查看次数: |
4358 次 |
最近记录: |