优化MySQL查询索引(交易滴答数据数据库)

优化MySQL查询索引(交易滴答数据数据库),mysql,optimization,indexing,create-table,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,

我的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分钟):


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


您还可以考虑添加多列索引。关于

(仪器,日期时间)
的索引可能会对您有所帮助。

您可以考虑以下几点:

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

    在qcollector上(日期时间、仪器、最后尺寸)

    在qcollector上(仪器、日期时间、最后大小)

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

    COUNT(lastsize)
    表达式等价于
    SUM(如果(lastsize为NULL,0,1))

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

    且lastsize不为空

在你的询问中

这些可能会有所帮助


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

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



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

大多数查询使用介于“yyyy-mm-dd hh:mm:ss”和“yyy-mm-dd hh:mm:ss”之间的datetime(只需一天或两天),因此我发现最好使用一列作为datetime,而不是两个单独的列。但有些查询(如上文所述)跨越数月或数年,我每天只需要包含特定的时间(hh:mm:ss)。只有不到10个“工具”,我的理解是,索引在这种情况下不会有帮助。如果您希望查询速度快,跨越数年,但每天只选择几次,则需要对时间进行索引。不幸的是,在MySQL中,唯一的方法就是创建一个新的列,因为它是。有趣的是(或者不太有趣…)我一开始使用两列作为日期/时间,但后来觉得效率很低,所以将它们组合起来。@user1530260:我们通常希望日期和时间放在一列中,当我们想要从连续的日期时间范围中检索行时,例如,
其中datetime>='2012-07-14 08:00:00'和datetime<'2012-07-14 17:00:00'
。在您的例子中,您将分别处理日期和时间部分,因此可能需要使用单独的列。您在一整段时间内都在检索相同的小时间窗口。对于您的查询,您的索引应该同时包含这两列。如果我添加了一个覆盖索引(比如datetime、lastsize)[或新建议的结构],但查询没有使用其中一列,那么该索引还会被使用吗?或者在这种情况下我需要两个单独的索引吗?关于使用COUNT,我通常使用SUM,通常在特定的时间内使用range@user1530260:您需要一个包含所有列的索引。单独列上的索引对查询没有帮助。(它们可能对其他查询有用。)但对于您的查询,您需要一个索引。
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 |
+------+-------------+------------+------+----------------+------+---------+------+-----------+----------------------------------------------+