mysql表上的正确索引

时间:2014-04-17 19:40:32

标签: mysql sql

当我在一个有22M行的表上运行以下查询时,它需要20秒才能运行:

select p.*,
(select avg(close)
from endOfDayData p2
where p2.symbol = p.symbol and
p2.date between p.date - interval 6 day and p.date
) as MvgAvg_X
from endOfDayData p
where p.symbol = 'AAPL'

表结构如下:

mysql> desc endOfDayData;
+--------+---------------+------+-----+---------+-------+
| Field  | Type          | Null | Key | Default | Extra |
+--------+---------------+------+-----+---------+-------+
| date   | date          | NO   | PRI | NULL    |       |
| symbol | varchar(14)   | NO   | PRI | NULL    |       |
| open   | decimal(10,4) | NO   |     | NULL    |       |
| high   | decimal(10,4) | NO   |     | NULL    |       |
| low    | decimal(10,4) | NO   |     | NULL    |       |
| close  | decimal(10,4) | NO   |     | NULL    |       |
| volume | int(11)       | NO   |     | NULL    |       |
+--------+---------------+------+-----+---------+-------+

并且存在以下索引:

mysql> show index from endOfDayData;
+--------------+------------+------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table        | Non_unique | Key_name   | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+--------------+------------+------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| endOfDayData |          0 | PRIMARY    |            1 | date        | A         |      162294 |     NULL | NULL   |      | BTREE      |         |               |
| endOfDayData |          0 | PRIMARY    |            2 | symbol      | A         |    24019617 |     NULL | NULL   |      | BTREE      |         |               |
| endOfDayData |          1 | EOD_dates  |            1 | date        | A         |       50145 |     NULL | NULL   |      | BTREE      |         |               |
| endOfDayData |          1 | EOD_symbol |            1 | symbol      | A         |       14322 |     NULL | NULL   |      | BTREE      |         |               |
+--------------+------------+------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
4 rows in set (0.00 sec)

该机器是一个专用的盒子,配有80GB的RAM和双处理器。我觉得它应该在一秒钟内使用正确的索引运行。感谢

| id | select_type        | table | type | possible_keys                | key        | key_len | ref                | rows | Extra                 |  
+----+--------------------+-------+------+------------------------------+-------‌​-----+---------+--------------------+------+-----------------------+
|  1 | PRIMARY            | p     | ref  | EOD_symbol                   | EOD_symbol | 16      | const              | 8409 | Using index condition |  
|  2 | DEPENDENT SUBQUERY | p2    | ref  | PRIMARY,EOD_dates,EOD_symbol | EOD_symbol | 16      | financial.p.symbol | 1677 | Using index condition |

我创建了一个ID为int的新表作为主键,并在symbol,date。

上创建了一个索引
CREATE INDEX EODDateSym ON endOfDayData_new (symbol, date) USING BTREE;

仍然有17秒。再次感谢所有的想法和帮助

我的my.conf是

[mysql]

# CLIENT #
port                           = 3306
socket                         = /var/lib/mysql/mysql.sock

[mysqld]

# GENERAL #
user                           = mysql
default-storage-engine         = InnoDB
socket                         = /var/lib/mysql/mysql.sock
pid-file                       = /var/lib/mysql/mysql.pid

# MyISAM #
key-buffer-size                = 32M
myisam-recover                 = FORCE,BACKUP

# SAFETY #
max-allowed-packet             = 16M
max-connect-errors             = 1000000

# DATA STORAGE #
datadir                        = /var/lib/mysql/

# BINARY LOGGING #
log-bin                        = /var/lib/mysql/mysql-bin
expire-logs-days               = 14
sync-binlog                    = 1
server_id              = 1

# CACHES AND LIMITS #
tmp-table-size                 = 32M
max-heap-table-size            = 32M
query-cache-type               = 0
query-cache-size               = 0
max-connections                = 500
thread-cache-size              = 50
open-files-limit               = 65535
table-definition-cache         = 4096
table-open-cache               = 4096

# INNODB #
innodb-flush-method            = O_DIRECT
innodb-log-files-in-group      = 2
innodb-log-file-size           = 512M
innodb-flush-log-at-trx-commit = 1
innodb-file-per-table          = 1
innodb-buffer-pool-size        = 68G

# LOGGING #
log-error                      = /var/lib/mysql/mysql-error.log
log-queries-not-using-indexes  = 1
slow-query-log                 = 1
slow-query-log-file            = /var/lib/mysql/mysql-slow.log

3 个答案:

答案 0 :(得分:1)

由于您在symboldate上都匹配,因此您需要在这两列(symbol, date)上设置索引,以使其在你表达的条件。

MySQL通常会为给定的表格选择最佳的工作索引,并且无法以任何有意义的方式组合两个。

如果您将这两者都作为主键,则非常奇怪且可能会损害性能。常规UNIQUE ID类型列的INT AUTO_INCREMENT PRIMARY KEY索引更好。当主键尽可能紧凑时,MySQL的性能最佳。

答案 1 :(得分:1)

列表中的第三个索引(EOD_Date)几乎毫无价值。你应该放弃它。真。 Date已经是主键中的第一个字段,因此主键几乎总是(如果不是总是)将在该索引上选择。保持该索引实际上使您的系统变慢,因为MySql仍将继续维护该索引。

您需要更新EOD_Symbol索引,以便它还包含日期字段作为索引中的第二列。您可能还希望close字段作为索引中的第三列。这意味着该索引覆盖了您的主键,并且close字段也完全覆盖了相关的子查询(内部select语句):

CREATE INDEX EOD_DateSymClose ON endOfDayData (symbol, date, close) USING BTREE;

确保此索引替换现有的EOD_Symbol索引。一旦这个索引准备就绪,MySql很少想要使用该索引而不是这个索引,并且MySql确实需要维护这两个索引,特别是在插入和更新时。

通过使用主键的symbol, date顺序,您可以获得更好的查询效果,但这可能会对其他查询产生负面影响,或者更糟糕的是,您的插入性能会受到影响。

最后,我经常看到人们查看使用列的查询,并认为他们可以通过添加该特定列的索引来加速查询。这是索引如何工作。列的索引有助于它符合查询需要访问字段的顺序。在这里,您的相关子查询首先需要按符号限制记录,然后在符号内限制它按日期使用的记录,因此您希望查询按顺序使用这两列。


我刚看到使用MySql配置进行编辑。我会尝试增加您的tmp-table-sizemax-heap-table-size。这些不仅适用于临时表,还适用于运行查询的工作集。如果它们太小,MySql将不得不在磁盘上放置活动查询的数据。

答案 2 :(得分:1)

我可以想象查询计划是按日期扫描范围,然后按符号匹配。扫描的范围非常大,并且根据外部选择的符号完成。

我尝试创建一个以(symbol, date)为列的索引。由于符号可能远远少于日期,因此按符号扫描然后按日期过滤的范围可能更快,甚至可能在外部和内部查询之间的散列连接期间完成。

但是你必须经常运行explain,并且可能analyze table才能继续运行。