故障分析 | MySQL 臨時表空間數據過多致使磁盤空間不足的問題排查

做者:宗楊
愛可生產品交付團隊成員,主要負責公司運維平臺和數據庫運維故障診斷。喜好數據庫、容器等技術,愛好歷史、追劇。
本文來源:原創投稿
*愛可生開源社區出品,原創內容未經受權不得隨意使用,轉載請聯繫小編並註明來源。

1、事件背景

咱們的合做客戶,駐場人員報告說一個 RDS 實例出現磁盤不足的告警,須要排查。html

告警信息:mysql

image

告警內容:
數據庫 data 磁盤不足,磁盤佔用 80% 以上
數據庫 binlog 磁盤不足,磁盤佔用 80% 以上sql

2、排查過程

登錄告警的服務器,查看磁盤空間,並尋找大容量文件後,發現端口號爲 4675 的實例臨時表空間 ibtmp1 的大小有 955G,致使磁盤被使用了 86%;數據庫

image

猜想和庫裏執行長 SQL 有關係,產生了不少臨時數據,並寫入到臨時表空間。     服務器

image

看到有這樣一條 SQL,繼續分析它的執行計劃;less

image

很明顯看到圖中標記的這一點爲使用了臨時計算,說明臨時表空間的快速增加和它有關係。這條 SQL 進行了三表關聯,每一個表都有幾十萬行數據,三表關聯並無在 where 條件中設置關聯字段,造成了笛卡爾積,因此會產生大量臨時數據;並且都是全表掃描,加載的臨時數據過多;還涉及到排序產生了臨時數據;這幾方面致使 ibtmp1 空間快速爆滿。運維

3、解決辦法

和項目組溝通後,殺掉這個會話解決問題;優化

image

image

可是這個 SQL 停下來了,臨時表空間中的臨時數據沒有釋放;spa

image

最後經過重啓 mysql 數據庫,釋放了臨時表空間中的臨時數據,這個只能經過重啓釋放。操作系統

image

4、分析原理

經過查看官方文檔,官方是這麼解釋的:

image

翻譯:

image

根據官網文檔的解釋,在正常關閉或初始化停止時,將刪除臨時表空間,並在每次啓動服務器時從新建立。重啓可以釋放空間的緣由在於正常關閉數據庫,臨時表空間就被刪除了,從新啓動後從新建立,也就是重啓引起了臨時表空間的重建,從新初始化,因此,重建後的大小爲 12M。

從錯誤日誌裏能夠驗證上面的觀點:

image

5、官網對於 ibtmp1 大小的說明

image

image

6、如何避免

1. 對臨時表空間的大小進行限制,容許自動增加,但最大容量有上限,本例中因爲 innodb_temp_data_file_path 設置的自動增加,但未設上限,因此致使 ibtmp1 有 955G。正確方法配置參數 innodb_temp_data_file_path:[mysqld]innodb_temp_data_file_path=ibtmp1:12M:autoextend:max:500M參考官方文檔:

image

image

設置了上限的大小,當數據文件達到最大大小時,查詢將失敗,並顯示一條錯誤消息,代表表已滿,查詢不能往下執行,避免 ibtmp1 過大。

2. 在發送例如本例中的多表關聯 SQL 時應確保有關聯字段並且有索引,避免笛卡爾積式的全表掃描,對存在 group by、order by、多表關聯的 SQL 要評估臨時數據量,對 SQL 進行審覈,沒有審覈不容許上線執行。

3. 在執行前經過 explain 查看執行計劃,對 Using temporary 須要格外關注。

7、其餘補充

1> 經過字典表查看執行的 SQL 產生臨時表、使用臨時表空間的狀況:查詢字典表:sys.x$statements_with_temp_tablesselect * from sys.x$statements_with_temp_tables where query like 'select%' and db='test' order by tmp_tables_to_disk_pct,disk_tmp_tables descG;

image

查詢字典表:sys.statements_with_temp_tablesselect * from sys.statements_with_temp_tables where query like 'select%' and db='test' order by tmp_tables_to_disk_pct,disk_tmp_tables descG;

image

這兩個表查詢的結果是同樣的,各列含義以下:
query:規範化的語句字符串。
db:語句的默認數據庫, NULL 若是沒有。
exec_count:語句已執行的總次數。
total_latency:定時出現的語句的總等待時間。
memory_tmp_tables:由該語句的出現建立的內部內存臨時表的總數。disk_tmp_tables:由該語句的出現建立的內部磁盤臨時表的總數。
avg_tmp_tables_per_query:每次出現該語句建立的內部臨時表的平均數量。
tmp_tables_to_disk_pct:內部內存臨時表已轉換爲磁盤表的百分比。
first_seen:第一次看到該聲明的時間。
last_seen:最近一次發表該聲明的時間。
digest:語句摘要。

參考連接:https://dev.mysql.com/doc/ref...
經過字典表 tmp_tables_to_disk_pct 這一列結果可知,內存臨時表已轉換爲磁盤表的比例是 100%,說明經過復現這個查詢,它的臨時計算結果已經都放到磁盤上了,進一步證實這個查詢和臨時表空間容量的快速增加有關係。

2> 對於 mysql5.7 中 kill 掉運行長 SQL 的會話,ibtmp1 容量卻沒有收縮問題的調研;來源連接:http://mysql.taobao.org/month...

image

從文章中的解釋看,會話被殺掉後,臨時表是釋放的,只是在 ibtmp1 中打了刪除標記,空間並無還給操做系統,只有重啓才能夠釋放空間。

3> 下面,進一步用 mysql8.0 一樣跑一下這個查詢,看是否有什麼不一樣;mysql 版本:8.0.18

image

image

image

當這個 sql 將磁盤跑滿以後,發現與 5.7 不一樣的是這個 SQL 產生的臨時數據保存到了 tmpdir,mysql5.7 是保存在 ibtmp1 中,並且因爲磁盤滿,SQL 執行失敗,很快磁盤空間就釋放了;

問題:如何使用到 8.0 版本的臨時表空間?

經過查看 8.0 的官方文檔得知,8.0 的臨時表空間分爲會話臨時表空間和全局臨時表空間,會話臨時表空間存儲用戶建立的臨時表和當 InnoDB 配置爲磁盤內部臨時表的存儲引擎時由優化器建立的內部臨時表,當會話斷開鏈接時,其臨時表空間將被截斷並釋放回池中;也就是說,在 8.0 中有一個專門的會話臨時表空間,當會話被殺掉後,能夠回收磁盤空間;而原來的 ibtmp1 是如今的全局臨時表空間,存放的是對用戶建立的臨時表進行更改的回滾段,在 5.7 中 ibtmp1 存放的是用戶建立的臨時表和磁盤內部臨時表;
也就是在 8.0 和 5.7 中 ibtmp1 的用途發生了變化,5.7 版本臨時表的數據存放在 ibtmp1 中,在 8.0 版本中臨時表的數據存放在會話臨時表空間,若是臨時表發生更改,更改的 undo 數據存放在 ibtmp1 中;

image

image

image

image

image

實驗驗證:將以前的查詢結果保存成臨時表,對應會話是 45 號,經過查看對應字典表,可知 45 號會話使用了 temp_8.ibt 這個表空間,經過把查詢保存成臨時表,能夠用到會話臨時表空間,以下圖:

image

下一步殺掉 45 號會話,發現 temp_8.ibt 空間釋放了,變爲了初始大小,狀態爲非活動的,證實在 mysql8.0 中能夠經過殺掉會話來釋放臨時表空間。

image

總結:在 mysql5.7 時,殺掉會話,臨時表會釋放,可是僅僅是在 ibtmp 文件裏標記一下,空間是不會釋放回操做系統的。若是要釋放空間,須要重啓數據庫;在 mysql8.0 中能夠經過殺掉會話來釋放臨時表空間。

8、參考文檔

https://dev.mysql.com/doc/ref...://dev.mysql.com/doc/refman/8.0/en/innodb-temporary-tablespace.html http://mysql.taobao.org/month...
相關文章
相關標籤/搜索