我有一个具有以下结构的表,其中包含近120000行,
desc user_group_report
+------------------+----------+------+-----+-------------------+-------+
| Field | Type | Null | Key | Default | Extra |
+------------------+----------+------+-----+-------------------+-------+
| user_id | int | YES | MUL | NULL | |
| group_id | int(11) | YES | MUL | NULL | |
| type_id | int(11) | YES | | NULL | |
| group_desc | varchar(128)| NO| | NULL |
| status | enum('open','close')|NO| | NULL | |
| last_updated | datetime | NO | | CURRENT_TIMESTAMP | |
+------------------+----------+------+-----+-------------------+-------+
我在以下键上有索引:
我的问题是我正在按group_id在上面的表组上运行count(*)聚合,并且在type_id上有一个子句
这是查询:
select count(*) user_count, group_id
from user_group_report
where type_id = 1
group by group_id;
这是解释计划(查询平均需要0.3秒):
+----+-------------+------------------+-------+---------------------------------+---------+---------+------+--------+--------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+------------------+-------+---------------------------------+---------+---------+------+--------+--------------------------+
| 1 | SIMPLE | user_group_report | index | user_group_type,group_type,user_group | group_type | 10 | NULL | 119811 | Using where; Using index |
+----+-------------+------------------+-------+---------------------------------+---------+---------+------+--------+--------------------------+
据我所知,由于索引复杂,查询几乎会进行全表扫描,当我尝试在group_id上添加索引时,说明计划中的行显示的数字较少(几乎是行的一半),但花费时间查询执行时间增加到0.4-0.5秒。
我尝试了不同的方式来添加/删除索引,但是没有一种方法可以减少花费的时间。
假设表结构无法更改,并且查询独立于其他表,有人可以建议我一种更好的方法来优化上述查询,或者如果我在这里遗漏任何内容。
PS: 我已经尝试将查询修改为以下内容,但找不到任何改进。
select count(user_id) user_count, group_id
from user_group_report
where type_id = 1
group by group_id;
不胜感激。
编辑:
根据建议,我添加了一个新索引
type_group on(type_id,group_id)
这是新的解释计划。说明中的行数减少了,但查询执行时间仍然相同
+----+-------------+------------------+------+---------------------------------+---------+---------+-------+-------+--------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+------------------+------+---------------------------------+---------+---------+-------+-------+--------------------------+
| 1 | SIMPLE | user_group_report | ref | user_group_type,type_group,user_group | type_group | 5 | const | 59846 | Using where; Using index |
+----+-------------+------------------+------+---------------------------------+---------+---------+-------+-------+--------------------------+
编辑2: 根据答案/评论中的建议添加详细信息
select count(*)
from user_group_report
where type_id = 1
此查询本身需要0.25秒才能执行。
这是解释计划:
+----+-------------+------------------+------+---------------+---------+---------+-------+-------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+------------------+------+---------------+---------+---------+-------+-------+-------------+
| 1 | SIMPLE | user_group_report | ref | type_group | type_group | 5 | const | 59866 | Using index |
+----+-------------+------------------+------+---------------+---------+---------+-------+-------+-------------+
答案 0 :(得分:3)
我相信您的group_type
是错误的。尝试切换属性。
create index ix_type_group on user_group_report(type_id,group_id)
此索引更适合您的查询,因为您在type_id = 1
子句中指定了where
。因此,查询处理器找到索引中带有type_id = 1
的第一条记录,然后扫描带有该type_id
的索引中的记录并执行聚合。使用这种索引,只能访问索引中的相关记录,而group_type
索引则无法访问。
答案 1 :(得分:1)
如果type_id是选择性的(即,它大大减少了搜索空间),则在type_id, group_id
上创建索引应该会很有帮助。
这是因为它减少了需要首先分组的记录数(删除了type_id!= 1的所有内容),然后才进行分组/求和。
编辑:
在评论之后,似乎我们需要找出更多瓶颈所在-查找记录或分组/求和。
第一步是评估以下各项的效果:
select count(*)
from user_group_report
where type_id = 1
如果这要快得多,则与查找记录相比,分组中的挑战可能更大。如果那样慢,那就是首先查找记录。
答案 2 :(得分:0)
大多数列是否真的需要NULLable
?更改为NOT NULL
。
type_id = 1
占表的百分之几?如果这是表格的大部分内容,那么这将解释为什么您看不到太多改进。同时,EXPLAIN
似乎在想type_id
只有两个不同的值,因此它说只扫描了一半的表-该数字不可信。
要对正在发生的事情有更多的了解,请执行以下操作:
EXPLAIN FORMAT=JSON SELECT...;
和
FLUSH STATUS;
SELECT ...
SHOW SESSION STATUS LIKE 'Handler%';
我们可以帮助您解释到达那里的数据。 (Here是对此的简短讨论。)