Mysql
MySQL 呼叫儲存過程的成本
在 Java 中,我們通過 JDBC 訪問 MySQL(v5.5.40)——一個準備語句,設置參數然後呼叫儲存過程。似乎每個 execute() 在實際 SP 呼叫之前都有 4 個元呼叫(每個 MySQL 查詢日誌)的成本:
SELECT name, type, comment FROM mysql.proc WHERE name like... SHOW FUNCTION STATUS LIKE 'sp_one' SHOW PROCEDURE STATUS LIKE 'sp_one' SHOW CREATE PROCEDURE `db-name`.`sp_one` CALL sp_one()
這是伺服器端的預期行為嗎?它似乎不是很有效。
以下是我們如何使用 API 的範例:
conn = DriverManager.getConnection(..); stmt = conn.prepareCall("{call sp_one(?,?)}"); stmt.setString(1, "a"); stmt.setString(2, "b"); stmt.execute();
請注意,以下情況屬實:
- 未使用 CallableStatement.getMetadata()
- 參數按索引訪問,而不是按名稱訪問
謝謝!
Percona 寫了一篇很好的文章MySQL Prepared Statements解釋了利弊
所以有充分的理由使用準備好的語句:
- 節省查詢解析
- 節省數據轉換和複製
- 避免 SQL 注入
- 節省處理 blob 的記憶體
使用準備好的語句也有缺點和注意事項:
- 查詢記憶體不起作用
- 如果語句僅使用一次,則需要額外的伺服器往返
- 並非所有語句都可以準備好。因此,您不能專門使用準備好的 API,您需要為某些語句回退到普通 API
- 更新的,有時是錯誤的程式碼。我在使用 PHP 準備語句時遇到了很多問題。它正在變得更好,但仍然不如標準 API 成熟
- 您不能使用佔位符代替所有標識符。例如,您不能將它們用作表名。在某些版本中,它甚至不適用於 LIMIT 邊界
- 不方便的列表處理。與例如 PEAR 模擬的準備好的語句不同,沒有很好的方法將值列表傳遞給 IN
- 更難追踪。日誌現在已修復,不僅包含完整的語句文本“執行”,而且在 SHOW INNODB STATUS 中,您仍然會看到沒有實際值的語句 - 非常不方便分析
由於您遇到的是預期行為,因此您不應使用準備語句。您正在多次呼叫以執行單個命令。如果你只是執行這個
stmt = conn.createStatement(); rs = stmt.executeQuery("call sp_one('a','b')");
SQL的完整解析,準備執行,以及已經通過SQL語句傳入的參數,都是在伺服器端一次呼叫完成的。
我只是想發布這些發現:我們在使用 connector/j 呼叫儲存過程時也遇到了這個問題。在探勘原始碼後,如果 mysql 使用者沒有對 mysql.proc 的讀取權限,它將擷取異常並繼續“SHOW …”呼叫。這些 SHOW 呼叫,至少對我們而言,是將 tmp 表寫入磁碟。我們在 mysql.proc 上授予 select 的那一刻,問題就消失了。簡單地應用“noAccessToProcedureBodies”並沒有成功。