当我在一个有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
答案 0 :(得分:1)
由于您在symbol
和date
上都匹配,因此您需要在这两列(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-size
和max-heap-table-size
。这些不仅适用于临时表,还适用于运行查询的工作集。如果它们太小,MySql将不得不在磁盘上放置活动查询的数据。
答案 2 :(得分:1)
我可以想象查询计划是按日期扫描范围,然后按符号匹配。扫描的范围非常大,并且根据外部选择的符号完成。
我尝试创建一个以(symbol, date)
为列的索引。由于符号可能远远少于日期,因此按符号扫描然后按日期过滤的范围可能更快,甚至可能在外部和内部查询之间的散列连接期间完成。
但是你必须经常运行explain
,并且可能analyze table
才能继续运行。