【IT168 技術】相同查詢在數據量相近的情況下在不同 RDS for MySQL 實例上有不同的性能表現,容易引發用戶對 RDS for MySQL 實例的性能差異性的疑慮,本文分享下近期碰到的一個原因比較隱蔽但很常見的案例。
1. 問題出現
一個用戶的下述查詢在 RDS for MySQL 實例 A 上執行需要 30+ 毫秒,而在 RDS for MySQL 實例 B 執行需要 12+ 秒。
2. 問題原因
排查 SQL 在 RDS for MySQL 實例 A 和 B 上的執行計劃,發現不一致。執行時間短 - A:
執行時間長 - B:
從執行計劃對比看問題出現在 表 A 和 中間表 B 關聯這步。
執行計劃 B 的 Extra 信息顯示 Using join buffer (Block Nested Loop),說明如果選擇單純的 Nested Loop Join 成本會很高(在內層循環無法使用索引的場景下,成本是 O(Rn x Sn))。
優化器為了提高效率,因此選擇了 Block Nested Loop。
對比執行計劃 A,內層使用的索引是 MySQL 自動創建的(auto_key1),檢查優化器開關配置是否有區別,以防萬一。
對比兩個實例優化器開關配置相同,且 materialization 和 subquery_materialization_cost_based 都已經打開, 加之執行計劃 B 中有物化表的使用,因此排除掉優化器開關配置問題。
此時問題就比較明朗了,應該是關聯的兩個欄位類型不匹配,導致無法通過索引物化臨時表的關聯欄位來使用 Nested Loop Join。
帶著上面的懷疑檢查下兩個實例的表 Pay 和 inv_msg 的關聯欄位 PayId 的欄位類型。
可以看到 payId 欄位在執行快場景下 2 個表都是 big int 類型;而在執行慢的場景下,2個表的欄位類型分別為 big int 和 varchar,導致執行計劃選擇了對無法使用索引場景優化的 Block Netsted Loop。
3. 問題解決
理清問題的根源,就有了針對性的方法。建議用戶修改 表 inv_msg 的欄位 payid 類型為 big int not null,重新收集統計信息後問題解決。
4. 問題結論
需要嚴格遵守規範進行開發工作。
用戶 DBA 應該進行 SQL 審核工作。