Current user:root@localhost
SSL: Not in use
Server version:8.0.18 MySQL Community Server - GPL
Protocol version:10
Connection: Localhost via UNIX socket

为了释放大表所占用的空间容量,是有一个前提的,就是要MySQL数据库开启了独立表空间,在MySQL5.6.7之后是默认开启。
如何查看是否开启了独立表空间呢?
主要是看参数innodb_file_per_table值是否为1,1表示开启了独立表空间,0则表示使用共享表空间。
查看数据库当前设置
[root@DB_PM:(none)]>show variables like '%per_table';
+-----------------------+-------+
| Variable_name | Value |
+-----------------------+-------+
| innodb_file_per_table | ON |
+-----------------------+-------+
1 row in set (0.00 sec)
复制
可以看到,innodb_file_per_table当前值是ON(1);也就是该数据库开启了独立表空间。
共享表空间:数据库的所有表数据,索引文件全部放在一个文件中,默认这个共享表空间的文件路径在data目录下。 默认的文件名为:ibdata1(可以扩展成多个)。由于所有数据都在一个文件里,如果要对单表维护,十分不方便:在delete数据后,文件内会留下很多间隙,ibdata1文件不会自动收缩;使用drop table命令删除表后,空间是无法释放的。
独立表空间:每一个表都是独立存在的,即每个表都是由一个.frm表描述文件和一个.ibd文件组成。.frm文件:保存了每个表的元数据,包括表结构的定义等 .ibd文件:保存了每个表的数据和索引的文件。由于每个表都有自已独立的表空间,在执行drop table操作的时候,表空间是可以释放回收的。在delete操作后,可以通过alter table TableName engine=innodb可以整理碎片,回收部分表空间。
删除表最常用的方式就是drop table命令。
我们来看下,在删除几十G表的情况下,会有什么影响?
先来看下几十G表的情况:
dmp@TDMPGLPT04[15:53:44]:/mysqldata/my3306/data/dkf$ ls -lh
总用量 140G
-rw-r----- 1 dmp dmp 46G 5月 6 21:27 sbtest1.ibd
-rw-r----- 1 dmp dmp 47G 5月 7 15:53 sbtest2.ibd
-rw-r----- 1 dmp dmp 47G 5月 7 15:53 sbtest3.ibd
dmp@TDMPGLPT04[15:53:48]:/mysqldata/my3306/data/dkf$
复制
我们现在来执行drop 命令:
[root@DB_PM:dkf]>drop table sbtest2;
Query OK, 0 rows affected (2.27 sec)
[root@DB_PM:dkf]>
复制
47GB的表直接删除也花费了2.27s的时间,如果表再大(几百G),或是有许多的事务在执行,删除的过程会堵塞表上所有的操作,造成大量的业务等待,这在业务时段是不可忍受的。而且,在删除大表的同时,数据库的存储IO是直线上升的,如果表特别大,很可能会导致IO被占满,影响正常的业务处理。
于是,我们就想,不在业务时段删除不就可以了,于是就有了如下的方案:
为了避免在业务时段对数据库的影响,将当前的表rename下,重建一张当前的表来达到删除的目的,而且将空间释放的操作放到业务低峰的时间去做,这样就能大大降低对业务的影响。
[root@DB_PM:dkf]>rename table sbtest3 to sbtest3_bak;
Query OK, 0 rows affected (0.03 sec)
[root@DB_PM:dkf]>
复制
可以看到,这个过程是非常快的,对于MySQL来说,仅仅是将数据字典里表的名字更改了下而已,但是空间是没有释放的。后续我们再安排在业务低峰的时间进行drop table。这跟方法1是一样的。
那有没有一种,既可以快速的删除表,又尽量不影响业务处理,而且对主机的IO占用也较小?
答案是有的,这就要我们利用linux操作系统中文件硬链接的知识,来进行快速删除。也就是我们接下来要介绍的方法了。
我们知道,在Linux系统中,文件有软链接和硬链接的方式:
软链接:类似于 Windows 系统中给文件创建快捷方式,即产生一个特殊的文件,该文件用来指向另一个文件,此链接方式同样适用于目录。
【示例】创建软链接:
dmp@TDMPGLPT04[13:10:39]:~$ touch softlink
dmp@TDMPGLPT04[13:10:47]:~$ ln -s home/mysql/softlink tmp/sslink
dmp@TDMPGLPT04[13:11:02]:~$ ll tmp/sslink
lrwxrwxrwx 1 dmp dmp 20 5月 8 13:11 tmp/sslink -> home/mysql/softlink
dmp@TDMPGLPT04[13:11:06]:~$
复制
硬链接:我们知道,文件的基本信息都存储在 inode 中,而硬链接指的就是给一个文件的 inode 分配多个文件名,通过任何一个文件名,都可以找到此文件的 inode,从而读取该文件的数据信息。
【示例 】创建硬链接:
dmp@TDMPGLPT04[13:12:21]:~$ touch hardlink
dmp@TDMPGLPT04[13:12:28]:~$ ln home/mysql/hardlink tmp/hdlink
dmp@TDMPGLPT04[13:12:41]:~$ ll tmp/hdlink
-rw-r----- 2 dmp dmp 0 5月 8 13:12 /tmp/hdlink
复制
在原始文件被删除之后,软链接文件还存在,但是是无法通过软链接进行编辑,相当于仅有快捷方式是无法访问源文件了。
dmp@TDMPGLPT04[13:12:55]:~$ rm /home/mysql/softlink
dmp@TDMPGLPT04[13:14:26]:~$ ll /tmp/sslink
lrwxrwxrwx 1 dmp dmp 20 5月 8 13:11 /tmp/sslink -> /home/mysql/softlink
dmp@TDMPGLPT04[13:14:34]:~$ ll /home/mysql/softlink
ls: 无法访问/home/mysql/softlink: 没有那个文件或目录
dmp@TDMPGLPT04[13:14:44]:~$
复制
同样的情况,在原始文件被删除之后,硬链接的情况下,还是可以编辑硬链接对应的文件,因为,本质上来说,硬链接对应的文件也就是真正的源文件。硬链接的情况下,没有任何一个inode指向该文件的时候,才会被真正删除。如果理解还有问题的,可参考Linux中inode的相关知识。
dmp@TDMPGLPT04[13:14:44]:~$ rm /home/mysql/hardlink
dmp@TDMPGLPT04[13:18:26]:~$
dmp@TDMPGLPT04[13:18:27]:~$ ll /tmp/hdlink
-rw-r----- 1 dmp dmp 0 5月 8 13:12 /tmp/hdlink
复制
可以看到,硬链接并不是一个快捷方式。
那我们来看看怎么通过硬链接来删除我们的几十G表。
我们先根据要删除的表来创建一个硬链接:
注意:如果有从库的情况,从库也要执行创建硬链接的操作。
dmp@TDMPGLPT04[13:23:26]:/mysqldata/my3306/data/dkf$ ln sbtest3.ibd sbtest3.ibdhdlk
dmp@TDMPGLPT04[13:23:43]:/mysqldata/my3306/data/dkf$ ll -lht
总用量 141G
-rw-r----- 1 dmp dmp 47G 5月 8 13:23 sbtest1.ibd
-rw-r----- 1 dmp dmp 47G 5月 7 17:19 sbtest3.ibd
-rw-r----- 2 dmp dmp 47G 5月 8 13:23 sbtest3.ibdhdlk
-rw-r----- 1 dmp dmp 112K 5月 8 11:00 t.ibd
dmp@TDMPGLPT04[13:23:45]:/mysqldata/my3306/data/dkf$
复制
我们现在来执行drop table的操作
[root@DB_PM:dkf]>drop table sbtest3;
Query OK, 0 rows affected (0.04 sec)
[root@DB_PM:dkf]>
复制
这次删除仅用了0.04s就完成了。数据库中已经没有该表的存在了。
[root@DB_PM:dkf]>select * from information_schema.tables where table_name='sbtest3';
Empty set (0.00 sec)
[root@DB_PM:dkf]>
复制
我们再看下操作系统上,表的ibd文件也被删除了,但是硬链接的文件还存在,我们后续只要将该文件删除即可。
dmp@TDMPGLPT04[13:25:17]:/mysqldata/my3306/data/dkf$ ll -lht
总用量 94G
-rw-r----- 1 dmp dmp 47G 5月 8 13:26 sbtest1.ibd
-rw-r----- 1 dmp dmp 47G 5月 8 13:23 sbtest3.ibdhdlk
-rw-r----- 1 dmp dmp 112K 5月 8 11:00 t.ibd
dmp@TDMPGLPT04[13:26:51]:/mysqldata/my3306/data/dkf$
复制
如何正确的删除sbtest3.ibd.hdlk呢?
如果直接使用rm命令来删除,特别是几百G的大文件删除,在生产环境,是会造成磁盘IO开销飙升,CPU负载过高,是会影响到业务的稳定运行的。
那么,这种时候,就应该采用Linux上的另一个工具了,使用truncate命令来删除,truncate命令在coreutils工具集中。有人对rm和truncate命令专程测试过,truncate命令对磁盘IO,CPU负载几乎无影响。
在主机上使用truncate命令分批进行删除
注意:tab_size 改为实际的以GB为单位数据文件大小并取整,脚本中仅将tab_size替换为数字,例如表大小为47GB,则tab_size替换为40
for i in `seq tab_size -10 10` ;do sleep 3; truncate -s ${i}G /mysqldata/my3306/data/dkf/sbtest3.ibd.hdlk;done
复制
注意:如果有从库的情况,从库也要执行删除的操作。
使用truncate 工具删除数据时,并未对服务器IO负载造成影响较大影响,
最后,删除剩余的小的数据文件.
rm -rf /mysqldata/my3306/data/dkf/sbtest3.ibd.hdlk
复制
到此,我们就介绍了在MySQL数据库上常见的删除大表的操作方法,大家在运维的过程中,如果遇到特别大的表需要删除,也可采用最后一种方法。 但是在实施前,请在测试环境做好验证,并且尽量在业务低峰的时间进行处理,如果是复制架构,也请考虑从库操作,最大化降低对业务及下游的影响。

完
=end=