什么索引应该提高选择查询的性能?

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 可以应用之前进行排序(或优先级哈希)。

【讨论】:

以上是关于什么索引应该提高选择查询的性能?的主要内容,如果未能解决你的问题,请参考以下文章

普通索引和唯一索引,应该怎么选择?

高性能索引-高性能索引策略

提高 MySQL 查询性能 - 可能的索引问题

如何提高选择查询的性能

MySQL高性能索引策略

C#笔记