在InnoDB中,你的删除操作实际上并没有删除数据。mysql其实只是把删除的数据标记为删除,所以你用delete删除表中的数据,表文件在磁盘上占用的空间并不会减少,我们这里暂且称之为假删除。以上就是结论,我们可以通过一个例子来验证。我们还是按照上一篇文章的例子,先创建一个存储过程,插入10w条数据,然后看看这10w条数据占用了多少空间。创建表`t`(`id`int(11)NOTNULL,`a`int(11)DEFAULTNULL,`b`int(11)DEFAULTNULL,PRIMARYKEY(`id`),KEY`a`(`a`),KEY`b`(`b`))ENGINE=InnoDB;#定义分隔符,mysql默认分隔符是分号;,这里定义为//#分隔符的作用主要是告诉mysql遇到下一个//符号时执行上面整个sql语句分隔符//#创建一个存储procedure并命名为testDatacreateproceduretestData()#下面一段是往表中插入10w条数据begindeclareiint;seti=1;while(i<=100000)doinsertintotvalues(i,i,i);seti=i+1;endwhile;end//#这里遇到了//符号,即执行了上面整个sql语句定界符;#恢复mysql定界符;calltestData();#callStoredprocedure#下面两条命令可以查看所占用的空间表文件mysql>useinformation_schema;ReadingtableinformationforcompletionoftableandcolumnnamesYoucanturnoffthisfeaturetogetaquickerstartupwith-ADatabasechangedmysql>selectconcat(round(sum(DATA_LENGTH/1024/1024),2),'M')fromtableswheretable_schemaAND='test'='t';+-----------------------------------------------+|concat(round(sum(DATA_LENGTH/1024/1024),2),'M')|+--------------------------------------------+|3.52M|+------------------------------------------------+1rowinset(0.04sec)可以看到10w条数据在mysql中占用了3.52M的空间,那么我们执行删除命令deletefromt,再看一下#先删除表中的所有数据,然后重新查看表文件大小mysql>deletefromt;QueryOK,100000rowsaffected(0.46sec)mysql>useinformation_schema;ReadingtableinformationforcompletionoftableandcolumnnamesYoucanturnoffthisfeaturetogetaquickerstartupwith-ADatabasechangedmysql>selectconcat(round(sum(DATA_LENGTH/1024/1024),2),'M')fromtableswheretable_schema='-'+---测试-table_name='t'--------------------------------------------+|concat(round(sum(DATA_LENGTH/1024/1024),2),'M')|+------------------------------------------------+|3.52M|+----------------------------------------------+1rowinset(0.00sec)从结果可以发现,表数据为cleared,表占用的空间没有变化,验证了上面的结论,delete操作并没有真正删除数据,表中的空间表未发布。这些被删除的记录行只是被标记为删除,可以重复使用,下次有符合条件的记录时,可以直接插入标记的位置。比如我们在id在300-600之间的记录中删除一条id=500的记录,这条记录就会被标记为删除,下次如果有id=400的记录要插入,那么就可以repeated使用id=500来标记删除的位置,称为行记录复用。另一种情况是数据页复用,即整个数据页都被标记为删除,所以整个数据页都可以复用。数据几乎是无限的。以上面的insert为例,如果要插入的记录是id=1000,那么id=500的位置就不能复用,但是如果有整个数据页可以复用,不管id值是多少,它可以在此页面上重复使用。这些被标记为删除的记录实际上是空的,感觉就像占厕所不拉屎。不说浪费空间,还会影响查询效率。因为你要知道mysql在底层是以数据页为单位存储和读取数据的。每次从磁盘读取数据时,都会读取一个数据页。但是,每次访问数据页都对应一次磁盘IO操作。DiskIO相对内存访问速度相当慢。所以大家想一想,如果一张表有很多数据空洞,本来数据只需要保存在一个数据页中,因为空间被很多空洞占用了,就得添加其他数据页来保存数据。相应的,mysql在查询相同数据时,磁盘IO操作要增加,影响查询速度。其实不仅是删除操作会造成数据空洞,插入和更新也会造成空洞。这里就不细说了,大家懂的就知道了。因此,对数据表进行大量频繁的增删改查后,必然会出现数据空洞,浪费空间,影响查询效率。通常在生产环境中,会直接表现出原本快速的查询会越来越慢。这种情况下,我们通常可以使用如下命令来解决数据空洞问题。optimizetablet命令的原理是重建表,即创建一个临时表B,然后查询表A(有数据空洞的表)中的所有数据,然后将所有数据重新插入到临时表B中,***然后用临时表B简单的替换表A,这就是重建表的过程。我们再试试看效果。mysql>optimizetablet;+--------+---------+--------+-------------------------------------------------------------+|Table|Op|Msg_type|Msg_text|+--------+--------+--------+-------------------------------------------------------------+|test.t|optimize|note|Table不支持optimize,doingrecreate+analyzeinstead||test.t|optimize|status|OK|+--------+-----------+------------+--------------------------------------------------------------+2rowsinset(0.39sec)mysql>useinformation_schema;ReadingtableinformationforcompletionoftableandcolumnnamesYoucanturnoffthisfeaturetogetaquickerstartupwith-ADatabasechangedmysql>selectconcat(round(sum(DATA_LENGTH/1024/1024),2),'M')fromtableswheretable_schema='test'ANDtable_name='t';+--------------------------------------------------+|concat(round(sum(DATA_LENGTH/1024/1024),2),'M')|+----------------------------------------------+|0.02M|+----------------------------------------------+1行et(0.00sec)可以看到表文件的大小变成了0.02M,说明表空间已经释放了。这个0.02M应该是定义表结构文件的大小。另外,也可以使用下面的命令来重建表,可以达到和上面一样的效果,推荐大家使用下面的命令,大家可以试试。altertabletengine=InnoDB注意,本文内容基于InnoDB引擎,针对其他引擎可能会有一些差异。原创并不容易。如果文章对您有所启发,请点击阅读。有什么问题可以在下方留言交流,也可以私信交流。感谢您的支持。
