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

时间:2016-02-12 08:19:56

标签: mysql performance indexing

这是表结构:

+--------------+--------------+------+-----+---------+----------------+
| 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));

在此请求之后,响应时间增加到~150ms。

如何提高此案例的效果?谢谢。

更新:

回答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 ms,索引后365,59 ms。

我也有索引

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      |         |               |
+--------------+------------+-----------------------+--------------+--------------+-----------+-------------+----------+--------+------+------------+---------+---------------+

3 个答案:

答案 0 :(得分:0)

我对sql机制的误解感到困惑,所以我决定每24小时创建一个模型Popular和save实例。感谢所有试图提供帮助的人。

答案 1 :(得分:0)

在我看来,虽然你有一个索引,MySQL并不知道如何正确使用它。当表中的数据分布(统计)信息不是最新时,就会发生这种情况。要更新它们,您应该致电ANALYZE TABLE visits_visit,然后检查结果。

答案 2 :(得分:0)

正如我在你的另一个问题中所说,前缀索引几乎没用;除非在极少数情况下,否则不要使用它们。

将字段缩小到合理的长度,您不会想要使用前缀索引。

该查询的最佳索引是INDEX(object_model, object_id)。尝试使用INDEX(object_model(##), ...)将不会超过object_model之后的任何内容。

如果object_model是'新闻'之类的东西,我怀疑其他可能的值很短,并且可能存在有限数量的模型。对于某些较小的VARCHAR进行“短暂”更改。对于“有限”,请考虑使用ENUM('News', 'Weather', 'Sports', ...)

至于为什么索引后需要更长的时间......

  • 没有索引,优化器别无选择,只能扫描整个表。这是一个简单的线性扫描。它会读取但不计算任何非新闻行。
  • 使用索引,Optimizer还可以选择使用索引。但是,也许大多数行都是新闻?好吧,它会扫描索引(很好),但对于索引中的每个新闻项,它必须查找行以获得object_id(不太好)。似乎(从时间上看)后者的效率较低。

通过缩小声明并使用INDEX(object_model, object_id)(按此顺序),可以在索引中执行查询。将索引视为一个迷你表,其中只包含这两列。它更小。它按模型排序,因此只需要扫描“新闻”部分。解释将通过说“使用索引”来显示这种“覆盖”。

如果遇到所有情况,GROUP BY会增加一些开销 - 要么在RAM中保留object_id的哈希值,要么保存中间结果并对其进行排序。然后ORDER BY需要排序(或优先级哈希)才能应用LIMIT