单条记录大小增长倍数和ibd文件大小的增长倍数不成正比
单条记录大小增长倍数和ibd文件大小的增长倍数不成正比
环境信息
数据库版本: GreatSQL 8.0.25
字符集:utf8mb4
innodb_default_row_format: dynamic
innodb_page_size: 16384
问题描述
表数据为新insert数据,无delete、无update
GreatSQL 一个数据量为1万的A表,有100个varchar字段,每个字段存10字节,ibd大小为21M
GreatSQL 一个数据量为1万的B表,有100个varchar字段,每个字段存100字节,ibd大小为4.7G
问题:**相同数据量,**相同数据量,B表的每行比A表大10倍,磁盘使用大小不是10倍,而是200多倍?
greatsql> show create table t_user_100_1000_100 \G; *************************** 1. row *************************** Table: t_user_100_1000_100 Create Table: CREATE TABLE `t_user_100_1000_100` ( `id` int NOT NULL AUTO_INCREMENT, `c_name1` varchar(10) NOT NULL DEFAULT '', 。。。 `c_name100` varchar(10) NOT NULL DEFAULT '', PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=10001 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci 1 row in set (0.00 sec) greatsql> show create table t_user_100_10000_100 \G; *************************** 1. row *************************** Table: t_user_100_10000_100 Create Table: CREATE TABLE `t_user_100_10000_100` ( `id` int NOT NULL AUTO_INCREMENT, `c_name1` varchar(100) NOT NULL DEFAULT '', 。。。 `c_name100` varchar(100) NOT NULL DEFAULT '', PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=10001 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci 1 row in set (0.00 sec) greatsql> select count() from t_user_100_10000_100 ; +----------+ | count() | +----------+ | 10000 | +----------+ 1 row in set (0.06 sec) greatsql> select count() from t_user_100_1000_100 ; +----------+ | count() | +----------+ | 10000 | +----------+ 1 row in set (0.18 sec) #os ibd 文件大小 ll total 4313096 -rw-r----- 1 mysql mysql 5016387584 Apr 9 18:52 t_user_100_10000_100.ibd -rw-r----- 1 mysql mysql 20971520 Apr 9 18:40 t_user_100_1000_100.ibd greatsql> select 5016387584/20971520 from dual; +---------------------+ | 5016387584/20971520 | +---------------------+ | 239.2000 | +---------------------+ 1 row in set (0.00 sec)
问题分析
多出来的24倍难道是碎片导致的?
使用optimize table重整表后,几乎没有任何优化,查看系统视图,也没有多少空洞。
使用官方工具innochecksum查看表空间文件PAGE类型分布,可以看到,B表相对A表多了大量的Other type of page。看来主要的空间消耗是在这个"Other"上。
- INNODB的行溢出
INNODB默认下每个PAGE的大小为16K。B表每行10K,每个PAGE只能存放1行记录,余下的6K就浪费了。但即使按照这个算法,也只浪费了37%的空间。
实际上,INNODB在这里有个处理,当记录过大,会将最大的列使用一个指针替代,指针指向一个新的PAGE,在该PAGE上存放实际数据。
由函数page_zip_rec_needs_ext()判断是否需要溢出。判断方式是该记录长度是否大于空PAGE的可用空间。
GDB执行一下,可以看到一个16k的PAGE实际可用的空间为16252字节(页头等占用了小量字节)。一半粗略算作是8k。
如果行长度大于8K,会将最长的列存放到新的PAGE,原位置使用20字节的指针代替。如果处理后,行长度依然大于8K,则选择当前最长的列进一步处理,不断循环。如果列长度无法进一步缩少,仍然大于8K,则抛出DB_TOO_BIG_RECORD错误,即"row size too large"。dtuple_convert_big_rec()函数上可以看到更多的执行细节。
- 小量数据溢出的情况
以下堆栈展示把溢出数据写入"Off Page"。主要函数为lob::insert()。
log::insert()会申请一个新的16K大小的PAGE,并将数据写入新的page。
dberr_t insert(){ ...... //分配一个新的16KB的PAGE first_page_t first(mtr, index); buf_block_t *first_block = first.alloc(mtr, ctx->is_bulk()); ...... //将100字节写入写入 ulint to_write = first.write(trxid, ptr, len); ...... }
以下是B表插入数据,往innodb"Off page"写入数据时候的断点,可以看到只写了B表单列100字节数据。16KB的容量只写100字节的数据,剩余99%的空间用不上,实在太浪费了。
B表有100个varchar列,每个列100字节。如果需要满足列长少于8K,需要25个列以上进行溢出(一个列还有隐藏列和其它数据,实际需要溢出的列略多于25)。使用innochecksum查询到"OTHER" page 有29万,B表有一万行,平均每行29个"OTHER" page。看来这个"OTHER" page基本都是这种"Off page"了。
问题总结
GreatSQL 白白浪费了95%的磁盘空间,是因为大量的列溢出了小量的数据。INNODB存放每个溢出列的数据的最小分配单元大小是16KB。原本10KB的行长度,需要多占N倍的存储空间。
优化建议
表设计时,要注意控制行长度小于8k,避免小量列数据溢出,导致磁盘容量和性能问题。
延伸阅读
- https://dev.mysql.com/doc/refman/8.0/en/out-of-range-and-overflow.html
- https://mp.weixin.qq.com/s/AjGrQONFPJgVAqy_qtoiYA
- https://mp.weixin.qq.com/s/QMZ7O0gfs81zXo69F1bHdQ
Enjoy GreatSQL :)
关于 GreatSQL
GreatSQL是适用于金融级应用的国内自主开源数据库,具备高性能、高可靠、高易用性、高安全等多个核心特性,可以作为MySQL或Percona Server的可选替换,用于线上生产环境,且完全免费并兼容MySQL或Percona Server。
相关链接: GreatSQL社区 Gitee GitHub Bilibili
GreatSQL社区:
社区有奖建议反馈: https://greatsql.cn/thread-54-1-1.html
社区博客有奖征稿详情: https://greatsql.cn/thread-100-1-1.html
(对文章有疑问或者有独到见解都可以去社区官网提出或分享哦~)
技术交流群:
微信&QQ群:
QQ群:533341697
微信群:添加GreatSQL社区助手(微信号:wanlidbc
)好友,待社区助手拉您进群。

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
开源软件:禅道项目管理软件安装教程
一、禅道软件简介 禅道项目管理软件是一款国产开源项目管理软件。它集项目集管理、产品管理、项目管理、质量管理、DevOps、知识库、BI效能、工作流、学堂、反馈管理、组织管理和事务管理于一体,是一款专业的研发项目管理软件,完整覆盖了研发项目管理的核心流程。 禅道目前提供多种解决方案: 规模化敏捷解决方案、DevOps一体化解决方案、自动化测试解决方案,研发效能解决方案、禅道基于IM+AI的研发协同解决方案、企业创新能力解决方案、企业决策分析解决方案,让每一个项目都可实现高效管理。 二、安装方式 (一)使用禅道智能应用平台安装 禅道智能应用平台是一个开源轻量级应用交付平台。通过应用市场,可以一键安装企业级软件,不仅具备高可用特性,还支持自动与手动备份,当有新版本发布时,可以查看软件的更新日志,决定是否升级,快捷方便。 禅道智能应用平台特点 云原生应用市场。这是禅道智能应用平台的核心功能,用户可以通过应用市场,选择和安装需要的应用。 多租户。平台支持多租户管理,用户可将应用装在不同空间,各空间有独立资源配额与权限控制规则。 服务管理。即对从应用市场安装的应用进行管理,可进行启动、关闭、重启、...
- 下一篇
高性能无锁队列 Disruptor 核心原理分析及其在i主题业务中的应用
作者:来自 vivo 互联网服务器团队- Li Wanghong 本文首先介绍了 Disruptor 高性能内存队列的基本概念、使用 Demo、高性能原理及源码分析,最后通过两个例子介绍了 Disruptor 在i主题业务中的应用。 一、i主题及 Disruptor 简介 i主题是 vivo 旗下的一款主题商店 app,用户可以通过下载主题、壁纸、字体等,实现对手机界面风格的一键更换和自定义。 Disruptor 是英国外汇交易公司 LMAX 开发的一个高性能的内存队列(用于系统内部线程间传递消息,不同于 RocketMQ、Kafka 这种分布式消息队列),基于 Disruptor 开发的系统单线程能支撑每秒600万订单。目前,包括 Apache Storm、Camel、Log4j 2在内的很多知名项目都应用了 Disruptor 以获取高性能。在 vivo 内部它也有不少应用,比如自定义监控中使用 Disruptor 队列来暂存通过监控 SDK 上报的监控数据,i主题中也使用它来统计本地内存指标数据。 接下来从 Disruptor 和 JDK 内置队列的对比、Disruptor 核心...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- MySQL8.0.19开启GTID主从同步CentOS8
- CentOS8安装Docker,最新的服务器搭配容器使用
- Linux系统CentOS6、CentOS7手动修改IP地址
- 2048小游戏-低调大师作品
- Springboot2将连接池hikari替换为druid,体验最强大的数据库连接池
- CentOS8安装MyCat,轻松搞定数据库的读写分离、垂直分库、水平分库
- CentOS8编译安装MySQL8.0.19
- CentOS6,CentOS7官方镜像安装Oracle11G
- CentOS7,8上快速安装Gitea,搭建Git服务器
- SpringBoot2整合Thymeleaf,官方推荐html解决方案