Mysql大概1700W大表删除1000W左右数据,发现数据大小和索引大小并没有减少思考
MySQL删除操作其实是假删除
因为近期在重构优化一个业务的时候 发现有一张表(send_log)数据量将近1700W 左右 占用数据大小17G,索引18G左右 而我们的核心应用在使用的时候 会去临时查询这张表 获取一些数据 先不管设计的合不合理吧,因为是维护 不出问题为第一要务 所以想到要物理删除一下表数据 计划把18年1000W左右数据给腾出空间 但运维执行删除操作后 发现情况没有那么理想 行数是少了 但表空间 大小 没降下去
在 InnoDB 中,你的 delete 操作,并不会真的把数据删除,mysql 实际上只是给删除的数据打了个标记,标记为删除,因此你使用 delete 删除表中的数据,表文件在磁盘上所占空间不会变小,我们这里暂且称之为假删除。
Rows原来是1700W 已经delete删除了1000W左右 但dataLength indexLength都没变。。。
在 InnoDB 中,你的 delete 操作,并不会真的把数据删除,mysql 实际上只是给删除的数据打了个标记,标记为删除,因此你使用 delete 删除表中的数据,表文件在磁盘上所占空间不会变小,我们这里暂且称之为假删除。
上面这个是结论,我们可以通过一个例子来验证下。
沿用前面文章中的例子吧,先创建一个存储过程,插入 10w 条数据,然后看下这 10w 条数据占了多大的空间。
- CREATE TABLE `t` (
- `id` int(11) NOT NULL,
- `a` int(11) DEFAULT NULL,
- `b` int(11) DEFAULT NULL,
- PRIMARY KEY (`id`),
- KEY `a` (`a`),
- KEY `b` (`b`)
- ) ENGINE=InnoDB;
- #定义分割符号,mysql 默认分割符为分号;,这里定义为 //
- #分隔符的作用主要是告诉mysql遇到下一个 // 符号即执行上面这一整段sql语句
- delimiter //
- #创建一个存储过程,并命名为 testData
- create procedure testData()
- #下面这段就是表示循环往表里插入10w条数据
- begin
- declare i int;
- set i=1;
- while(i<=100000)do
- insert into t values(i, i, i);
- set i=i+1;
- end while;
- end // #这里遇到//符号,即执行上面一整段sql语句
- delimiter ; #恢复mysql分隔符为;
- call testData(); #调用存储过程
- #下面这两条命令可以查看表文件所占空间大小
- mysql> use information_schema;
- Reading table information for completion of table and column names
- You can turn off this feature to get a quicker startup with -A
- Database changed
- mysql> select concat(round(sum(DATA_LENGTH/1024/1024),2),'M') from tables where table_schema='test' AND table_name='t';
- +-------------------------------------------------+
- | concat(round(sum(DATA_LENGTH/1024/1024),2),'M') |
- +-------------------------------------------------+
- | 3.52M |
- +-------------------------------------------------+
- 1 row in set (0.04 sec)
可以看到 10w 条数据在 mysql 中占用了 3.52M 大小的空间,那么我们执行删除命令 delete from t,再看看呢。
- #先删除表所有数据,再重新查看表文件大小
- mysql> delete from t;
- Query OK, 100000 rows affected (0.46 sec)
- mysql> use information_schema;
- Reading table information for completion of table and column names
- You can turn off this feature to get a quicker startup with -A
- Database changed
- mysql> select concat(round(sum(DATA_LENGTH/1024/1024),2),'M') from tables where table_schema='test' AND table_name='t';
- +-------------------------------------------------+
- | concat(round(sum(DATA_LENGTH/1024/1024),2),'M') |
- +-------------------------------------------------+
- | 3.52M |
- +-------------------------------------------------+
- 1 row in set (0.00 sec)
从结果可以发现表数据被清空后,表所占空间大小并没有变化,这就验证了上面的结论,delete 操作并没有真正删除数据,表的空间并没有被释放。
这些被删除的记录行,只是被标记删除,是可以被复用的,下次有符合条件的记录是可以直接插入到这个被标记的位置的。
比如我们在 id 为 300-600 之间的记录中删除一条 id=500 的记录,这条记录就会被标记为删除,等下一次如果有一条 id=400 的记录要插入进来,那么就可以复用 id=500 被标记删除的位置,这种情况叫行记录复用。
还有一种情况是数据页复用,就是指整个数据页都被标记删除了,于是这整个数据页都可以被复用了,和行记录复用不同的是,数据页复用对要插入的数据几乎没有条件限制。
还以上面那个插入为例,假如要插入的记录是 id=1000,那么就不能复用 id=500 这个位置了,但如果有一整个数据页可复用的话,那么无论 id 值为多少都可以被复用在这个页上。
这些被标记删除的记录,其实就是一个空洞,有种占着茅坑不拉屎的感觉,浪费空间不说,还会影响查询效率。
因为你要知道,mysql 在底层是以数据页为单位来存储和读取数据的,每次向磁盘读一次数据就是读一个数据页,然而每访问一个数据页就对应一次磁盘 IO 操作,磁盘 IO 相对内存访问速度是相当慢的。
所以你想想,如果一个表上存在大量的数据空洞,原本只需一个数据页就保存的数据,由于被很多空洞占用了空间,不得不需要增加其他的数据页来保存数据,相应的,mysql 在查询相同数据的时候,就不得不增加磁盘 IO 操作,从而影响查询速度。
其实不仅仅是删除操作会造成数据空洞,插入和更新同样也会造成空洞,这里就不细说了,你知道就行。
因此,一个数据表在经过大量频繁的增删改之后,难免会产生数据空洞,浪费空间并影响查询效率,通常在生产环境中会直接表现为原本很快的查询会变得越来越慢。
对于这种情况,我们通常可以使用下面这个命令就能解决数据空洞问题。
- optimize table t
这个命令的原理就是重建表,就是建立一个临时表 B,然后把表 A(存在数据空洞的表) 中的所有数据查询出来,接着把数据全部重新插入到临时表 B 中,***再用临时表 B 替换表 A 即可,这就是重建表的过程。
我们再来试验一下,看看效果。
- mysql> optimize table t;
- +--------+----------+----------+-------------------------------------------------------------------+
- | Table | Op | Msg_type | Msg_text |
- +--------+----------+----------+-------------------------------------------------------------------+
- | test.t | optimize | note | Table does not support optimize, doing recreate + analyze instead |
- | test.t | optimize | status | OK |
- +--------+----------+----------+-------------------------------------------------------------------+
- 2 rows in set (0.39 sec)
- mysql> use information_schema;
- Reading table information for completion of table and column names
- You can turn off this feature to get a quicker startup with -A
- Database changed
- mysql> select concat(round(sum(DATA_LENGTH/1024/1024),2),'M') from tables where table_schema='test' AND table_name='t';
- +-------------------------------------------------+
- | concat(round(sum(DATA_LENGTH/1024/1024),2),'M') |
- +-------------------------------------------------+
- | 0.02M |
- +-------------------------------------------------+
- 1 row in set (0.00 sec)
可以看到表文件大小已经变成 0.02M了,说明表空间被释放了,这个 0.02M 应该是定义表结构文件的大小了。
另外下面这个命令也可以实现重建表,可以达到跟上面一样的效果,而且推荐大家使用下面这个命令,大家可以试试。
- alter table t engine=InnoDB
注意本文内容是基于 InnoDB 引擎,对于其他引擎可能存在一些差异。原创不易,如果文章对你有启发,就点个在看吧,有疑问也可以在下面留言交流,也可以与我私信交流,感谢支持。
Mysql大概1700W大表删除1000W左右数据,发现数据大小和索引大小并没有减少思考的更多相关文章
- Mysql千万级大表优化
Mysql的单张表的最大数据存储量尚没有定论,一般情况下mysql单表记录超过千万以后性能会变得很差.因此,总结一些相关的Mysql千万级大表的优化策略. 1.优化sql以及索引 1.1优化sql 1 ...
- 如何优化MySQL千万级大表
很好的一篇博客,转载 如何优化MySQL千万级大表 原文链接::https://blog.csdn.net/yangjianrong1985/article/details/102675334 千万级 ...
- MySQL千万级大表优化解决方案
MySQL千万级大表优化解决方案 非原创,纯属记录一下. 背景 无意间看到了这篇文章,作者写的很棒,于是乎,本人自私一把,把干货保存下来.:-) 问题概述 使用阿里云rds for MySQL数据库( ...
- 查询mysql数据库表的信息(表大小、数据大小、索引大小)
select * from information_schema.TABLES where information_schema.TABLES.TABLE_SCHEMA='databasename' ...
- 【优化】MySQL千万级大表优化解决方案
问题概述 使用阿里云rds for MySQL数据库(就是MySQL5.6版本),有个用户上网记录表6个月的数据量近2000万,保留最近一年的数据量达到4000万,查询速度极慢,日常卡死.严重影响业务 ...
- Mysql千万级大表优化策略
1.优化sql以及索引 1.1优化sql 1.有索引但未被用到的情况(不建议) (1)避免like的参数以通配符开头时 尽量避免Like的参数以通配符开头,否则数据库引擎会放弃使用索引而进行全表扫描. ...
- MySQL 上亿大表优化实践
目录 背景 分析 select xxx_record语句 delete xxx_record语句 测试 实施 索引优化后 delete大表优化为小批量删除 总结 背景 XX实例(一主一从)xxx告警中 ...
- mysql 如何给大表添加字段
1. 能不加字段就不要加, 能不修改字段就不要修改, 能不删除字段就不要删除, 等等为什么要删除字段呢? 如果没事,不要蛋疼的找事. 实际上,我们那次更新失败后, 我们并没有增加那个字段, 然后我们一 ...
- oracle 大表删除数据后,回收空间的问题。
在oracle中由于表结构设计不合理或者需要清楚老数据的时候,经常需要对大表数据进行清理. 一般有一下几种方法: 1. 删除大部分数据,留下小部分数据.我们可以把需要保留的数据转移到别的表,然后再把大 ...
随机推荐
- 在 Spring Boot 中使用 Flyway
一.Flyway 介绍 Flyway 是一个开源的数据库迁移工具,MySQL, SQL Server, Oracle 等二十多种数据库 在 Flyway 中数据库的所有改变均称为迁移(migratio ...
- 老猿学5G扫盲贴:3GPP规范中与计费相关的主要规范文档列表及下载链接
专栏:Python基础教程目录 专栏:使用PyQt开发图形界面Python应用 专栏:PyQt入门学习 老猿Python博文目录 老猿学5G博文目录 在<老猿学5G扫盲贴:3GPP规范中部分与计 ...
- 老猿学5G随笔:5G网元功能体NF以及NF之间的两种接口--服务化接口和参考点
一.5G功能体之间的接口类型 5G不同功能体之间提供了两种接口: 服务化接口:Service-basedinterface,这个是类似微服务化架构的服务注册和服务发现来实现的功能体对外暴露的接口,这种 ...
- Fiddle抓包应用概述
抓包: 抓包(packet capture)就是将网络传输发送与接收的数据包进行截获.重发.编辑.转存等操作,也用来检查网络安全.抓包也经常被用来进行数据截取等.说简单点就是抓取前端发送给服务器的数据 ...
- 在CTF比赛中,命令中空格被过滤的解决方法
1.linux {cat,flag.txt} cat${IFS}flag.txt cat$IFS$9flag.txt cat<flag.txt cat<>flag.txt kg=$' ...
- python中的万能密码
在php中,我们经常见到这样的语句 if(isset($_GET['id'])) { $id=$_GET['id']; //logging the connection parameters to a ...
- neo4j数据库数据转移,从阿里云转移到windows服务器
1.从阿里云迁移neo4j时需停掉neo4j数据库,在neo4j的bin目录下输入 ./neo4j stop 2.将数据备份到一个文件中 ./neo4j-admin dump --database=g ...
- 【题解】P3629 [APIO2010]巡逻
link 题意 有 \(n\) 个村庄,编号为 \(1, 2, ..., n\) .有 \(n – 1\) 条道路连接着这些村 庄,从任何一个村庄都可以到达其他任一个村庄.道路长度均为 1. 巡警车每 ...
- 小白都看得懂的Javadoc上手教程
Javadoc是什么 官方回答: Javadoc is a tool for generating API documentation in HTML format from doc comments ...
- mysqldump: [ERROR] unknown variable 'database=test'.
背景: 导出数据库test库的test表数据出现报错信息:mysqldump: [ERROR] unknown variable 'database=test'. 原来是my.cnf参数文件配置了以下 ...
