Aurora RDS MySQL The table ‘/rdsdbdata/tmp/#sql14b_df16d_1bd‘ is full
关键词:AWS RDS、MySQL、临时表溢出、The table is full、SQL 优化、JOIN 重构、窗口函数性能
适用场景:Java 后端开发、DBA、运维工程师、定时任务系统维护者
环境:AWS RDS for MySQL(5.7/8.0)、Spring Boot + MyBatis、XXL-JOB 定时任务
一个每日执行的发货邮件通知定时任务(WorkDaySendShipmentEmailJob)突然失败。查看日志,发现如下关键错误:
ERROR ... - workDaySendShipmentEmailJob error:
org.springframework.jdbc.UncategorizedSQLException:
### Error querying database.
Cause: java.sql.SQLException: (conn=913773) The table '/rdsdbdata/tmp/#sql14b_df16d_1bd' is full
...
error code [1114]; (conn=913773) The table '/rdsdbdata/tmp/#sql14b_df16d_1bd' is full
这表示 MySQL 在执行查询时使用的临时表空间(tmpdir)已满,无法继续创建或写入临时表。在 AWS RDS for MySQL 环境中,这是非常典型的性能/资源瓶颈问题。
一、错误原因分析
错误本质:临时表(Temporary Table)溢出
在执行复杂 SQL 时,MySQL 会使用内存或磁盘上的临时表来存储中间结果。当数据量过大,超出 tmpdir 可用空间(RDS 中为 /rdsdbdata/tmp/),就会抛出:
Error 1114: The table ‘xxx’ is full
尤其在以下操作中极易触发:
DISTINCTGROUP BY/ORDER BY非索引字段- 多表
JOIN导致结果集爆炸 - 窗口函数(如
ROW_NUMBER()) - 子查询物化(Materialization)
而我们的 SQL 正好“集齐”了所有高危操作!
原始SQL :
SELECT DISTINCT...,CASE WHEN pn.creation_date ... THEN ... END AS paymentStatus,...
FROM task_shipment ts
INNER JOIN task_network_delivery_record tndr ON ...
LEFT JOIN project_network pn ON ...
LEFT JOIN project p ON ...
-- ⚠️ 重复 JOIN 同一张表 5 次!
LEFT JOIN project_network_milestone pnm ON ... milestone='3A'
LEFT JOIN project_network_milestone pnm2 ON ... milestone='3B'
LEFT JOIN project_network_milestone pnm3 ON ... milestone='3B2'
LEFT JOIN project_network_milestone pnm4 ON ... milestone='3P'
LEFT JOIN milestone_sap_retry msr ON ... milestone='3X'
-- ⚠️ 窗口函数子查询
LEFT JOIN (SELECT ..., ROW_NUMBER() OVER (PARTITION BY network_number, delivery_group ...) AS rnFROM network_delivery_info
) ndi ON ...
WHERE ts.create_time BETWEEN ? AND ?
数据规模:核心表均达 数百万行(task_shipment、project_network_milestone 等)。
即使单表不大,但多重 JOIN + 函数计算 + 无有效过滤,导致中间结果轻松突破千万行,临时表直接撑爆。
优化策略:从“治标”到“治本”
生产保命妙招
扩大 临时表大小:
temptable_max_ram:限制临时表最多可以使用多少内存.
temptable_max_mmap:当内存用尽时,临时表可使用多少磁盘空间作为缓存
【参考文档】:
【1】 https://docs.amazonaws.cn/zh_cn/AmazonRDS/latest/AuroraUserGuide/aurora-mysql-write-forwarding.html 【2】 https://docs.amazonaws.cn/AmazonRDS/latest/AuroraUserGuide/AuroraMySQL.Managing.Performance.html#AuroraMySQL.Managing.TempStorage 【3】 https://repost.aws/knowledge-center/rds-aurora-mysql-table-storage-issues
1. 【核心】合并重复 JOIN:用聚合替代多次连接
问题:对 project_network_milestone 表按不同 milestone 值 JOIN 5 次,极易产生笛卡尔积。
优化后:
LEFT JOIN (SELECT network_number,MAX(CASE WHEN milestone = '3A' THEN actual_date END) AS actual_date_3A,MAX(CASE WHEN milestone = '3B' THEN actual_date END) AS actual_date_3B,MAX(CASE WHEN milestone = '3B2' THEN actual_date END) AS actual_date_3B2,MAX(CASE WHEN milestone = '3P' THEN actual_date END) AS actual_date_3P,MAX(CASE WHEN milestone = '3X' THEN success END) AS success_3XFROM project_network_milestoneWHERE deleted = '0' OR deleted IS NULLGROUP BY network_number
) pnm_agg ON pnm_agg.network_number = pn.network_number
效果:只需扫描一次里程碑表,避免 5 倍中间结果膨胀。
2. 优化窗口函数子查询
原写法:
LEFT JOIN (SELECT ..., ROW_NUMBER() OVER (PARTITION BY network_number, delivery_group ORDER BY id DESC) AS rnFROM network_delivery_info WHERE deleted = 0
) ndi ON ndi.rn = 1
建议:
-
添加复合索引加速分区取最新:
CREATE INDEX idx_ndi_lookup ON network_delivery_info (network_number, delivery_group, id DESC); -
若业务允许,加时间范围过滤(如近 6 个月)。
3. 避免字段函数转换,让索引生效
原条件:
STR_TO_DATE(pn.creation_date, '%Y%m%d') > STR_TO_DATE('20250614', '%Y%m%d')
问题:无法使用索引,每行都要计算。
解决方案:
-
长期:将
creation_date改为DATE类型。 -
短期(MySQL 8.0+):创建函数索引:
CREATE INDEX idx_pn_creation_expr ON project ((STR_TO_DATE(creation_date, '%Y%m%d')));
4. 谨慎使用 DISTINCT
检查是否因 JOIN 不精确导致重复。若可通过主键关联或聚合消除重复,则应移除 DISTINCT,大幅降低排序与临时表开销。
5. 分批次处理大数据查询
即使优化后,若 create_time 范围过大(如查整周),仍可能超限。
推荐做法:
- 定时任务每次只处理 1 小时或 1 天 的数据
- 应用层循环调用小窗口查询
// 伪代码
for (LocalDateTime windowStart = start; windowStart.isBefore(end); ) {LocalDateTime windowEnd = windowStart.plusHours(1);job.execute(windowStart, windowEnd);windowStart = windowEnd;
}
优化前后对比
| 指标 | 优化前 | 优化后 |
|---|---|---|
| 执行状态 | 失败(临时表满) | 成功 |
| 执行时间 | — | < 10 秒(原预估 > 2 分钟) |
| 临时表大小 | > 10GB(估算) | < 500MB |
| RDS CPU/IOPS | 高峰飙升 | 平稳 |
经验总结
- 不要迷信“几百万行不算大” —— 复杂查询会让中间结果指数级增长。
- RDS 的 tmp 空间是隐形瓶颈,无法直接扩容,必须从 SQL 入手。
- 重复 JOIN 同一张表是性能杀手,优先考虑聚合展开。
- 分页 or 分批 是处理大数据定时任务的黄金法则。
- EXPLAIN 是你的朋友:重点关注
Using temporary; Using filesort。
