【问题标题】:Interpretation of a MySQL Explain and Suggestions to enhance query response timeMySQL解释和建议的解释以提高查询响应时间
【发布时间】:2012-12-22 01:08:58
【问题描述】:

我正在运行一个过滤后的聚合查询,希望获得一些关于如何获得更好的查询响应时间的反馈。

查询(运行,但平均超过 400 秒):

select data_date,sum(closeprice) from moving_avgs
where
    symbol in (select distinct symbol from moving_avgs
                where
                ma200_close     >= 5.00 and
                ma200_volume    >= 400000 and
                data_date   = (select min(data_date) from moving_avgs
                                where year(data_date) = 2007) 
                )
group by data_date;

我的 EXPLAIN 查询读取(格式化为在此环境中读取):

id:         1
select_type:    PRIMARY
table:      moving_avgs
type:       ALL
possible_keys:  NULL
key:        NULL
key_len:        NULL
ref:        NULL
rows:       6250033
Extra:      Using where; Using temporary; Using filesort

id:         2
select_type:    DEPENDENT SUBQUERY
table:      moving_avgs
type:       unique_subquery
possible_keys:  PRIMARY,symbol,data_date,ma200_close,ma200_volume
key:        PRIMARY
key_len:        29
ref:        func,const
rows:       1
Extra:      Using where

id:         3
select_type:    SUBQUERY
table:      moving_avgs
type:       index
possible_keys:  NULL
key:        data_date
key_len:        3
ref:        NULL
rows:       6250033
Extra:      Using where; Using index

我的 my.ini [mysqld] 和 [myisamchk] 部分读取(在 4GB 双处理器 AMD 笔记本电脑上运行):

[mysqld]
port        = 3306
socket      = /tmp/mysql.sock
skip-external-locking
key_buffer_size = 512M
max_allowed_packet = 20M
table_open_cache = 256
sort_buffer_size = 8M
read_buffer_size = 8M
read_rnd_buffer_size = 16M
myisam_sort_buffer_size = 256M
thread_cache_size = 8
query_cache_size= 132M
basedir=c:/wamp/bin/mysql/mysql5.5.24
log-error=c:/wamp/logs/mysql.log
datadir=c:/wamp/bin/mysql/mysql5.5.24/data
# Try number of CPU's*2 for thread_concurrency
thread_concurrency = 8

[myisamchk]
key_buffer_size = 256M
sort_buffer_size = 256M
read_buffer = 4M
write_buffer = 4M

谢谢!

【问题讨论】:

  • 您需要向我们展示表和索引定义。 诊断慢查询需要完整的表和索引定义,而不仅仅是描述或解释。也许您的表格定义不佳。也许索引没有正确创建。也许您认为您在该列上没有索引。没有看到表和索引定义,我们无法分辨。

标签: mysql sql database-performance


【解决方案1】:

您能列出 SHOW CREATE TABLE 的结果吗?

你也可以试试这个变种,看看需要多长时间:

SELECT  
    data_date,  
    sum(closeprice)  
FROM moving_avgs  
INNER JOIN  
(  
    SELECT distinct symbol  
    FROM moving_avgs  
    WHERE    
        ma200_close     >= 5.00 and    
        ma200_volume    >= 400000 and  
        data_date   =  
        (  
            SELECT min(data_date)  
            FROM moving_avgs  
            WHERE year(data_date) = 2007  
        )   
) symbols ON symbols.symbol = moving_avgs.symbol  
GROUP BY data_date;  

我怀疑缓慢的三个来源(组合或单独)。前两个背后的原因非常简单:

(1) 表上的索引可能没有设计得像它们应该的那样好。我在您的 EXPLAIN 信息中没有看到良好的索引使用情况。

(2) 在 WHERE 中设计子查询的方式可能会迫使引擎不使用您在“符号”上拥有的索引——否则索引可能会给您带来性能损失。 EXPLAIN 输出使它看起来像这种损失。

(3) 在不讨论索引滚动的情况下陈述 (2) 的另一种方式是,基于错误地推断出与外部查询的关系 (也就是说,它认为存在一种关系——你的查询是一个相关的子查询——并且它对该关系做出了错误的选择)。

[注意:您的 WHERE 编写方式,子查询不是相关查询,它可以有效地执行,并且 IN 可以相对有效地解决(尽管可能没有索引的好处);但是,引擎可能无法很好地解释这种情况——您确实有一些复杂的嵌套子查询情况,这可能会使引擎出错]。

在任何情况下,将子查询移动到连接可以解决这种情况,因为它消除了引擎试图低效地将子查询与查询的其余部分相关联的任何可能性。当子查询是连接的来源时,引擎必须先解决它,然后才能考虑查询的其余部分。这消除了关于子查询与引擎可能正在执行的其余查询之间关系的任何错误推断。

【讨论】:

  • 这是我的 CREATE TABLE KEYS(我也专注于我的索引)。无法修复整个 CREATE TABLE ddl...PRIMARY KEY (symbol,data_date), KEY symbol (symbol), INDEX data_date (data_date), INDEX closeprice (closeprice), INDEX ma200_close (ma200_close), INDEX ma200_volume (ma200_volume)
  • 那个基于连接的变体表现如何?
  • 现在在不到 70 秒的时间内返回结果集。对于一个 620 万行的数据库,我不知道这是否好。任何其他关于索引的建议将不胜感激。
【解决方案2】:

我怀疑这种情况:

(select min(data_date) from moving_avgs
                            where year(data_date) = 2007)

会很昂贵,因为它会计算每一行的年份,而且它不能使用data_date 上可能存在的任何索引(我们不知道,因为你还没有向我们展示了表和索引定义)。

如果data_date 上有索引,那么你可以让 MySQL 使用该索引,方法是将其更改为

(select min(data_date) from moving_avgs
where data_date between '01-01-2007' and '12-31-2007')

请注意,这可能不是 MySQL 指定日期的方式,但您明白了。你给它一个起点和终点,让它使用索引。在您要求它计算每一行的年份时,这是不可能的。

【讨论】:

  • 安迪,这很有意义。我也会试试这个。
  • 通过使用您的建议以及上述其他更改,查询结果集现在在 30 多秒内返回。谢谢!
  • @Andy。出色的。我错过了年份的意义(data_date)。
  • @DWright:这是发布到 SO 的 SQL 问题中非常常见的问题。请参阅今天早些时候的this question。由于没有正确地将条件视为范围,我在自己的代码中被咬了很多次,现在它只是反射成为我首先寻找的东西之一。
【解决方案3】:

通过 1) 创建和设置两个 my.ini 变量:

max_heap_table_size = 256M

tmp_table_size = 512M

并且,2)增加第三个变量:

myisam_sort_buffer_size = 256M

3) 删除三个单字段索引并用一个四字段索引替换它(INDEX: data_date-ma200_close-ma200_volume-symbol)

我能够将时间缩短到 178 秒。

与 4) 一起,感谢 @DWright,通过重新构造查询,现在缩短到 67 秒。

【讨论】:

    【解决方案4】:

    我看到的一种方法是预先计算每年的 min(data_date)。这样,您不必为外部查询的每条记录触发 SELECT 查询。但是您需要维护此表,以确保它在任何时间点始终具有给定年份的最小 data_date。

    【讨论】:

    • 我正在考虑至少将 data_date 字段分解为它们自己的字段(即 data_year、data_month )。我不经常查询特定日期,但将来可能需要。您对此有何看法?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-04-23
    • 1970-01-01
    • 2017-06-30
    • 1970-01-01
    相关资源
    最近更新 更多