1

我的 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 |
+------+-------------+------------+------+----------------+------+---------+------+-----------+----------------------------------------------+
4

2 回答 2

1

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

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

于 2012-07-16T23:19:51.417 回答
1

有几个想法供您考虑:

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

    ON qcollector (datetime,instrument,lastsize) 或者

    ON qcollector (instrument,datetime,lastsize)

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

    COUNT(lastsize)表达式等价于SUM(IF(lastsize IS NULL,0,1))

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

    AND lastsize IS NOT NULL

在您的查询中?

这些可能会有所帮助。


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

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


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

于 2012-07-16T23:48:32.110 回答