我正在尝试优化以下查询而没有成功。有什么想法可以将其编入索引以防止临时表和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 | |
+-------------+------------+----------------------------------------------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+
答案 0 :(得分:0)
memberships
列user_id
上的索引(如果是PK,则应该已有)memberships
列status_code
和manager
上的索引(两者都在同一索引上)groups
列created_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
之外,我会放弃所有其他索引,因为它们没有帮助,只能增加维护这些表的成本。