4

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

查询(运行,但平均超过 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

谢谢!

4

4 回答 4

4

你能列出 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 可以相对有效地解决(尽管可能没有索引的好处);但是,引擎可能无法很好地解释这种情况——您确实有一个有点复杂的嵌套子查询情况,这可能会使引擎出错]。

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

于 2012-12-22T04:13:52.557 回答
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 指定日期的方式,但您明白了。你给它一个起点和终点,让它使用索引。在您要求它计算每一行的年份时,这是不可能的。

于 2012-12-22T05:27:45.980 回答
1

通过 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 秒。

于 2012-12-22T05:17:53.780 回答
0

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

于 2012-12-22T03:11:00.053 回答