一、問題描述
1、生產環境
MySQL
為5.7.10
,Connect Timeout=45
超時時間設置為45s
,啟用連接池,最大連接池設置為2000
,數據庫服務器2臺,讀寫分離。
2、問題呈現
今天早上一個業務接口突然出現問題,經查詢,5臺服務器都不可訪問,日志為查詢數據庫超時Timeout expired. The timeout period elapsed prior to completion of the operation or the server is not responding. ---> System.TimeoutException: Timeout in IO operation
。
二、原因探尋
既然超時,那就要查詢該接口到底是哪里慢了。拿到該接口執行的sql
,直接在dbForge Studio for MySQL
執行,在從庫上(客戶端只有從庫的讀權限,生產上使用的主庫)執行時間為3.78s
,雖然慢,但是執行時間遠小于設置的超時時間。
懷疑是否是由于數據庫連接池滿了,連接數達到上限。于是查詢數據庫的連接數,小于峰值。同時異常日志是IO
超時,而不是超過最大連接數。那為什么會超時呢?
組內同事聯系運維同事,讓他幫忙把該SQL
在主庫查詢,竟然需要22s
。運維同事說,在9:40~10:00
這時間段,主庫壓力太大,導致超時。從庫的壓力較小。運維建議使用從庫。
同時該查詢語句也屬于慢查詢,準備優化。
查看執行計劃:
p
表進行全表掃描Full Table Scan
。p
表建立的是聯合索引,但是查詢的字段中有聯合索引之外的字段,導致全表掃描。好,那就去查詢去除不包含在索引中的字段,重新查詢,查看執行計劃:
p
表為全索引掃描Full Index Scan
。但是查詢并沒有變快。
與全表掃描比較,索引全掃描有如下兩好處:1:掃描的塊更少,因為索引只儲存部分字段的數據;2: 索引是順序存儲的,在某些條件下,可以避免額外的排序操作
把原始的查詢SQL
(包含不在聯合索引中的字段)放在測試數據庫執行,執行時間不到200ms
,查看執行計劃:
p
表沒有全表掃描Full Table Scan
,而是Non-Unique Key Lookup
非唯一索引查詢。那這就奇怪啦?為什么呢?
難度是測試庫和生產環境索引不一致,排查了下,完全一致。難道是由于數據量不一致導致的?由于把生產庫所有相關表的表結構及數據導入到測試服務器新建的數據。
查看執行計劃查詢時間為78ms
。
那么問題來了,這么大的差異,是由于什么導致的呢?數據庫的設置?
聯系運維同事,讓幫忙比較下測試庫與生產庫數據庫的設置。
經查詢,生產庫優化策略禁止derived table
合并到外層的Query
中derived_merge
了set optimizer_switch='derived_merge=off'
。
在測試環境禁止止derived table
,執行SQL
。
查看執行計劃:
執行時間
3.92s
,p
全表掃描Full Table Scan
。終于找出來了,是由于生產環境與測試環境優化方式不一致導致的。而前段時間是由于測試前段時間遷移了數據庫,所以derived_merge
默認開啟。
三、解決方式
既然知道問題所在,那就解決了。
- 目前主從同步,平均在秒內可完成,由于該接口對數據的實時性要求不是很高,可使用從庫查詢數據。
- 上面我們已經知道由于生產環境未開啟
SQL
優化derived_merge
,那最直接的方法當然是開啟,set optimizer_switch='derived_merge=on'
。問題來了,在MySQL 5.7.10
版本啟用合并,會將子查詢中的SQL合并到外部查詢中,會發生ER_UPDATE_TABLE_USED
錯誤,那生產庫主庫暫時不能啟用優化。解決方式是:從庫啟用查詢優化。
四參考
Changes Affecting Upgrades to MySQL 5.7