MySQL:查询在不同环境中执行的方式不同

Posted

技术标签:

【中文标题】MySQL:查询在不同环境中执行的方式不同【英文标题】:MySQL: query executed differently in different environments 【发布时间】:2013-11-18 22:56:47 【问题描述】:

我正在生成文件中创建一堆表。我的 make 目标看起来像:

TASK:
    cat script.sql | mysql -v -v -v dbName

script.sql 内部,create table 命令之一在mysql 进程处于 100% CPU 时无限期挂起。

如果我在同一台机器上与同一用户运行相同的命令,但从命令行运行,它运行良好。

$ cat script.sql | mysql -v -v -v dbName

再深入研究一下,发现explain 在两种环境中会产生不同的结果。

从内部制作:

+----+-------------+-------+--------+---------------+---------+---------+----------------------------------------+------+----------------------------------------------------+
| id | select_type | table | type   | possible_keys | key     | key_len | ref                                    | rows | Extra                                              |
+----+-------------+-------+--------+---------------+---------+---------+----------------------------------------+------+----------------------------------------------------+
|  1 | SIMPLE      | o     | ALL    | NULL          | NULL    | NULL    | NULL                                   | 2340 | NULL                                               |
|  1 | SIMPLE      | d     | index  | NULL          | PRIMARY | 3       | NULL                                   | 2739 | Using index; Using join buffer (Block Nested Loop) |
|  1 | SIMPLE      | p     | eq_ref | PRIMARY       | PRIMARY | 7       | db1.o.field1,db3.d.date                |    1 | Using where                                        |
|  1 | SIMPLE      | n     | ALL    | PRIMARY       | NULL    | NULL    | NULL                                   |    1 | Using where; Using join buffer (Block Nested Loop) |
+----+-------------+-------+--------+---------------+---------+---------+----------------------------------------+------+----------------------------------------------------+

从命令行:

+----+-------------+-------+--------+---------------+---------+---------+----------------------------------------+------+----------------------------------------------------+
| id | select_type | table | type   | possible_keys | key     | key_len | ref                                    | rows | Extra                                              |
+----+-------------+-------+--------+---------------+---------+---------+----------------------------------------+------+----------------------------------------------------+
|  1 | SIMPLE      | o     | ALL    | NULL          | NULL    | NULL    | NULL                                   | 2340 | NULL                                               |
|  1 | SIMPLE      | d     | index  | NULL          | PRIMARY | 3       | NULL                                   | 2739 | Using index; Using join buffer (Block Nested Loop) |
|  1 | SIMPLE      | p     | eq_ref | PRIMARY       | PRIMARY | 7       | db1.o.field1,db3.d.date                |    1 | Using where                                        |
|  1 | SIMPLE      | n     | ref    | PRIMARY       | PRIMARY | 4       | db2.p.field1                           |    1 | Using where                                        |
+----+-------------+-------+--------+---------------+---------+---------+----------------------------------------+------+----------------------------------------------------+

一些挖掘将我定向到this question,并在其中一个涉及的表上运行analyze 确实解决了这个问题。

但是说真的,这里发生了什么?是否有一些环境变量导致 mysql 行为不同?

有问题的查询如下所示:

drop view if exists v;
create view v as (
    select *
    from db1.order o
    cross join db3.dates d
    left join db2.price p on (1=1
        and p.id = o.id 
        and p.date = d.date
        and p.volume > 0)
    left join db3.security n on (1=1
        and n.id = p.id
        and n.date <= d.date)
);

explain select * from v;
analyze table n; 
explain select * from v;

create table t (
    primary key (date asc, id asc)
) as (
    select * from v
);

make 内部,第一个explain 产生上面的第一个结果,然后analyze 导致第二个explain 产生上面的第二个结果。

【问题讨论】:

很好的例子,但这可能更适合 dba.stackexchange 【参考方案1】:

怀疑 script.sql 是否相等。根据您的EXPLAIN 输出。

JOIN顺序相同,但第三个表'p'的引用表不同。 在 shell 中执行时,'p' 引用 'db3.d' 但在 Make 中,'p' 引用 'db2.d'

这就是我怀疑的原因。

您可以发布您的查询吗?如果保密,重命名表,列。如果有子查询,可以有两个以上的表别名。但是好像没有子查询。

this question你给我的与你无关。他有新环境,更改表统计信息需要ANALYZE

要找出两个 sql 确实是相同的打开General log。这很简单。在script.sql的第一行添加SET GLOBAL general_log = 'ON',在sql的末尾添加SET GLOBAL general_log = 'OFF'

你觉得我的意见怎么样?

更新

好的,script.sql 被排除在外。然后我不知道为什么两个运行方式不同。 MySQL forums 可以帮助你。

顺便说一句,我可以告诉你一些信息。

script.sql 是如何工作的? CREATE VIEWSELECT .. FROM view 是 script.sql 的一部分或全部。是否在 db3.security 或其他表上创建或插入?如果你发布 MySQL 论坛,最好描述一下 script.sql 是如何工作的。

使用索引您是否尝试过明确使用USE INDEX?大多数内表'n' 正在进行全扫描。

innodb_stats_sample_pages 如果你使用 InnoDB,最后在 my.cnf 中设置 innodb_stats_sample_pages=64(默认为 8)。当 innodb 表打开时,MySQL 读取 8 个随机页面,这些页面用于汇总表的统计信息(此统计信息用于连接成本)。所以统计数据可能会改变每个表打开(它是读取随机页面)。更多示例页面准确统计。 (对不起我的英语不好)

【讨论】:

感谢您的回答。脚本是相同的,我只是错误地混淆了输出。我会更正我的帖子。事实上,虽然运行 analyze 可以解决问题。 @davez0r 我已经更新了我的答案。但这对你没有多大帮助。

以上是关于MySQL:查询在不同环境中执行的方式不同的主要内容,如果未能解决你的问题,请参考以下文章

为啥相同的查询在从 MySQL 服务器上的解释和执行如此不同?

相同的查询在重复执行时返回不同的值

在 phpmyadmin 和 SQL Fiddle 上执行时相同 SELECT 查询的不同结果

Mysql执行计划功能详解

如何迭代优化 MySQL 查询?

mysql 优化