mysql删除操做实际上是假删除

在 InnoDB 中,你的 delete 操做,并不会真的把数据删除,mysql 实际上只是给删除的数据打了个标记,标记为删除,所以你使用 delete 删除表中的数据,表文件在磁盘上所占空间不会变小,咱们这里暂且称之为假删除mysql

上面这个是结论,咱们能够经过一个例子来验证下。sql

沿用前面文章中的例子吧,先建立一个存储过程,插入 10w 条数据,而后看下这 10w 条数据占了多大的空间。bash

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,再看看呢。ui

#先删除表全部数据,再从新查看表文件大小
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 操做并无真正删除数据,表的空间并无被释放。this

这些被删除的记录行,只是被标记删除,是能够被复用的,下次有符合条件的记录是能够直接插入到这个被标记的位置的。spa

好比咱们在 id 为 300-600 之间的记录中删除一条 id=500 的记录,这条记录就会被标记为删除,等下一次若是有一条 id=400 的记录要插入进来,那么就能够复用 id=500 被标记删除的位置,这种状况叫行记录复用。code

还有一种状况是数据页复用,就是指整个数据页都被标记删除了,因而这整个数据页均可以被复用了,和行记录复用不一样的是,数据页复用对要插入的数据几乎没有条件限制。orm

还以上面那个插入为例,假如要插入的记录是 id=1000,那么就不能复用 id=500 这个位置了,但若是有一整个数据页可复用的话,那么不管 id 值为多少均可以被复用在这个页上。内存

这些被标记删除的记录,其实就是一个空洞,有种占着茅坑不拉屎的感受,浪费空间不说,还会影响查询效率。get

由于你要知道,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 引擎,对于其余引擎可能存在一些差别。原创不易,若是文章对你有启发,就点个赞吧,有疑问也能够在下面留言交流,也能够与我私信交流,感谢支持。

相关文章
相关标签/搜索