为查询优化MySQL索引(交易计分数据库)

时间:2012-07-16 23:17:10

标签: mysql optimization indexing create-table

我的MySQL数据库有超过3.5亿行,并且正在增长。它的尺寸现在是32GB。我正在使用SSD和大量的RAM,但是想寻求建议以确保我使用适当的索引。

CREATE TABLE `qcollector` (
  `key` bigint(20) NOT NULL AUTO_INCREMENT,
  `instrument` char(4) DEFAULT NULL,
  `datetime` datetime DEFAULT NULL,
  `last` double DEFAULT NULL,
  `lastsize` int(10) DEFAULT NULL,
  `totvol` int(10) DEFAULT NULL,
  `bid` double DEFAULT NULL,
  `ask` double DEFAULT NULL,
  PRIMARY KEY (`key`),
  KEY `datetime_index` (`datetime`)
) ENGINE=InnoDB;

show index from qcollector;
+------------+------------+----------------+--------------+-------------+-----------+--    -----------+----------+--------+------+------------+---------+---------------+
| Table      | Non_unique | Key_name       | Seq_in_index | Column_name | Collation |     Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+------------+------------+----------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| qcollector |          0 | PRIMARY        |            1 | key         | A         |   378866659 |     NULL | NULL   |      | BTREE      |         |               |
| qcollector |          1 | datetime_index |            1 | datetime    | A         |    63144443 |     NULL | NULL   | YES  | BTREE      |         |               |
+------------+------------+----------------+--------------+-------------+-----------+------    -------+----------+--------+------+------------+---------+---------------+
2 rows in set (0.03 sec)

select * from qcollector order by datetime desc limit 1;
+-----------+------------+---------------------+---------+----------+---------+---------+--------+
| key       | instrument | datetime            | last    | lastsize | totvol  | bid     | ask    |
+-----------+------------+---------------------+---------+----------+---------+---------+--------+
| 389054487 | ES         | 2012-06-29 15:14:59 | 1358.25 |        2 | 2484771 | 1358.25 | 1358.5 |
+-----------+------------+---------------------+---------+----------+---------+---------+--------+
1 row in set (0.09 sec)

典型的查询速度很慢(全表扫描,此查询需要3-4分钟):

explain select date(datetime), count(lastsize) from qcollector where instrument = 'ES' and datetime > '2011-01-01' and time(datetime) between '15:16:00' and '15:29:00' group by date(datetime) order by date(datetime) desc;
+------+-------------+------------+------+----------------+------+---------+------+-----------+----------------------------------------------+
| id   | select_type | table      | type | possible_keys  | key  | key_len | ref  | rows      | Extra                                        |
+------+-------------+------------+------+----------------+------+---------+------+-----------+----------------------------------------------+
|    1 | SIMPLE      | qcollector | ALL  | datetime_index | NULL | NULL    | NULL | 378866659 | Using where; Using temporary; Using filesort |
+------+-------------+------------+------+----------------+------+---------+------+-----------+----------------------------------------------+

2 个答案:

答案 0 :(得分:1)

在列上使用datetime函数时,无法有效使用索引。您还可以将日期和时间存储在单独的列中并对其进行索引,但这会占用更多存储空间。

您可能还需要考虑添加多列索引。 (instrument, datetime)上的索引可能会对您有所帮助。

答案 1 :(得分:1)

您可以考虑几个想法:

  • 覆盖索引(即包含查询中引用的所有列的索引)可能有所帮助。这样的索引将需要更多的磁盘(SSD?)空间,但它将消除MySQL访问数据页以查找不在索引中的列的值的必要性。

    ON qcollector (datetime,instrument,lastsize)

    ON qcollector (instrument,datetime,lastsize)

  • 您真的需要从计数中排除lastsize的NULL值的行吗?你可以返回所有行的计数吗?如果您可以返回COUNT(1)SUM(1),则查询不需要引用lastsize列,因此索引中不需要它来使其成为覆盖索引

    COUNT(lastsize)表达式相当于SUM(IF(lastsize IS NULL,0,1))

  • 当日期时间范围只有NULL lastsize值时,是否需要返回日期,或者是否可以排除所有具有NULL lastsize的行?也就是说,你可以包含一个像

    这样的谓词

    AND lastsize IS NOT NULL

在您的查询中

这些可能有所帮助。


我认为最大的问题是TIME(datetime)表达式的谓词不可理解。也就是说,MySQL不会对那些使用索引范围扫描操作。裸datetime列上的谓词是可搜索的......这就是EXPLAIN将datetime_index显示为可能的键的原因。

另一个大问题是查询正在对派生表达式执行GROUP BYORDER BY操作,这将要求MySQL生成中间结果集(作为临时MyISAM表) ,然后处理该结果集。当有很多行要处理时,这可能会带来很多繁重的工作。


就表格更改而言,我会考虑使用单独的DATE和TIME列,并使用TIMESTAMP数据类型代替DATETIME(如果需要将日期和时间存储在一起)。我会重写查询以引用裸DATE和裸TIME列,并考虑添加覆盖索引,其中包括重写查询中引用的所有列,前导列是具有最高基数的列(并且具有最高选择性谓词)查询。)