记录一下那天晚上的排查过程。统计报表的 group by 查询跑一次要三分钟,监控里 Created_tmp_disk_tables 每分钟涨几百,/tmp 一天被写满一次,MySQL 8.0 的 tmp_table_size 只有 16M。
刚开始盯着「分组字段是 text 类型,内存临时表放不下」,折腾了半个多小时,排掉了。
真正麻烦的地方在于,几个原因的现象几乎一样。分组字段没有索引,只能建内部临时表再聚合。还有一种是tmp_table_size 太小,内存表一超就整体落盘。
不容易想到的是,查询里同时有 distinct 和 order by,多排了一次。
动手时先确认临时表是内存还是落了磁盘
mysql -e "show global status like 'Created_tmp%tables';"
给分组字段补索引减少临时表
mysql -e "alter table order_stat add index idx_shop_date(shop_id,stat_date);"
适当放大内存临时表的上限
mysql -e "set global tmp_table_size=268435456; set global max_heap_table_size=268435456;"
复查执行计划里的临时表标志
mysql -e "explain select shop_id,count(*) from order_stat group by shop_id\G"
怎么算真的好了,看的是同一批现象。执行计划里 Using temporary 变成 Using index,Created_tmp_disk_tables 不再增长,报表查询降到十秒内。
补一句。内存临时表一旦超过上限是整体转成磁盘表,不是部分落盘;报表类计算提前聚合到汇总表,别让线上库承担。拖得越久,排查成本越高,因为现场状态会被后续操作覆盖。
