这篇文章,我将从 innodb 存储空间分配、delete 对性能的影响 以及 最佳实践建议 三个角度,逐步剖析为什么不推荐直接使用 delete 删除大批量数据。
一、innodb 存储架构概览
逻辑结构
- 表空间 (tablespace)
- 段 (segment)
- extent(区):每个 extent 包含 32 个页 (page)。
- 页 (page):innodb 的最小 i/o 单位,默认 16kb。
物理结构
- 数据文件 (
.ibd/ibdata1):存储表、索引和字典元数据。 - 日志文件 (
ib_logfile*):记录页的修改,用于崩溃恢复。
- 数据文件 (
extent 自动扩展策略
- 初始分配为 1 个 extent
- 若总表空间 < 32mb,每次 +1 个 extent
- 大于 32mb,则每次 +4 个 extent
二、innodb 表空间类型
- 系统表空间 (
ibdata1),保存内部字典等元数据。 - 独立表空间(
innodb_file_per_table=on),每个表一个.ibd文件。 - undo 表空间,存储 mvcc 的回滚段。
从 mysql 8.0 起,支持自定义通用表空间:
create tablespace tbs_hot add datafile '/hot_data/tbs_hot01.dbf' initial_size = 10g autoextend_size = 1g max_size = 32g engine = innodb;
冷热分离
- 热数据 (用户、订单) → ssd 表空间
- 冷数据 (日志、归档) → hdd 表空间
三、实际演示:空间分配 & 回收
1. 创建空表
create table user ( id bigint primary key auto_increment, name varchar(20) not null, age tinyint not null, gender char(1) not null, phone varchar(16) not null, create_time datetime not null default current_timestamp, update_time datetime not null ) engine=innodb;
$ ls -lh user.ibd -rw-r----- 1 mysql mysql 96k nov 6 12:48 user.ibd
说明:空表首个 extent(32 页)占用约 96kb。
2. 插入 10w 条
call insert_user_data(100000); -- 自定义存储过程批量插入
$ ls -lh user.ibd -rw-r----- 1 mysql mysql 14m nov 6 10:58 user.ibd
- 分配了更多 extent,总计约 896 页(≈14mb)。
3. delete 50k 条
delete from user limit 50000;
$ ls -lh user.ibd -rw-r----- 1 mysql mysql 14m nov 6 13:22 user.ibd
- 空间未释放,仍然保持 14mb。
- innodb 只打 删除标记 (delete_flag),不进行物理回收。
四、delete 对查询性能的影响
初始查询(100w 条+索引)
select id, age, phone from user where name like 'lyn12%';
- 执行时间:30ms
- cost:10.499
- 物理读:7,868,409
- 逻辑读:7,855,239
- 扫描行:22,226
- 返回行:11,111
删除 50w 后再查
delete from user limit 500000; analyze table user;
select id, age, phone from user where name like 'lyn12%';
- 执行时间:50ms
- cost:10.499
- 物理/逻辑读:同上
- 扫描行:22,226
- 返回行:0
结论:大表删除半数数据后,查询成本和 i/o 基本不变,只是返回结果不同。
五、为什么不推荐大批量 delete
空间不回收
.ibd文件不缩小,extents 保留
页碎片
- 随机删除/更新导致页分裂、空洞增加
后续写入难用
- 删除标记页只有在插入更小行时才会重用
碎片回收代价高
alter table … engine=innodb:全表重建,i/o 密集、阻塞 dml
六、最佳实践与优化建议
1. 逻辑删除(标记删除)
alter table user add column is_deleted tinyint not null default 0; update user set is_deleted = 1 where id = 123456; -- 查询时统一过滤: select * from user where is_deleted = 0 and name like 'lyn12%';
- 优点:无需大规模物理删除,不引入碎片。
2. 分区归档
- 按时间分区,定期交换分区、归档历史数据。
- 在线 ddl + 元数据交换:零或极低阻塞。
alter table ota_order_bak exchange partition p202301 with table ota_order_mid;
通过分区操作,瞬间移动大块数据,无需耗时 delete。
3. 权限隔离
- 对业务账号仅授
select, insert, update,禁用 delete 权限。 - 拆分微服务数据库,每个服务独立账号,避免误删。
create user 'svc_user'@'%' identified by '…'; grant select, insert, update on db_user.*;
4. 专用归档系统
- 对冷数据、历史日志,可考虑 clickhouse、elasticsearch 存储与清理。
- 利用 ttl 自动淘汰旧数据。
七、总结
- delete 大量数据不会缩减空间,反倒留下一堆碎片,影响索引与性能。
- 逻辑删除 + 分区归档 才是大规模数据清理的良方。
- 结合 权限控制、专用归档系统 (clickhouse 等),才能既保证性能,也不丢失历史记录。
到此这篇关于为什么说mysql不建议使用delete删除数据的文章就介绍到这了,更多相关mysql不使用delete删除数据内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
发表评论