【问题标题】:Basic MySQL query is pretty slow. Am I indexing my tables properly?基本的 MySQL 查询非常慢。我是否正确索引我的表?
【发布时间】:2018-04-03 10:22:42
【问题描述】:

我目前正在优化一个非常大的 MySQL 数据库,我正在围绕它构建一个基于 Web 的查询界面。

数据库将有两个表。第一张表已经优化(我相信),其中包含有关美国 950 个气象数据观测站的信息:

Description for: stations (950 records)
+-----------+------------+--------+-------+---------+----------------+
|Field      |Type        | NULL   |KEY    | Default | Extra          |
+-----------+------------+--------+-------+---------+----------------+
|id         |INT         |NO      |PRI    |NULL     |auto_increment  |
|stationID  |char(4)     |NO      |PRI    |NULL     |                |
|name       |varchar(16) |YES     |       |NULL     |                |
|state      |char(2)     |YES     |MUL    |NULL     |                |
|lat        |float(6,2)  |YES     |       |NULL     |                |
|lon        |float(6,2)  |YES     |       |NULL     |                |
|elev       |INT         |YES     |       |NULL     |                |
+-----------+------------+--------+-------+---------+----------------+

另一张表包含从 2014 年到 2017 年在这些站点收集的观测数据(已构建,未优化):

Description for: metar_records (359786049 records)
+-----------+------------+--------+-------+---------+----------------+
|Field      |Type        | NULL   |KEY    | Default | Extra          |
+-----------+------------+--------+-------+---------+----------------+
|auto_id    |INT         |NO      |PRI    |NULL     |auto_increment  |
|stationID  |char(4)     |NO      |MUL    |0        |                |
|zdatetime  |datetime    |NO      |       |NULL     |                |
|ldatetime  |datetime    |NO      |       |NULL     |                |
|temp       |tinyint(4)  |YES     |       |NULL     |                |
|dew        |tinyint(4)  |YES     |       |NULL     |                |
|wspd       |tinyint(3)  |YES     |       |NULL     | #unsigned      |
|wdir       |tinyint(3)  |YES     |       |NULL     | #unsigned      |
|wgust      |tinyint(3)  |YES     |       |NULL     | #unsigned      |
|VRB        |char(3)     |YES     |       |NULL     |                |
+-----------+------------+--------+-------+---------+----------------+

其中 stationID 是两个表相关的字段。 meta_records 在('stationID', 'zdatetime') 上有一个唯一索引。 metar_records 表索引列表:

+-------------+--------+---------+------------+-----------+-----------+----------+
|Table        |Non_UNQ |Key_name |Seq_in_index|Column_name|Cardinality|Index_type|
+-------------+--------+---------+------------+-----------+-----------+----------+
|metar_records|0       |PRIMARY  |1           |auto_id    |358374698  |BTREE     |
|metar_records|0       |sz_date  |1           |stationID  |820079     |BTREE     |
|metar_records|0       |sz_date  |2           |zdatetime  |358374698  |BTREE     |
|metar_records|1       |stationID|1           |stationID  |598288     |BTREE     |
+-------------+--------+---------+------------+-----------+-----------+----------+

这是我真正感到困惑的地方:我还有一个测试表(称为 metar_test),除了没有 auto_increment 字段之外,它与 metar_records 相同,并且有没有任何索引SELECT COUNT(*) FROM metar_test; 的执行最多持续 0.02 秒,而 SELECT COUNT(*) FROM metar_records; 大约需要 1 分 18 秒才能完成。

我知道拥有这么大的表会导致查询时间较长,但 metar_records 仅比 metar_test 大 3.36 倍——为什么两者的 SELECT COUNT(*) ... 查询之间存在如此大的差异表?我对数据存储不是特别精通,但这种差异对我来说似乎出乎意料的大。

如何改进索引以优化大表大小?是否可以从这里减少查询持续时间?

【问题讨论】:

  • 索引根本不应该影响这些查询;索引有助于在索引字段上过滤/加入/分组的查询;但通常不应对未过滤/加入/分组的选择查询产生负面影响。我建议仔细检查表格之间的差异;包括字符集、排序规则和存储引擎。
  • 大表中当前有多少行?它会增长多少?你考虑过“分区”吗? (见dev.mysql.com/doc/refman/5.7/en/partitioning.html
  • @Used_By_Already 359786049 行在较大的表中。不知道它是否会增长。我基本上是为面试组合构建它,但我还没有决定是否要维护它以使其保持最新。分区是我将不得不探索的东西。谢谢推荐!
  • 分区不会加速select count(*),除非 dbms 也支持并行处理。 select count(*) 正在进行表扫描,因此磁盘访问速度可能是瓶颈(并且可以解释为什么测试表和真实表之间的差异)。

标签: mysql indexing


【解决方案1】:

你可以试试:

select count(stationID) 
from metar_records

这将使查询优化器使用stationID的索引,因此读取更少的数据作为读取完整数据的count(*)。

【讨论】:

  • 试过这个,在 1 分 17 秒内返回计数。好不了多少:/
【解决方案2】:

我会以这种方式重建你的表格。

车站。 ID 为汽车公司。 StationId char (4) 唯一 休息...

Metar_records ID 为汽车公司 StationId 引用stations.id 休息...

这样您的密钥长度会更小且数字化。会提高你的表现。

【讨论】:

    【解决方案3】:

    您可能已打开“查询缓存”。这使得 exactly 第二次运行相同的查询非常快。要正确计时查询,请执行两次并进行第二次计时:

    SELECT SQL_NO_CACHE ...
    

    COUNT(*) 是计算行数的常用模式。 COUNT(col) 比较慢,因为它需要检查每个 col 是否为 NOT NULL

    你的大桌子上有 3 个INDEXes;你只需要一个:

    PRIMARY KEY(stationID, zdatetime)
    

    而且,通过这种方式进行聚类,几个可能的查询会运行得更快。

    请使用SHOW CREATE TABLE;它比DESCRIBE 更具描述性。

    您应该使用ENGINE=InnoDB,而不是ENGINE=MyISAM(请参阅SHOW CREATE TABLE)。

    SELECT COUNT(*) ... 不是一个很常见的查询;你不应该过多地评估它的运行速度。

    PARTITIONing 不太可能有助于提高性能。让我们看看您的更多查询 - 仔细检查我的声明。 MySQL 没有并行处理,即使对于 PARTITIONed 表也是如此。

    还要从 Stations 表中扔掉 id AUTO_INCREMENT;取而代之的是PRIMARY KEY(stationID)

    【讨论】:

      猜你喜欢
      • 2014-10-16
      • 1970-01-01
      • 2011-02-19
      • 2016-06-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-06-12
      • 1970-01-01
      相关资源
      最近更新 更多