什么索引应该提高选择查询的性能?
Posted
技术标签:
【中文标题】什么索引应该提高选择查询的性能?【英文标题】:What index should increase performance of select query? 【发布时间】:2016-05-23 07:38:11 【问题描述】:这是表结构:
+--------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+--------------+--------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| visitor_hash | varchar(40) | YES | MUL | NULL | |
| uri | varchar(255) | YES | | NULL | |
| ip_address | char(15) | YES | MUL | NULL | |
| last_visit | datetime | YES | | NULL | |
| visits | int(11) | NO | | NULL | |
| object_app | varchar(255) | YES | MUL | NULL | |
| object_model | varchar(255) | YES | | NULL | |
| object_id | varchar(255) | YES | | NULL | |
| blocked | tinyint(1) | NO | | NULL | |
+--------------+--------------+------+-----+---------+----------------+
这是请求:
SELECT `object_id`
FROM `visits_visit`
WHERE `object_model` = 'News'
GROUP BY `object_id`
ORDER BY COUNT( * ) DESC
LIMIT 0, 3
响应时间约为 77.63 毫秒。
CREATE INDEX resource_model ON visits_visit (object_model(100));
在此请求之后,响应时间增加到 ~150 毫秒。
如何提高这种情况下的性能?谢谢。
更新:
回答 Michal Komorowski。 这是索引前的解释:
+----+-------------+--------------+------+---------------+------+---------+------+--------+----------------------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+--------------+------+---------------+------+---------+------+--------+----------------------------------------------+
| 1 | SIMPLE | visits_visit | ALL | NULL | NULL | NULL | NULL | 142938 | Using where; Using temporary; Using filesort |
+----+-------------+--------------+------+---------------+------+---------+------+--------+----------------------------------------------+
1 row in set (0.00 sec)
这是在索引之后:
+----+-------------+--------------+------+----------------+----------------+---------+-------+-------+----------------------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+--------------+------+----------------+----------------+---------+-------+-------+----------------------------------------------+
| 1 | SIMPLE | visits_visit | ref | resource_model | resource_model | 303 | const | 64959 | Using where; Using temporary; Using filesort |
+----+-------------+--------------+------+----------------+----------------+---------+-------+-------+----------------------------------------------+
1 row in set (0.00 sec)
我不知道是什么给了我这些信息。
SELECT `object_id`
FROM `visits_visit`
WHERE `object_model` = 'News'
GROUP BY `object_id`
ORDER BY COUNT( * ) DESC
LIMIT 0, 3
索引前 78.85 毫秒,索引后 365.59 毫秒。
我也有索引
CREATE INDEX resource ON visits_visit (object_app(100), object_model(100), object_id(100));
但我需要这个,因为在其他选择查询中,WHERE 包含这三个键。
更新:
我正在使用 django 调试工具栏来测试请求的性能。
更新:
查询:
ANALYZE TABLE visits_visit;
输出:
+-----------------------------+---------+----------+-----------------------------+
| Table | Op | Msg_type | Msg_text |
+-----------------------------+---------+----------+-----------------------------+
| **************.visits_visit | analyze | status | Table is already up to date |
+-----------------------------+---------+----------+-----------------------------+
1 row in set (0.00 sec)
更新:
SHOW INDEXES FROM visits_visit;
输出:
+--------------+------------+-----------------------+--------------+--------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+--------------+------------+-----------------------+--------------+--------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| visits_visit | 0 | PRIMARY | 1 | id | A | 142938 | NULL | NULL | | BTREE | | |
| visits_visit | 1 | visits_visit_0880babc | 1 | visitor_hash | A | 142938 | NULL | NULL | YES | BTREE | | |
| visits_visit | 1 | visits_visit_5325a746 | 1 | ip_address | A | 142938 | NULL | NULL | YES | BTREE | | |
| visits_visit | 1 | resource | 1 | object_app | A | 1 | 100 | NULL | YES | BTREE | | |
| visits_visit | 1 | resource | 2 | object_model | A | 3 | 100 | NULL | YES | BTREE | | |
| visits_visit | 1 | resource | 3 | object_id | A | 959 | 100 | NULL | YES | BTREE | | |
+--------------+------------+-----------------------+--------------+--------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
【问题讨论】:
应用索引前后的执行计划检查了吗? 谢谢你的回答,Michal Komorowski,我在下面回答了你。 尝试运行 ANALYZE TABLE visit_visit 然后检查结果。您也可以尝试OPTIMIZE TABLE visit_visit,但这可能需要更多时间。 你能做一个小实验,例如通过将 object_model(100) 更改为 object_model(10) 来显着减少索引的长度吗? webmonkeyuk.wordpress.com/2010/09/27/… 【参考方案1】:在我看来,虽然你有一个索引,但 mysql 不知道如何正确使用它。当表中有关数据分布(统计)的信息不是最新的时,就会发生这种情况。为了更新它们,您应该致电ANALYZE TABLE visits_visit
,然后检查结果。
【讨论】:
我做到了并将其添加到问题中。【参考方案2】:我对sql机制的误解感到困惑,所以我决定创建模型Popular并每24小时在其中保存实例。感谢所有试图提供帮助的人。
【讨论】:
只检查创建索引前后检查的行数。 谢谢你的回答,桑迪,检查是什么意思? rows 列表示 MySQL 认为它必须检查以执行查询的行数。 dev.mysql.com/doc/refman/5.5/en/explain-output.html @vadimka 您应该将此详细信息作为问题的一部分发布,而不是作为答案。【参考方案3】:正如我在您的另一个问题中所说,前缀索引实际上是无用的;除非在极少数情况下,否则不要使用它们。
将字段缩小到合理的长度,您就不会想使用前缀索引。
该查询的最佳索引是INDEX(object_model, object_id)
。尝试使用 INDEX(object_model(##), ...)
将无法通过 object_model
到达它之后的任何内容。
如果object_model
是“新闻”之类的东西,我怀疑其他可能的值很短,并且可能存在有限数量的模型。对于“短”更改为一些较小的VARCHAR
。对于“有限”,请考虑使用ENUM('News', 'Weather', 'Sports', ...)
。
至于为什么索引后花了更长的时间......
没有索引,优化器别无选择,只能扫描整个表。这是一个简单的线性扫描。它会读取但不计算任何非新闻行。 有了索引,优化器就有了使用索引的额外选择。但是,也许大多数行都是新闻?好吧,它会扫描索引(很好),但是对于索引中的每个新闻项目,它必须查找行以获取object_id
(不太好)。 (从时间上看)后者的效率似乎较低。
通过收缩声明并使用INDEX(object_model, object_id)
(按此顺序),可以在索引中执行查询。将索引想象成一个只有这两列的迷你表。它更小。它是按型号排序的,因此只需要扫描“新闻”部分。解释将通过说“使用索引”来显示这种“覆盖”。
如果是所有情况,GROUP BY
会增加一些开销——要么将 object_id
的哈希值保存在 RAM 中,要么保存中间结果并对其进行排序。那么ORDER BY
需要在LIMIT
可以应用之前进行排序(或优先级哈希)。
【讨论】:
以上是关于什么索引应该提高选择查询的性能?的主要内容,如果未能解决你的问题,请参考以下文章