我有以下简单的MySQL查询:
SELECT SQL_NO_CACHE mainID
FROM tableName
WHERE otherID3=19
AND dateStartCol >= '2012-08-01'
AND dateStartCol <= '2012-08-31';
当我运行它时,需要0.29秒才能恢复36074结果。当我增加我的日期时间以带回更多结果(65703)时,它以0.56运行。当我在同一台服务器上但在不同的表上运行其他类似的SQL查询时(某些表更大),结果会在大约0.01秒内返回。
尽管0.29并不慢 - 这是复杂查询的基本部分,但这个时间意味着它不可扩展。
请参阅下面的表定义和索引。
我知道这不是服务器负载,因为我在使用率很低的开发服务器上遇到同样的问题。
+---------------------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+---------------------------+--------------+------+-----+---------+----------------+
| mainID | int(11) | NO | PRI | NULL | auto_increment |
| otherID1 | int(11) | NO | MUL | NULL | |
| otherID2 | int(11) | NO | MUL | NULL | |
| otherID3 | int(11) | NO | MUL | NULL | |
| keyword | varchar(200) | NO | MUL | NULL | |
| dateStartCol | date | NO | MUL | NULL | |
| timeStartCol | time | NO | MUL | NULL | |
| dateEndCol | date | NO | MUL | NULL | |
| timeEndCol | time | NO | MUL | NULL | |
| statusCode | int(1) | NO | MUL | NULL | |
| uRL | text | NO | | NULL | |
| hostname | varchar(200) | YES | MUL | NULL | |
| IPAddress | varchar(25) | YES | | NULL | |
| cookieVal | varchar(100) | NO | | NULL | |
| keywordVal | varchar(60) | NO | | NULL | |
| dateTimeCol | datetime | NO | MUL | NULL | |
+---------------------------+--------------+------+-----+---------+----------------+
+--------------------+------------+-------------------------------+--------------+---------------------------+-----------+-------------+----------+--------+------+------------+---------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment |
+--------------------+------------+-------------------------------+--------------+---------------------------+-----------+-------------+----------+--------+------+------------+---------+
| tableName | 0 | PRIMARY | 1 | mainID | A | 661990 | NULL | NULL | | BTREE | |
| tableName | 1 | idx_otherID1 | 1 | otherID1 | A | 330995 | NULL | NULL | | BTREE | |
| tableName | 1 | idx_otherID2 | 1 | otherID2 | A | 25 | NULL | NULL | | BTREE | |
| tableName | 1 | idx_otherID3 | 1 | otherID3 | A | 48 | NULL | NULL | | BTREE | |
| tableName | 1 | idx_dateStartCol | 1 | dateStartCol | A | 187 | NULL | NULL | | BTREE | |
| tableName | 1 | idx_timeStartCol | 1 | timeStartCol | A | 73554 | NULL | NULL | | BTREE | |
|tableName | 1 | idx_dateEndCol | 1 | dateEndCol | A | 188 | NULL | NULL | | BTREE | |
|tableName | 1 | idx_timeEndCol | 1 | timeEndCol | A | 73554 | NULL | NULL | | BTREE | |
| tableName | 1 | idx_keyword | 1 | keyword | A | 82748 | NULL | NULL | | BTREE | |
| tableName | 1 | idx_hostname | 1 | hostname | A | 2955 | NULL | NULL | YES | BTREE | |
| tableName | 1 | idx_dateTimeCol | 1 | dateTimeCol | A | 220663 | NULL | NULL | | BTREE | |
| tableName | 1 | idx_statusCode | 1 | statusCode | A | 2 | NULL | NULL | | BTREE | |
+--------------------+------------+-------------------------------+--------------+---------------------------+-----------+-------------+----------+--------+------+------------+---------+
解释输出:
+----+-------------+-----------+-------+----------------------------------+-------------------+---------+------+-------+----------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-----------+-------+----------------------------------+-------------------+---------+------+-------+----------+-------------+
| 1 | SIMPLE | tableName | range | idx_otherID3,idx_dateStartCol | idx_dateStartCol | 3 | NULL | 66875 | 75.00 | Using where |
+----+-------------+-----------+-------+----------------------------------+-------------------+---------+------+-------+----------+-------------+
答案 0 :(得分:1)
如果这确实是你的查询(而不是它的简化版本),那么这应该是最好的结果:
CREATE INDEX table_ndx on tableName( otherID3, dateStartCol, mainID);
第一个索引条目意味着WHERE
中的第一个匹配非常快;同样适用于dateStartCol
。第三个字段非常小,并且不会明显减慢索引,但允许在索引中立即找到您需要的数据,而根本没有表访问。
密钥必须位于相同的索引中。在您发布的EXPLAIN
中,每个键都在其自己的索引中,因此即使MySQL选择最佳索引,性能也不会是最佳的。我尝试使用较少的索引,因为它们也有成本(无耻的插件:Can Indices actually decrease SELECT performance?)。
答案 1 :(得分:0)
如果这是一个经常性或重要的查询,那么创建一个多列索引:
CREATE INDEX index_name ON tableName (otherID3, dateStartCol)
删除未使用的索引,因为它们使表格更改更加昂贵。
顺便说一下,您不需要两个单独的列来表示日期和时间。您可以在datetime
或timestamp
类型中合并。少一列,少一个索引。
explain
输出显示它选择了dateStartCol
索引,因此您可以尝试与上面建议的相反:
CREATE INDEX index_name ON tableName (dateStartCol, otherID3)
请注意,查询的dateStartCol条件仍将获得75%的行,因此在使用该单个索引时没有太大的改进(如果有的话)。
otherID3
有多独特?如果没有多少重复otherID3
,您可以hint引擎使用它。
答案 2 :(得分:0)
首先尝试添加正确的密钥。似乎dateStartCol比otherID3更具选择性
ALTER TABLE tableName ADD KEY idx_dates(dateStartCol, dateStartCol)
第二 - 请确保通过向SELECT添加LIMIT子句来仅选择所需的行。这应该是查询。试试这样:
SELECT SQL_NO_CACHE mainID FROM tableName 其他ID = 19 AND dateStartCol&gt; =''2012-08-01' AND dateStartCol&lt; =''2012-08-31' 限制10;
请确保您的MySQL已正确调整。您可能需要检查key_buffer_size和innodb_buffer_pool_size,如http://astellar.com/2011/12/why-is-stock-mysql-slow/
中所述