mysql删除操作其实是假删除问题
作者:【谭小谭】 发布时间:2024-01-27 23:15:50
mysql删除操作其实是假删除
在 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 引擎,对于其他引擎可能存在一些差异。
来源:https://blog.csdn.net/samll_snail/article/details/90370201


猜你喜欢
- 本文代码重点在于演示Python扩展库matplotlib.pyplot中fill_between()函数的用法。import numpy
- 本文实例为大家分享了Python实现GUI学生信息管理系统的具体代码,供大家参考,具体内容如下项目环境: 软件环境: &
- 如下所示:<!DOCTYPE html><html><head lang="en">
- pytorch的广播语义(broadcasting semantics),和numpy的很像,所以可以先看看numpy的文档:1、什么是广播
- 01. 应用场景及定义方式应用场景在实际开发中,对象 的 某些属性或方法 可能只希望 在对象的内部被使用,而 不希望在外部被访问到私有属性
- 在我们关于SQL服务器安全系列的这文章里,我们的目标是向你提供安全安装SQL服务器所需要的工具和信心,这样的话,你有价值的数据就会受到保护,
- 以下内容在python中完全可以接受:1、将整数的字符串表示形式传递给 int2、将float的字符串表示形式传递给 float但是,如果你
- 之前我写过一篇文章介绍如何实现中国站长站的文章干扰码功能:《谈中国站长站的文章干扰码实现方法》 首发在asp之家。如果大家有兴趣可以先看看。
- Exec sp_droplinkedsrvlogin ZYB,Null --删除映射(录与链接服务器上远程登录之间的映射) Exec sp_
- 将Django与其他现有认证系统的用户名和密码或者认证方法进行整合是可以办到的。例如,你所在的公司也许已经安装了LDAP,并且为每一个员工都
- 0x01介绍PySocks使您可以通过SOCKS和HTTP代理服务器发送流量。它是SocksiPy的现代分支,具有错误修复和其他功能。0x0
- (1)二进制数据类型 二进制数据包括 Binary、Varbinary 和 ImageBinary 数据类型既可以是固定长度的(Binary
- UDP 套接字是可以使用 connect 系统调用连接到指定的地址的。从此以后,这个套接字只会接收来自这个地址的数据,而且可以使用 send
- 大家好,我是海拥,在今天的博客中,我们将讨论 Python 中简化代码的技巧。我清楚地记得当我选择学习 python 时,最令我震惊的是它的
- 昨晚今晚写了两晚,总算把Py Port Scanner 写完了,姑且称之为0.1版本,算是一个Python多线程端口扫描工具。水平有限,实话
- 网页开发人员常常希望能够了解并掌握多种语言,结果是,学习一门语言的所有内容是棘手的,但是却很容易发现你并没有完全利用那些比较特殊却很有用的标
- 在Asp中如何得到所有表单的名称跟对应的值。其实,这个问题很简单,但是可能还是有很多人不知道该怎么做,所以特地写下来,仅供参考。在Asp程序
- 本文实例为大家分享了Python KNN分类算法的具体代码,供大家参考,具体内容如下KNN分类算法应该算得上是机器学习中最简单的分类算法了,
- 本文实例讲述了Python矩阵常见运算操作。分享给大家供大家参考,具体如下:python的numpy库提供矩阵运算的功能,因此我们在需要矩阵
- 我就废话不多说了,直接上代码吧!import turtleturtle.pensize(5)turtle.pencolor("ye