記一次mysql的preparedStatement使用超限問題


【現象&背景】

        本服務是個為數據庫的分庫分表提供路由規則計算,sql過濾執行的中間服務。即上游將請求發給本服務,本服務根據分庫分表規則將相應的sql執行發送給對應的分庫、分表去執行,並整理結果返回給上游。

        2016-07-14下午,上游流量切換后,mysql大量報出" Can't create more than max_prepared_stmt_count statements (current value: 16382)",mysql不能工作導致本層數據庫路由服務不能工作,大量請求失敗。

        大概意思就是說,同一時間在mysqld上所有session中preparedStatement語句超過了mysql的限制,導致新建preparedStatement失敗,產生錯誤。

        臨時解決:上游回退。

【原因分析】

  • 為什么會建立這么多的preparedStatement?

        1)其中有個接口由於bug導致會對所有分庫、分表進行全表sql執行操作,而每個sql執行前都會新建一個preparedStatement

        2)上游切換流量后使用的新庫擴大了分庫數和分表數,導致一個請求對應分表數量成倍數增加,那一個請求對應preparedStatement數量也成倍數增加。

  • preparedStatement是為何物?

        preparedStatement是在多次重復執行只用參數值不同的sql語句時,先發一個請求去mysql服務端將sql語句編譯好,並將其預編譯結果存儲在preparedStatement對象中,以后便可以使用該對象高效地多次執行該語句而不用預編譯,直接使用數據庫的緩沖區,提高數據庫訪問的效率。

【preparedStatement】

  • 原理

上圖為mysql查詢執行過程,包括:

1、傳輸sql給數據庫

2、服務器先檢查查詢緩存,如果命中,吉利返回結果;否則進入下一階段

3、數據庫驗證&解析sql

4、計算執行計划

5、根據執行計划調用存儲引擎的api執行查詢

6、返回數據

        在上面步驟中,第4步非常耗時。為了提高性能,數據庫會緩存執行語句及其執行計划。但是,sql語句本身為key,執行計划為value,sql語句中只要有一個字節不一樣就不能命中緩存。

        因此,有了preparedStatement,能夠提高在只有參數不一樣的sql的緩存命中率。

        preparedStatement創建的時候,會將參數化的語句發給數據庫,進行語法檢測和執行計划計算。sql語句被預編譯並且存儲在preparedStatement對象中,下次執行相同的sql語句時,數據庫不會再進行預編譯,而是直接使用數據庫的緩沖區,提高數據庫的訪問效率。

 

  • 作用

        1、代碼可讀性&可維護性

        2、在批量執行時提高cache命中率,提高性能

        3、提高安全性,防止sql注入

 

  • 適用場景

        有大量的sql結構相同,只有數值不同的sql請求時。或者處於對寫入的sql的安全考慮。

 

【解決】

        由於執行的sql是由上游傳過來的,並不能確保sql的結構相同,所以其實使用preparedStatement並不能提高緩存的命中率,只能做防止sql注入的功能;並且,如果每次sql前都使用preparedStatement,還為每次sql執行增加了一次網絡交互,得不償失。因此,解決方法就是沒有使用preparedStatement進行預編譯,而是直接使用Statement執行sql語句。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM