故障分析 | 从 data_free 异常说起

本文涉及的产品
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
RDS MySQL Serverless 高可用系列,价值2615元额度,1个月
简介: 临时表 引发的data_free异常案例

一、前言
某个客户反馈查询数据库发现 information_schema.tables 的 data_free 值突发异常,达到 13G 左右。如图:

图片
需要排查什么原因导致的,本文梳理排查的过程和和解决问题的方法。

二、排查
2.1 分析
首先 data_free 的含义是 表空间 ibd 文件经过写入和删除之后,留下的没有回收的碎片空间大小。
让现场的同学同时检查主备库,对比有没有文件大小和配置上的差异。发现主库的data_free 值是 13G 左右, 备库正常。
image.png

看结果猜测和主库上的某些请求动作有关,空洞是 MySQL 因为 sql 写入而请求分配的空间没有自动回收的结果。基于前线给的信息,没有其他思路,再看前线发的截图:

image.png

意外从 截图的 ibtmp1 文件大小找到一些线索,截图显示 ibtmp1 文件大小也是 13G ,备库则是初始值大小。

image.png

忽略红色的箭头,查看 ibtmp1 文件大小为 13G ,似乎有些头绪,data_free 是否和 ibtmp1 有关。

2.2 验证猜想
使用 sysbench 创建测试表 sbtest1 ,构造2w条记录,然后创建 sbtest2 ,将 sbtest1 的数据 导入到 sbtest2 。为何这么操作,后面会说明。

mysql > show variables like 'innodb_temp_data_file_path';
Variable_name Value
innodb_temp_data_file_path ibtmp1:12M:autoextend

1 row in set (0.00 sec)

查看物理 ibtmp1 文件大小:

[root@tidb00 data]# du -sm ibtmp1
12 ibtmp1

构建测试用例,让系统自动生成临时表

mysql  > create table sbtest2 like sbtest1;
Query OK, 0 rows affected (0.01 sec)
mysql > insert into sbtest2(k,c,pad) select k,c ,pad from sbtest1;
Query OK, 200000 rows affected (1.18 sec)
Records: 200000  Duplicates: 0  Warnings: 0
mysql > insert into sbtest2(k,c,pad) select k,c ,pad from sbtest1;
Query OK, 200000 rows affected (1.06 sec)
mysql > insert into sbtest2(k,c,pad) select k,c ,pad from sbtest2;
Query OK, 400000 rows affected (2.49 sec)
Records: 400000  Duplicates: 0  Warnings: 0
mysql > insert into sbtest2(k,c,pad) select k,c ,pad from sbtest2;
Query OK, 800000 rows affected (6.18 sec)
Records: 800000  Duplicates: 0  Warnings: 0

再次检查 ibtmp1 文件大小 204MB

[root@tidb00 data]# du -sm ibtmp1
204 ibtmp1

mysql > SELECT FILE_NAME, TABLESPACE_NAME, ENGINE, INITIAL_SIZE, TOTAL_EXTENTS*EXTENT_SIZE
    ->        AS TotalSizeBytes, DATA_FREE, MAXIMUM_SIZE FROM INFORMATION_SCHEMA.FILES
    ->        WHERE TABLESPACE_NAME = 'innodb_temporary'\G
*************************** 1. row ***************************
      FILE_NAME: ./ibtmp1
TABLESPACE_NAME: innodb_temporary
         ENGINE: InnoDB
   INITIAL_SIZE: 12582912
 TotalSizeBytes: 213909504
      DATA_FREE: 207618048  ## 和物理文件大小对应
   MAXIMUM_SIZE: NULL
1 row in set (0.00 sec)

查看 I_S.tables 的data_free 的值:

image.png

查看 insert select from table 在执行过程中的确使用了临时表。

mysql > explain insert into sbtest2(k,c,pad) select k,c ,pad from sbtest2\G
*************************** 1. row ***************************
..
*************************** 2. row ***************************
           id: 1
  select_type: SIMPLE
        table: sbtest2
   partitions: NULL
         type: ALL
possible_keys: NULL
          key: NULL
      key_len: NULL
          ref: NULL
         rows: 1578168
     filtered: 100.00
        Extra: Using temporary  ## 
2 rows in set (0.00 sec)
Records: 200000  Duplicates: 0  Warnings: 0

至此,可以确定客户的实例因为执行某些 SQL 过程中占用系统临时表空间,使用完之后临时表空间并未被回收导致开头的问题。接下来我们详细了解 MySQL 临时表的相关知识。

三、临时表空间

3.1 介绍
ibtmp1 是非压缩的 innodb 临时表的独立表空间, 通过 innodb_temp_data_file_path参数指定文件的路径,文件名和大小,默认配置为ibtmp1:12M:autoextend,如果没有指定位置,临时表空间会被创建到innodb_data_home_dir 指定的路径。
需要注意的是: 按照默认值,这个文件大小是可以无限增长的。而且 5.7 版本并不会随着 SQL 语句结束主动回收该临时表空间,导致空间资源不足的安全风险。
3.2 什么情况下会用到临时表
当 explain 查看执行计划结果的 extra 列中,如果包含 Using Temporary 就表示会用到临时表,例如如下几种常见的情况通常就会用到:

insert into tab1 select ... from tab2 。

group by 无索引字段或 group by order by 的字段不一样。

distinct 的值和 group by 的值不一样,无法利用稀疏索引。

其他的欢迎补充。
3.3 临时表相关的参数和元数据
5.7 版本:

innodb_temp_data_file_path
default_tmp_storage_engine
internal_tmp_disk_storage_engine
8.0 版本分为会话级和全局级临时表空间

innodb_temp_tablespaces_dir #指定会话级创建临时表到BASEDIR/data/#innodb_temp
innodb_temp_data_file_path # 全局变量
internal_tmp_disk_storage_engine
用户自己创建的临时表可以通过查询 INFORMATION_SCHEMA.INNODB_TEMP_TABLE_INFO

mysql > CREATE TEMPORARY TABLE t1 (c1 INT PRIMARY KEY) ENGINE=INNODB;
Query OK, 0 rows affected (0.00 sec)

mysql > SELECT * FROM INFORMATION_SCHEMA.INNODB_TEMP_TABLE_INFO\G
1. row **

        TABLE_ID: 54
            NAME: #sqlfd5_b_0
          N_COLS: 4
           SPACE: 36

PER_TABLE_TABLESPACE: FALSE

   IS_COMPRESSED: FALSE

1 row in set (0.00 sec)
MySQL 在执行 sql 过程中被优化器创建的表,则无法通过 INFORMATION_SCHEMA.INNODB_TEMP_TABLE_INFO 直接查看。比如本文的案例。

3.4 怎么解决 ibtmp1 文件空间占用的问题
万能的重启大法, 找个合适的时间,切换数据库,重启老的主库。
通过配置 innodb_temp_data_file_path 控制ibtmp1 文件的最大值,避免表空间大小无限增加。

innodb_temp_data_file_path = ibtmp1:12M:autoextend:max:10G

12M是文件的初始大小,10G是文件的最大值,超过最大值则系统会提示报错

ERROR 1114 (HY000): The table '/data/msb_5_7_31/tmp#sql_xxxxx_0' is full

参考文章
https://dev.mysql.com/doc/refman/5.7/en/innodb-temporary-tablespace.html

https://dev.mysql.com/doc/refman/8.0/en/innodb-temporary-tablespace.html

相关实践学习
如何在云端创建MySQL数据库
开始实验后,系统会自动创建一台自建MySQL的 源数据库 ECS 实例和一台 目标数据库 RDS。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助     相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
目录
相关文章
|
2月前
|
搜索推荐 Java API
Electron V8排查问题之分析 node-memwatch 提供的堆内存差异信息来定位内存泄漏对象如何解决
Electron V8排查问题之分析 node-memwatch 提供的堆内存差异信息来定位内存泄漏对象如何解决
74 0
|
3月前
|
开发框架 缓存 Java
浅析JAVA日志中的性能实践与原理解释问题之"Garbage Free"技术的实现方式问题如何解决
浅析JAVA日志中的性能实践与原理解释问题之"Garbage Free"技术的实现方式问题如何解决
|
3月前
|
存储 数据库
cannot read properties of underfined (reading ‘code‘),别光知道抄,有的时候,细节就是影响全局关键,别人代码到你项目不一定100%正确,判断bug出
cannot read properties of underfined (reading ‘code‘),别光知道抄,有的时候,细节就是影响全局关键,别人代码到你项目不一定100%正确,判断bug出
|
5月前
|
存储 监控 算法
一次通过dump文件分析OutOfMemoryError异常代码定位过程
OutOfMemoryError是Java程序中常见的异常,通常出现在内存不足时,导致程序无法运行。借助MAT内存分析工具分析可能的内存泄漏代码问题定位。
93 1
一次通过dump文件分析OutOfMemoryError异常代码定位过程
|
5月前
,出现了一个RPA(机器人流程自动化)的运行异常,具体错误为 `rpa.core.errors.RPATimeoutError`
【2月更文挑战第21天】,出现了一个RPA(机器人流程自动化)的运行异常,具体错误为 `rpa.core.errors.RPATimeoutError`
323 2
|
Linux
内核笔记](四)——内核常见调试手段(printf、dump_stack、devmem)
内核笔记](四)——内核常见调试手段(printf、dump_stack、devmem)
253 0
内核笔记](四)——内核常见调试手段(printf、dump_stack、devmem)
|
API iOS开发
连续启动 crash 自修复技术实现与原理解析
**Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)* - [前言](#%E5%89%8D%E8%A8%80) - [实现原理](#%E5%AE%9E%E7%8E%B0%E5%8E%9F%E7%90%86) - [优化:降低50%以上误报机率](#%E4%BC%98%
1759 0