别再用delete删除数据
参与了好几个项目开发,每个项目随着业务量的增大,MySQL数据日益剧增,例如其中一个项目中得用户足迹表,那是非常的疯狂,只怪我大意了,没有闪。
这篇文章我会从delete对性能的影响,以及如何以正确的姿势来删除数据。
在MySQL中Innodb存储引擎的表存在两部分,一部分是表结构,另一部分是表数据。
在MySQL8.0之前/var/lib/mysql下都会存在.frm文件,在MySQL8.0之后就不存在了。这是因为MySQL8.0中已经允许把表结构定义放到数据字典中了,是用参数innodb_file_per_table来决定的。
一、表空间
表空间分为几种,系统表空间、用户表空间、undo空间。
系统表空间:MySQL内部的数据字典,如information_schema库下的数据。
用户表空间:自己建立的表结构数据
undo空间:存储Undo信息,用于快速回滚。
MySQL8.0之前表结构是在系统表空间存储的,在MySQL5.6.6后可以使用参数innodb_file_per_table来控制。
设置为off时,表数据是放在系统表空间中,也就是MySQL的数据字典放在一起。
设置为on时,innodb存储引擎的表数据存储在.idb文件中。
你知道表定义存储在哪里吗?
来到死磕MySQL系列的专用数据库kaka,新建一张表evt_sms。
猜一下创建的evt_sms表结构定义存储在哪里呢?
在information_schema库里边的TABLES中,执行查询SELECT TABLE_NAME,TABLE_COMMENT FROM TABLES WHERE TABLE_TYPE='BASE TABLE';
我们自定义的表类型是TABLE_TYPE。
说了这么是为了解释如果把innodb_file_per_table设置为off,则表数据也会存放在这里。
问题:如果数据存在放共享表空间中,表删除了,空间会删除吗?
答案是不会的。
参数innodb_file_per_table设置为on数据存储在哪里呢?
一般情况下是在var/lib/mysql中,会看到你创建的数据库,进入到数据库中就能看到一张表对应一个ibd文件。
数据就是存储在这里。
结论
在项目开始阶段,切记将innodb_file_per_table设置为on,这是正确的做法。
二、数据删除流程
现在你应该知道Innodb存储引擎用的是B+树数据结构,如下图。
如果现在删了主键ID为4的这条记录,Innodb引擎会把ID为4的这条记录标记为删除,如果之后再插入ID为4的记录,可能会复用这个位置,但磁盘文件大小并不会缩小。
隐式字段
这里就牵扯到了mvcc中的一个知识点,MVCC实现原理是由俩个隐式字段、undo日志、Read view来实现的。
上文说的标记删除就是隐式字段中的delete flag,即记录被更新或删除,这里的删除并不代表真的删除,而是将这条记录的delete flag改为true。
在MVCC:听说有人好奇我的底层实现这篇文章中也给大家留下了一个伏笔,数据库的删除是真的删除吗?
问题:删了一个数据页的所有数据会怎么样
跟单条数据是一样的,整个数据页都是可以复用的。
记录的复用是仅限于符合范围条件的数据,例如上文删除的ID为4这条记录,如果在插入ID为4就会复用。
这里需要给大家再聊一个新的知识点页合并,若相邻的两个数据页利用率都很低,系统就会把这两个数据页合并到一个页上,另一个数据页就会标记为可复用。
问题:使用delete把整个表的数据都删除了会怎么样
答案是,所有的数据页都会标记为可复用,但是磁盘文件大小是不会改变的。
三、实践全表删除表文件大小不改变
经过添加数据后表数据已经达到近100W了,文件大小已经达到108M。
扩展
这里大家应该能看见stopped,就是执行命令ctrl + z来的,作用是开始我们在MySQL窗口里边,但不想退出MySQL窗口查看MySQL表文件大小,然后就可以执行这个命令结束任务。
查看完后可以在执行fg返回到MySQL窗口。
问题:Linux如何把文件单位显示为M
假设刚刚直接执行ll命令查看文件,那么就需要手动计算文件大小,很不方便。
执行ll -h命令则可以直观的看到文件大小。
删除数据查看磁盘文件是否缩小
为了直观看大文件大小变化,咔咔直接把表里边的数据全部删了,再看文件大小,还是108M。文件大小是没有变化的。