我目前正在优化一个非常大的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是两个表相关的字段。 metar_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
),除了metar_records
之外没有auto_increment字段,没有任何索引。执行SELECT COUNT(*) FROM metar_test;
最多持续0.02秒 ,而SELECT COUNT(*) FROM metar_records;
大约需要1分18秒才能完成。
据我所知,拥有这么大的表会导致一些较长的查询时间,但metar_records
只比metar_test
大3.36倍 - 为什么{{1}之间存在如此大的差异查询这两个表?我并不是特别精通数据存储,但这种差异似乎对我来说意外大。
如何改进索引以优化大表大小?是否可以从这里减少查询持续时间?
答案 0 :(得分:0)
您可以尝试:
select count(stationID)
from metar_records
这将使查询优化器使用stationID的索引,因此读取较少的数据作为读取完整数据的count(*)。
答案 1 :(得分:0)
我会以这种方式重建你的桌子。
站。 我是汽车公司。 StationId char(4)独一无二 其余...
Metar_records 我是汽车公司 StationId引用stations.id 其余...
这样你的密钥长度就会变小和数字。会提升你的表现。
答案 2 :(得分:0)
您可能已启用“查询缓存”。这使得第二次运行完全相同的查询非常快。要正确计算查询时间,请执行两次并执行第二次计时:
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)
。