使用内部联接和order by优化sql查询

时间:2009-11-10 21:57:59

标签: sql mysql query-optimization sql-optimization

我正在尝试优化以下查询而没有成功。有什么想法可以将其编入索引以防止临时表和filesort吗?

EXPLAIN SELECT SQL_NO_CACHE `groups`.*
FROM `groups`
INNER JOIN `memberships` ON `groups`.id = `memberships`.group_id
WHERE ((`memberships`.user_id = 1) 
  AND (`memberships`.`status_code` = 1 AND `memberships`.`manager` = 0))
ORDER BY groups.created_at DESC LIMIT 5;`

+----+-------------+-------------+--------+--------------------------+---------+---------+---------------------------------------------+------+----------------------------------------------+
| id | select_type | table       | type   | possible_keys            | key     | key_len | ref                                         | rows | Extra                                        |
+----+-------------+-------------+--------+--------------------------+---------+---------+---------------------------------------------+------+----------------------------------------------+
|  1 | SIMPLE      | memberships | ref    | grp_usr,grp,usr,grp_mngr | usr     | 5       | const                                       |    5 | Using where; Using temporary; Using filesort | 
|  1 | SIMPLE      | groups      | eq_ref | PRIMARY                  | PRIMARY | 4       | sportspool_development.memberships.group_id |    1 |                                              | 
+----+-------------+-------------+--------+--------------------------+---------+---------+---------------------------------------------+------+----------------------------------------------+
2 rows in set (0.00 sec)


    +--------+------------+-----------------------------------+--------------+-----------------+-----------+-------------+----------+--------+------+------------+---------+
| Table  | Non_unique | Key_name                          | Seq_in_index | Column_name     | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment |
+--------+------------+-----------------------------------+--------------+-----------------+-----------+-------------+----------+--------+------+------------+---------+
| groups |          0 | PRIMARY                           |            1 | id              | A         |           6 |     NULL | NULL   |      | BTREE      |         | 
| groups |          1 | index_groups_on_name              |            1 | name            | A         |           6 |     NULL | NULL   | YES  | BTREE      |         | 
| groups |          1 | index_groups_on_privacy_setting   |            1 | privacy_setting | A         |           6 |     NULL | NULL   | YES  | BTREE      |         | 
| groups |          1 | index_groups_on_created_at        |            1 | created_at      | A         |           6 |     NULL | NULL   | YES  | BTREE      |         | 
| groups |          1 | index_groups_on_id_and_created_at |            1 | id              | A         |           6 |     NULL | NULL   |      | BTREE      |         | 
| groups |          1 | index_groups_on_id_and_created_at |            2 | created_at      | A         |           6 |     NULL | NULL   | YES  | BTREE      |         | 
+--------+------------+-----------------------------------+--------------+-----------------+-----------+-------------+----------+--------+------+------------+---------+


     +-------------+------------+----------------------------------------------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+
| Table       | Non_unique | Key_name                                                 | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment |
+-------------+------------+----------------------------------------------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+
| memberships |          0 | PRIMARY                                                  |            1 | id          | A         |           2 |     NULL | NULL   |      | BTREE      |         | 
| memberships |          0 | grp_usr                                                  |            1 | group_id    | A         |           2 |     NULL | NULL   | YES  | BTREE      |         | 
| memberships |          0 | grp_usr                                                  |            2 | user_id     | A         |           2 |     NULL | NULL   | YES  | BTREE      |         | 
| memberships |          1 | grp                                                      |            1 | group_id    | A         |           2 |     NULL | NULL   | YES  | BTREE      |         | 
| memberships |          1 | usr                                                      |            1 | user_id     | A         |           2 |     NULL | NULL   | YES  | BTREE      |         | 
| memberships |          1 | grp_mngr                                                 |            1 | group_id    | A         |           2 |     NULL | NULL   | YES  | BTREE      |         | 
| memberships |          1 | grp_mngr                                                 |            2 | manager     | A         |           2 |     NULL | NULL   | YES  | BTREE      |         | 
| memberships |          1 | complex_index                                            |            1 | group_id    | A         |           2 |     NULL | NULL   | YES  | BTREE      |         | 
| memberships |          1 | complex_index                                            |            2 | user_id     | A         |           2 |     NULL | NULL   | YES  | BTREE      |         | 
| memberships |          1 | complex_index                                            |            3 | status_code | A         |           2 |     NULL | NULL   | YES  | BTREE      |         | 
| memberships |          1 | complex_index                                            |            4 | manager     | A         |           2 |     NULL | NULL   | YES  | BTREE      |         | 
| memberships |          1 | index_memberships_on_user_id_and_status_code_and_manager |            1 | user_id     | A         |           2 |     NULL | NULL   | YES  | BTREE      |         | 
| memberships |          1 | index_memberships_on_user_id_and_status_code_and_manager |            2 | status_code | A         |           2 |     NULL | NULL   | YES  | BTREE      |         | 
| memberships |          1 | index_memberships_on_user_id_and_status_code_and_manager |            3 | manager     | A         |           2 |     NULL | NULL   | YES  | BTREE      |         | 
+-------------+------------+----------------------------------------------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+

5 个答案:

答案 0 :(得分:0)

  • membershipsuser_id上的索引(如果是PK,则应该已有)
  • membershipsstatus_codemanager上的索引(两者都在同一索引上)
  • groupscreated_at上的索引(如果可能,使用默认DESC,我不知道您是否可以使用mySql)

这就是我在MS SQL Server中所做的事情,但我想同样的优化也可以在mySql中使用。

答案 1 :(得分:0)

您在加入的字段中是否包含所有“明显的”单列索引,where子句中的字段和您订购的created_at字段?

答案 2 :(得分:0)

问题是你需要一个关于组的索引才能消除文件排序,但你所有的条件都属于会员资格。

尝试在(id,created_at)上添加组索引。

如果这不起作用,请尝试使用子查询欺骗优化器(将上述索引保留在组上):

SELECT SQL_NO_CACHE `groups`.*
    FROM `groups`
    INNER JOIN (select group_id from `memberships`
       WHERE 
           `memberships`.user_id = 1
           AND `memberships`.`status_code` = 1
           AND `memberships`.`manager` = 0
    ) m on m.group_id=`groups`.id
    ORDER BY groups.created_at DESC LIMIT 5;

至少应该有成员资格.user_id的索引,但你也可以从像(user_id,status,manager)这样的索引中获得一些好处。我假设status和manager是没有大范围可能值的标志,所以只要user_id上有索引就没那么重要。

答案 3 :(得分:0)

(user_id, status_code, manager)上的memberships(按任意顺序)索引会有所帮助。

避免排序会很困难,因为那时你必须在groups表中启动连接,这意味着你不能使用引用memberships表的所有(可能是非常有选择性的)where子句直到它为时已晚。

答案 4 :(得分:0)

感谢您发布有关您正在使用的索引的详细信息。

我已经对此进行了测试,并尝试省略了一些索引。最多的索引是memberships.complex_index,用作覆盖索引。这允许查询通过只读取索引来实现其结果;它根本不必读取memberships的数据行。

groups上的所有索引都没有任何区别。似乎无论如何都使用filesort。 MySQL中的文件排序只是意味着它正在进行表扫描,在某些情况下,使用索引可以成本更低。例如,如果需要读取groups的每一行以产生查询结果,为什么还要使用索引进行不必要的双重查找呢?优化器可以感知这些情况,并可以适当地拒绝使用索引。

除了主键索引和complex_index之外,我会放弃所有其他索引,因为它们没有帮助,只能增加维护这些表的成本。