Mysql

MySQL 呼叫儲存過程的成本

  • November 15, 2016

在 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解釋了利弊

所以有充分的理由使用準備好的語句:

  1. 節省查詢解析
  2. 節省數據轉換和複製
  3. 避免 SQL 注入
  4. 節省處理 blob 的記憶體

使用準備好的語句也有缺點和注意事項:

  1. 查詢記憶體不起作用
  2. 如果語句僅使用一次,則需要額外的伺服器往返
  3. 並非所有語句都可以準備好。因此,您不能專門使用準備好的 API,您需要為某些語句回退到普通 API
  4. 更新的,有時是錯誤的程式碼。我在使用 PHP 準備語句時遇到了很多問題。它正在變得更好,但仍然不如標準 API 成熟
  5. 您不能使用佔位符代替所有標識符。例如,您不能將它們用作表名。在某些版本中,它甚至不適用於 LIMIT 邊界
  6. 不方便的列表處理。與例如 PEAR 模擬的準備好的語句不同,沒有很好的方法將值列表傳遞給 IN
  7. 更難追踪。日誌現在已修復,不僅包含完整的語句文本“執行”,而且在 SHOW INNODB STATUS 中,您仍然會看到沒有實際值的語句 - 非常不方便分析

由於您遇到的是預期行為,因此您不應使用準備語句。您正在多次呼叫以執行單個命令。如果你只是執行這個

stmt = conn.createStatement();
rs = stmt.executeQuery("call sp_one('a','b')");

SQL的完整解析,準備執行,以及已經通過SQL語句傳入的參數,都是在伺服器端一次呼叫完成的。

閱讀 Percona 部落格了解更多詳情

我只是想發布這些發現:我們在使用 connector/j 呼叫儲存過程時也遇到了這個問題。在探勘原始碼後,如果 mysql 使用者沒有對 mysql.proc 的讀取權限,它將擷取異常並繼續“SHOW …”呼叫。這些 SHOW 呼叫,至少對我們而言,是將 tmp 表寫入磁碟。我們在 mysql.proc 上授予 select 的那一刻,問題就消失了。簡單地應用“noAccessToProcedureBodies”並沒有成功。

引用自:https://dba.stackexchange.com/questions/98813