MySQL 表空间碎片的概念及相关问题解决


Posted in MySQL onMay 07, 2021

背景

经常使用 MySQL 的话,会发现 MySQL 数据文件的磁盘空间一般会不停的增长,而且有时候删了数据或者插入一批数据的时候,磁盘空间有时候还会毫无变化。引发这个其妙现象的就是 MySQL 的表空间碎片。

什么是表空间碎片?

表空间碎片指的是表空间中存在碎片,形象一点来比喻的话,就像是一张 A4 纸,“表空间碎片”就像是把这张 A4 纸撕碎,再重新拼起来,各个碎片之间都会有一些缝隙存在,这些缝隙就是“表空间碎片”。重新拼起来的碎片实际上会比完整的 A4 纸大上一圈,这也代表着表空间容易引发的问题:空间浪费。

对于背景中描述的现象,可以用一张图来进行解释:

MySQL 表空间碎片的概念及相关问题解决

图中的数字代表真实的数据行,圆角矩形代表一个表的表空间。从左往右,第一次操作是删除数据,由于 MySQL 在设计上是不会主动释放空间的,因此当表中的数据行被删除时,虽然数据被“删除”了,但是实际上这部分空间是没有释放的,依旧会被 Table A 占用,因此也就出现了这样子的情景:删除了日志表的很多数据,但是 MySQL 的磁盘空间并没有降低。

PS:这种不释放空间的设计多半和惰性删除有关,早期设计数据库时,使用的 IO 设备一般是机械盘,读写性能比 SSD 差很多,所以删除操作一般不会直接触发磁盘上的数据删除。

可以看到数据删除之后,原本连续的空间中出现了两个空白的区域,这种一般就叫做表空间空洞,空洞太多了就叫做表空间碎片化(对应的是表空间连续)。这部分的空间虽然不会释放,但是会被标记为可重复利用,参考最右边的表空间示意图(第三个圆角矩形),当新插入数据的时候新数据会重新写入到表空间空洞中,这也代表着:在大规模删除过数据的表上,写入数据时,表空间可能不会明显增长或者不会增长。

实际上产生表空间空洞的操作并不只有 delete,update 也会引起这个问题,比如在 varchar 这种变长的字符型列中修改数据,改短一些的时候就会出现非常小的空洞,改长的话就有可能会因为空间不足导致把数据行的一些数据迁移到其他地方去。

怎么查看表空间碎片

MySQL 的系统表记录了表空间的使用情况,可以用如下查询检查:

SELECT CONCAT(table_schema,'.',table_name) AS 'table_name',
                table_rows AS 'Number of Rows',
                CONCAT(ROUND(data_length/(1024*1024),2),' M') AS 'data_size',
                CONCAT(ROUND(index_length/(1024*1024),2),' M') AS 'index_size' ,
                CONCAT(ROUND(data_free/(1024*1024),2),' M') AS'data_free',
                CONCAT(ROUND(data_free/data_length,2),' %') AS 'data_free_pct',
                ENGINE as 'engine'
FROM information_schema.TABLES
WHERE table_schema = 'tablename' 
ORDER by data_free desc;

data_free 指表空间碎片的总空间大小,data_free_pct 指这个表的碎片百分比,效果如下:

mysql> SELECT CONCAT(table_schema,'.',table_name) AS 'table_name',
    ->                 table_rows AS 'Number of Rows',
    ->                 CONCAT(ROUND(data_length/(1024*1024),2),' M') AS 'data_size',
    ->                 CONCAT(ROUND(index_length/(1024*1024),2),' M') AS 'index_size' ,
    ->                 CONCAT(ROUND(data_free/(1024*1024),2),' M') AS'data_free',
    ->                 CONCAT(ROUND(data_free/data_length,2),' %') AS 'data_free_pct',
    ->                 ENGINE as 'engine'
    -> FROM information_schema.TABLES
    -> WHERE table_schema = 'sbtest'
    -> ORDER by data_free desc;
+----------------+----------------+-----------+------------+-----------+---------------+--------+
| table_name     | Number of Rows | data_size | index_size | data_free | data_free_pct | engine |
+----------------+----------------+-----------+------------+-----------+---------------+--------+
| sbtest.sbtest5 |              0 | 0.02 M    | 0.00 M     | 44.00 M   | 2816.00 %     | InnoDB |
| sbtest.sbtest4 |         986400 | 214.70 M  | 15.52 M    | 4.00 M    | 0.02 %        | InnoDB |
| sbtest.sbtest3 |         986400 | 214.70 M  | 15.52 M    | 4.00 M    | 0.02 %        | InnoDB |
| sbtest.sbtest2 |         986400 | 214.70 M  | 15.52 M    | 4.00 M    | 0.02 %        | InnoDB |
| sbtest.sbtest1 |         987400 | 199.70 M  | 15.52 M    | 4.00 M    | 0.02 %        | InnoDB |
+----------------+----------------+-----------+------------+-----------+---------------+--------+
5 rows in set (0.00 sec)

第一行数据是测试用的数据,表中的所有数据都被删掉了,因此计算出来的 data_free_pct 超过了 100%。

怎么解决表空间碎片问题

目前,能够回收表空间的办法仅有一个,就是重建表,手段包括但不限于 optimize,alter table 等。alter table 的有些操作只能靠 rebuild 表来完成,所以有时候对大表进行一些维护操作之后,也会看到磁盘空间使用率下降,这就是回收了表空间碎片腾出来的那一部分空间。

从一般经验来看,表空间碎片的回收操作不建议经常执行,每个月一次就足够了,因为 rebuild 表对服务器的资源影响会比较大,且会影响这个表的写入操作。碎片率(data_free_pct)低于 20% 的时候也不用特别在意,除非磁盘空间非常紧张,且日志基本被清空。

对于回收空间的问题

对一些日志表,或者是有区域性特征的表,建议使用 MySQL 的分区表来管理,需要清理一批数据的时候,可以用 partition truncate 的方式进行清理,磁盘空间也能直接释放掉。

以上就是MySQL 表空间碎片的概念及相关问题解决的详细内容,更多关于MySQL 表空间碎片的资料请关注三水点靠木其它相关文章!

MySQL 相关文章推荐
MySQL之DML语言
Apr 05 MySQL
MYSQL(电话号码,身份证)数据脱敏的实现
May 28 MySQL
MYSQL 无法识别中文的永久解决方法
Jun 03 MySQL
Mysql systemctl start mysqld报错的问题解决
Jun 03 MySQL
详细谈谈MYSQL中的COLLATE是什么
Jun 11 MySQL
新手入门Mysql--sql执行过程
Jun 20 MySQL
MySQL快速插入一亿测试数据
Jun 23 MySQL
MySQL系列之三 基础篇
Jul 02 MySQL
Mysql中一千万条数据怎么快速查询
Dec 06 MySQL
MySQL七大JOIN的具体使用
Feb 28 MySQL
MySQL的存储过程和相关函数
Apr 26 MySQL
SQL中去除重复数据的几种方法汇总(窗口函数对数据去重)
May 08 MySQL
MySQL kill不掉线程的原因
May 07 #MySQL
MySQL数字类型自增的坑
May 07 #MySQL
MySQL获取所有分类的前N条记录
May 07 #MySQL
教你解决往mysql数据库中存入汉字报错的方法
MySQL时间设置注意事项的深入总结
仅用一句SQL更新整张表的涨跌幅、涨跌率的解决方案
May 06 #MySQL
MySQL创建高性能索引的全步骤
You might like
php设计模式  Command(命令模式)
2011/06/17 PHP
destoon二次开发模板及调用语法汇总
2014/06/21 PHP
PHP添加文字水印或图片水印的水印类完整源代码与使用示例
2019/03/18 PHP
PHP设计模式之装饰器(装饰者)模式(Decorator)入门与应用详解
2019/12/13 PHP
tp5.1 框架join方法用法实例分析
2020/05/26 PHP
js中cookie的使用详细分析
2008/05/28 Javascript
Jquery网页内滑动缓冲导航的实现代码
2015/04/05 Javascript
实现高性能JavaScript之执行与加载
2016/01/30 Javascript
jQuery实现按钮点击遮罩加载及处理完后恢复的效果
2016/06/07 Javascript
巧用Vue.js+Vuex制作专门收藏微信公众号的app
2016/11/03 Javascript
js实现水平滚动菜单导航
2017/07/21 Javascript
Angular实现类似博客评论的递归显示及获取回复评论的数据
2017/11/06 Javascript
web前端vue filter 过滤器
2018/01/12 Javascript
angularjs1.5 组件内用函数向外传值的实例
2018/09/30 Javascript
layui-table获得当前行的上/下一行数据的例子
2019/09/24 Javascript
[02:30]DOTA2英雄基础教程 暗影恶魔
2013/12/17 DOTA
[03:09]2014DOTA2国际邀请赛 赛场上的美丽风景线 中国Coser也爱DOTA2
2014/07/20 DOTA
Python实现类的创建与使用方法示例
2017/07/25 Python
Python MD5加密实例详解
2017/08/02 Python
Python之reload流程实例代码解析
2018/01/29 Python
浅析Python3爬虫登录模拟
2018/02/07 Python
Python使用matplotlib模块绘制图像并设置标题与坐标轴等信息示例
2018/05/04 Python
Python 中pandas索引切片读取数据缺失数据处理问题
2019/10/09 Python
Python-jenkins 获取job构建信息方式
2020/05/12 Python
keras小技巧——获取某一个网络层的输出方式
2020/05/23 Python
python 实现图片批量压缩的示例
2020/12/18 Python
结合CSS3的布局新特征谈谈常见布局方法
2016/01/22 HTML / CSS
鞋子女王塔玛拉·梅隆同名奢侈品牌:Tamara Mellon
2017/11/22 全球购物
中英文求职信范文
2014/01/27 职场文书
追悼会子女答谢词
2014/01/28 职场文书
生产部厂长职位说明书
2014/03/03 职场文书
群众路线领导对照材料
2014/08/23 职场文书
办公室主任四风问题对照检查材料思想汇报
2014/09/28 职场文书
2015政治思想表现评语
2015/03/25 职场文书
如何利用js在两个html窗口间通信
2021/04/27 Javascript
解决Tkinter中button按钮未按却主动执行command函数的问题
2021/05/23 Python