调用存储过程的 MySQL 开销

Sla*_*mir 5 mysql stored-procedures jdbc mysql-5.5

在 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()
Run Code Online (Sandbox Code Playgroud)

这是服务器端的预期行为吗?只是看起来效率不高。

以下是我们如何使用 API 的示例:

  conn = DriverManager.getConnection(..);

  stmt = conn.prepareCall("{call sp_one(?,?)}");

  stmt.setString(1, "a");
  stmt.setString(2, "b");

  stmt.execute();
Run Code Online (Sandbox Code Playgroud)

请注意以下情况是正确的:

  • 未使用 CallableStatement.getMetadata()
  • 参数按索引访问,而不是按名称访问

谢谢!

Rol*_*DBA 1

Percona 写了一篇很好的文章MySQL 准备语句解释了优点和缺点

\n\n
\n

因此,有充分的理由使用准备好的语句:

\n\n
    \n
  1. 节省查询解析

  2. \n
  3. 节省数据转换和复制

  4. \n
  5. 避免 SQL 注入

  6. \n
  7. 节省处理 blob 的内存

  8. \n
\n\n

使用准备好的语句也有缺点和困难:

\n\n
    \n
  1. 查询缓存不起作用

  2. \n
  3. 如果语句仅使用一次,则需要额外的服务器往返

  4. \n
  5. 并非所有报表都可以准备。因此,您可以\xe2\x80\x99t 专门使用准备好的API,您\xe2\x80\x99 需要针对某些语句回退到普通API

  6. \n
  7. 较新且有时有错误的代码。我在使用 PHP 准备好的语句时遇到了很多问题。它正在变得更好,但仍然不如标准 API 成熟

  8. \n
  9. 您可以\xe2\x80\x99t 使用占位符代替所有标识符。例如,您可以\xe2\x80\x99t 将它们用作表名。在某些版本中,它甚至不适用于 LIMIT 边界

  10. \n
  11. 列表处理不方便。与 PEAR 模拟\n 准备好的语句不同,没有很好的方法将值列表传递给 IN

  12. \n
  13. 更难追踪。日志现在已修复为包含完整的语句文本,不仅\n\n\xe2\x80\x9cExecute\xe2\x80\x9d,但在 SHOW INNODB STATUS 中,您仍然会看到\n没有实际值的语句\xe2\x80\x93,这对于分析来说非常不方便

  14. \n
\n
\n\n

由于您遇到的是预期行为,因此您不应使用准备语句。您正在进行多次调用来执行单个命令。如果你只是运行这个

\n\n
stmt = conn.createStatement();\nrs = stmt.executeQuery("call sp_one(\'a\',\'b\')");\n
Run Code Online (Sandbox Code Playgroud)\n\n

SQL的完整解析、执行准备以及SQL语句已经传入的参数都在服务器端通过一次调用完成。

\n\n

阅读 Percona 博客了解更多详细信息

\n