mysql存储过程比标准查询慢20倍

时间:2013-05-25 18:58:34

标签: mysql stored-procedures

我有10个表,除了表名以外具有相同的结构。

我的sp(存储过程)定义如下:

 select * from table1 where (@param1 IS NULL OR col1=@param1)
 UNION ALL
 select * from table2 where (@param1 IS NULL OR col1=@param1)
 UNION ALL
 ...
 ...
 UNION ALL
 select * from table10 where (@param1 IS NULL OR col1=@param1)

我用以下行调用sp:

call mySP('test')  //it executes in 6,836s

然后我打开了一个新的标准查询窗口。我刚刚复制了上面的查询。然后用' test'替换@ param1。

这在0,321秒内执行,比存储过程快20倍。

我重复更改了参数值,以防止缓存结果。但这并没有改变结果。 SP比同等标准查询慢约20倍。

请你帮我弄清楚为什么会这样?

有没有人遇到过类似的问题?

我在Windows Server 2008 R2 64位上使用mySQL 5.0.51。

编辑:我正在使用Navicat进行测试。

任何想法对我都有帮助。

EDIT1:

根据Barmar的回答,我刚做了一些测试。

最后我改变了下面的sp,只有一行:

 SELECT * FROM table1 WHERE col1=@param1 AND col2=@param2

然后我首先执行标准查询

 SELECT * FROM table1 WHERE col1='test' AND col2='test'  //Executed in 0.020s

我打电话给我的sp:

 CALL MySp('test','test')    //Executed in 0.466s

所以我完全改变了where子句但没有改变。我从mysql命令窗口调用sp而不是navicat。它给出了相同的结果。我仍然坚持下去。

my sp ddl:

 CREATE DEFINER = `myDbName`@`%`
 PROCEDURE `MySP` (param1 VARCHAR(100), param2 VARCHAR(100))
 BEGIN
    SELECT * FROM table1 WHERE col1=param1 AND col2=param2
 END

并且col1和col2被组合索引。

你可以说为什么不使用标准查询呢?我的软件设计不合适。我必须使用存储过程。所以这个问题对我来说非常重要。

EDIT2:

我已经获得了查询个人资料信息。差异很大是因为"发送数据行"在SP配置文件信息中。发送数据部分占查询执行时间的%99。我正在本地数据库服务器上进行测试。我没有从远程计算机连接。

SP个人资料信息    SP Profile Information

查询个人资料信息   enter image description here

我在我的sp中试过了下面的强制索引语句。但结果相同。

 SELECT * FROM table1 FORCE INDEX (col1_col2_combined_index) WHERE col1=@param1 AND col2=@param2

我已经改变了下面的sp。

 EXPLAIN SELECT * FROM table1 FORCE INDEX (col1_col2_combined_index) WHERE col1=param1 AND col2=param2

这给出了这个结果:

 id:1
 select_type=SIMPLE
 table:table1
 type=ref
 possible_keys:NULL
 key:NULL
 key_len:NULL
 ref:NULL
 rows:292004
 Extra:Using where

然后我执行了下面的查询。

 EXPLAIN SELECT * FROM table1 WHERE col1='test' AND col2='test'

结果是:

 id:1
 select_type=SIMPLE
 table:table1
 type=ref
 possible_keys:col1_co2_combined_index
 key:col1_co2_combined_index
 key_len:76
 ref:const,const
 rows:292004
 Extra:Using where

我在SP中使用FORCE INDEX语句。但它坚持不使用索引。任何的想法?我想我即将结束:)

4 个答案:

答案 0 :(得分:9)

只是一个猜测:

当您手动运行查询时,可以在解析查询时优化表达式WHERE ('test' IS NULL or COL1 = 'test')。解析器可以看到字符串'test'不为空,因此它将测试转换为WHERE COL1 = 'test'。如果COL1上有索引,则会使用此索引。

但是,在创建存储过程时,会在创建过程时进行解析。那时,它不知道@param将是什么,并且必须将查询实现为对表的顺序扫描。

尝试将您的程序更改为:

IF @param IS NULL
THEN BEGIN
  SELECT * FROM table1
  UNION ALL
  SELECT * FROM table2
  ...
END;
ELSE BEGIN
  SELECT * FROM table1 WHERE col1 = @param
  UNION ALL
  SELECT * FROM table2 WHERE col1 = @param
  ...
END;
END IF;

我对MySQL存储过程没有太多经验,所以我不确定这是否是正确的语法。

答案 1 :(得分:4)

可能的字符集问题?如果表格字符集与数据库字符集不同,则可能会导致问题。

请参阅此错误报告:http://bugs.mysql.com/bug.php?id=26224

  

[2007年11月12日21:32] Mark Kubacki 5.1.22_rc仍然没有运气 - 键   在进行中,查询在36秒内和外面进行   0.12S。

     

[2007年11月12日22:30] Mark Kubacki将字符集更改为UTF-8(特别是使用的两个)后,用于   无论如何连接,在存储的内部考虑密钥   程序!

     

我无法回答的问题是:为什么优化器会处理字符集   在存储过程内外转换另一种方式?   (事实上​​,我可能会错误地问这个。)

答案 2 :(得分:0)

有趣的问题,因为我喜欢使用存储过程。原因是维护和封装原则。

这是我发现的信息: http://dev.mysql.com/doc/refman/5.1/en/query-cache-operation.html

它声明查询缓存不用于查询 1.是属于外部查询的子查询,和 2.在存储过程,触发器或事件的主体内执行。

这意味着它按设计工作。

答案 3 :(得分:0)

我见过这种行为,但它与字符集无关。

我有一张表,其中包含自我引用的分层数据(有孩子的父母,有些孩子有自己的孩子,等等)。由于parent_id必须引用主id(并且该列指定了该效果的约束),因此我无法将父id设置为NULL或0(零)以将子项与父项取消关联,因此我只是将其引用为本身。

当我去运行存储过程来执行递归查询以查找特定父级的所有子级(在所有级别)时,查询占用了30到20之间。运行时间长40倍。我发现更改存储过程使用的查询以确保它排除了顶级父记录(通过指定WHERE parent_id!= id)恢复了查询的性能。

我正在使用的存储过程基于以下所示的存储过程: https://stackoverflow.com/questions/27013093/recursive-query-emulation-in-mysql