当删除的数据量过大时,可能会导致实例卡顿、HA切换、实例抖动或复制延迟等问题。本文将介绍解决这些问题的方法。
问题描述
在数据库运维中,删除表中部分数据、清空表或删除整个表是常见的操作。对于小规模的数据集,这些操作对数据库实例的影响可以忽略。然而,当涉及数百万乃至上亿行记录,或者数据文件达到几百GB甚至数TB时,这类操作可能会引发如下严重问题。
实例卡顿或HA切换
问题1:Binlog提交阶段,在单个大事务中删除大量数据时,生成的临时Binlog文件可能达到数十GB甚至数百GB。提交这些Binlog时会阻塞其他写事务,导致短暂卡顿或长时间延迟,严重时会因HA探测超时引发主备切换。
问题2:AHI清理阶段,InnoDB需要遍历Buffer Pool清理相关AHI记录,此过程需持有全局锁
dict_sys->mutex
,耗时较长。详情请参见MySQL官方说明(MySQL Bugs: #91977: Dropping Large Table Causes Semaphore Waits; No Other Work Possible)。问题3:文件删除阶段,大表文件的删除可能导致实例瞬间及后续一段时间内IO资源被完全占用,影响正常请求的处理。
实例抖动
问题4:文件删除阶段,InnoDB会遍历Buffer Pool以清理该表的相关页面。若buffer_pool_size非常大,清理过程将耗时较长,会长时间独占单个Buffer Pool的Mutex,阻碍其他事务正常读写页面,进而引起实例性能波动。
解决方案
参数调整
参数配置 | 说明 | 支持版本 |
binlog_cache_free_flush = ON | 解决了问题1:大Binlog文件写入阻塞的问题,详情请参见AliSQL自研功能Binlog Cache Free Flush。 |
|
innodb_rds_drop_ahi_ahead = ON | 解决了问题2:清理AHI的问题,AliSQL自研功能。 |
|
innodb_data_file_purge = ON innodb_data_file_purge_interval=100 innodb_data_file_purge_max_size=512 | 解决问题3:IO资源被完全占用问题,详情请参见AliSQL自研功能Purge Large File Asynchronously 重要 建议根据最大IOPS的50%来调整interval和max_size参数。 | 版本不限 |
innodb_rds_faster_ddl = ON | 解决了问题4:清理Buffer Pool导致实例抖动的问题,详情请参见AliSQL自研功能Faster DDL。 MySQL官方在8.0.23解决了此问题,详情请参见MySQL官方说明(WL#14100: InnoDB: Faster truncate/drop table space)。 |
|
为防止误删,可启用AliSQL自研的Recycle Bin功能。该功能会暂时将删除的表移至回收站,并允许设置保留时间,便于数据恢复。
删除数据操作
删除部分数据
分批操作,建议单次删除的数据量不超过一万行,不要一次性删除大量数据。
删除所有数据
MySQL 5.7版本推荐使用
RENAME + DROP
操作未采用
TRUNCATE
是因为其在5.7中通过清空原表空间实现,而非重建新表。# 重建一张相同的表 CREATE TABLE t1_new LIKE t1; # 检查重建表的表定义是否符合预期 SHOW CREATE TABLE t1_new; # 新表和老表交换 RENAME TABLE t1 TO t1_bak, t1_new TO t1; # 删除原表 DROP TABLE t1_bak; # 检查表空间文件清理进展 SELECT * FROM information_schema.innodb_purge_files;
说明在MySQL 5.7版本中,DDL操作不具备原子性,实例崩溃可能导致文件残留。
RENAME
过程可能触发重命名文件失败导致实例崩溃问题,详情请参见MySQL官方说明(bug#108087 InnoDB:crash because of failure to rename file in DDL),此问题已在AliSQL 5.7 20220731及更高版本中修复。
MySQL 8.0版本推荐使用
RENAME + DROP
或TRUNCATE
操作使用
RENAME + DROP
参考如下SQL:# 重建一张相同的表 CREATE TABLE t1_new LIKE t1; # 检查重建表的表定义是否符合预期 SHOW CREATE TABLE t1_new; # 提前锁定新表和老表 lock TABLE t1 write, t1_new write; # 新表和老表交换 RENAME TABLE t1 TO t1_bak, t1_new TO t1; # 释放表锁 unlock TABLE; # 删除原表 DROP TABLE t1_bak; # 检查表空间文件清理进展 SELECT * FROM information_schema.innodb_purge_files;
说明RENAME
过程可能触发重命名文件失败导致实例崩溃问题,详情请参见MySQL官方说明(bug#108087 InnoDB:crash because of failure to rename file in DDL),此问题已在AliSQL 5.7 20220731及更高版本中修复。MySQL 8.0增加了
lock TABLE
,可以选择是否执行,具体参考8.0.13新增的能力8.0 rename table。RENAME + DROP
避免了TRUNCATE
过程中潜在的卡顿情况。
使用
TRUNCATE
,参考如下SQL:# truncate表 TRUNCATE TABLE t1; # 检查表空间文件清理进展 SELECT * FROM information_schema.innodb_purge_files;
删除大表
直接使用DROP。
# 删除表
DROP TABLE t1;
# 检查表空间文件清理进展
SELECT * FROM information_schema.innodb_purge_files;