MySQL-使用复合索引键改进计数(*)聚合

MySQL-使用复合索引键改进计数(*)聚合,mysql,sql,query-optimization,aggregation,sql-query-store,Mysql,Sql,Query Optimization,Aggregation,Sql Query Store,我有一个表,结构如下,几乎有120000行 描述用户组报告 +---------+-----+---+---+----------+----+ |字段|类型|空|键|默认|额外| +---------+-----+---+---+----------+----+ |用户id | int | YES | MUL | NULL || |组id | int11 | YES | MUL | NULL || |键入| id | int11 | YES | | NULL || |组描述| varchar12

我有一个表,结构如下,几乎有120000行

描述用户组报告 +---------+-----+---+---+----------+----+ |字段|类型|空|键|默认|额外| +---------+-----+---+---+----------+----+ |用户id | int | YES | MUL | NULL || |组id | int11 | YES | MUL | NULL || |键入| id | int11 | YES | | NULL || |组描述| varchar128 |否| |空| |状态|枚举“打开”、“关闭”|否|空|| |上次更新日期时间否当前时间戳| +---------+-----+---+---+----------+----+

我在以下键上有索引:

用户\组\类型用户\ id,组\ id,组\类型 组\类型组\ id,类型\ id 用户\类型用户\ id,类型\ id 用户\组用户\ id,组\ id 我的问题是,我在上面的表中逐组\u id运行count*聚合,并且在type\u 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 |
+----+-------------+------------------+-------+---------------------------------+---------+---------+------+--------+--------------------------+
据我所知,由于复杂的索引,查询几乎会进行一次完整的表扫描,当我尝试在组_id上添加索引时,explain计划中的行显示的数量较少,几乎是行的一半,但执行查询所需的时间增加到了0.4-0.5秒

我尝试了不同的方法来添加/删除索引,但没有一种方法能够减少所花费的时间

假设表结构无法更改,并且查询独立于其他表,有人能给我建议一种更好的方法来优化上面的查询吗?或者如果我在这里遗漏了什么

附言: 我已经尝试将查询修改为以下内容,但找不到任何改进

select count(user_id) user_count, group_id
from user_group_report
where type_id = 1
group by group_id;
任何小小的帮助都将不胜感激

编辑:

根据建议,我添加了一个新的索引

类型组在类型组id上,组id

这是新的计划。explain中的行数减少了,但查询执行时间仍然相同

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

如果type_id是选择性的,即它显著减少了搜索空间,那么在type_id、group_id上创建索引应该会有很大帮助

这是因为它减少了需要分组的记录数,首先删除类型为_id!=1,然后才进行分组/求和

编辑:

根据评论,我们似乎需要进一步了解瓶颈在哪里——查找记录或分组/求和

第一步是衡量以下各项的绩效:

select count(*)
from user_group_report
where type_id = 1

如果这要快得多,那么分组可能比查找记录更困难。如果搜索速度同样慢,那么首先要查找记录。

如果type\u id是选择性的,即它大大减少了搜索空间,那么在type\u id、group\u id上创建索引应该会有很大帮助

这是因为它减少了需要分组的记录数,首先删除类型为_id!=1,然后才进行分组/求和

编辑:

根据评论,我们似乎需要进一步了解瓶颈在哪里——查找记录或分组/求和

第一步是衡量以下各项的绩效:

select count(*)
from user_group_report
where type_id = 1
如果这要快得多,那么分组可能比查找记录更困难。如果这也一样慢的话,那首先是在查找记录。

我相信你的分组类型是错误的。尝试切换属性

create index ix_type_group on user_group_report(type_id,group_id)
此索引更适合您的查询,因为您在where子句中指定了类型_id=1。因此,查询处理器在索引中找到第一条类型为_id=1的记录,然后扫描该类型为_id的索引中的记录并执行聚合。使用这样的索引,只能访问索引中的相关记录,而使用组类型索引是不可能的。

我认为您的组类型是错误的。尝试切换属性

create index ix_type_group on user_group_report(type_id,group_id)

此索引更适合您的查询,因为您在where子句中指定了类型_id=1。因此,查询处理器在索引中找到第一条类型为_id=1的记录,然后扫描该类型为_id的索引中的记录并执行聚合。使用这样的索引,只能访问索引中的相关记录,而使用组类型索引是不可能的。

大多数列真的需要为空吗?在适用的情况下更改为NOTNULL

表格中类型为1的百分比是多少?如果它是表中的大部分,那么这就解释了为什么您没有看到太多的改进。同时,“解释”似乎认为,type_id只有两个不同的值,因此它表示只有一半t 将扫描该表-此号码不可信

要深入了解正在发生的事情,请执行以下操作:

EXPLAIN FORMAT=JSON SELECT...;


我们可以帮助解释你在那里得到的数据。这是一个简短的讨论。

大多数列真的需要为空吗?在适用的情况下更改为NOTNULL

表格中类型为1的百分比是多少?如果它是表中的大部分,那么这就解释了为什么您没有看到太多的改进。同时,解释似乎认为type_id只有两个不同的值,因此它说只有一半的表将被扫描-这个数字不可信

要深入了解正在发生的事情,请执行以下操作:

EXPLAIN FORMAT=JSON SELECT...;



我们可以帮助解释你在那里得到的数据。这是一个简短的讨论。

谢谢,它确实有助于减少计算行数,但查询执行时间仍然是一样的。您看到了什么问题吗?它与其他索引有什么关系吗,虽然我觉得这不是问题所在。如果执行时间仍然相同,那么您的环境中一定存在一些外部问题网络延迟?我不认为是这样,因为其他查询没有问题,我直接从mysql控制台运行它。谢谢,这确实有助于减少计算行数,但查询执行时间仍然是一样的任何问题你看到了吗?它与其他指数有什么关系吗,虽然我觉得这不是问题所在。如果执行时间仍然相同,那么您的环境中一定有一些外部问题网络延迟?我不认为是这样,因为其他查询没有问题,我直接从mysql控制台运行。谢谢。我从Other的回答中也看到了同样的建议。请跟随我的评论尽管评估行减少到一半,但我仍然无法缩短时间。您能否更新您的问题,显示新的查询计划和索引?在这种情况下,您可能在其他地方遇到瓶颈-您是否检查了内存,CPU和磁盘利用率?我不认为这是问题所在,因为其他表甚至同一个表上的所有其他查询都运行良好。谢谢。我从Other的回答中也看到了同样的建议。请跟随我的评论尽管评估行减少到一半,但我仍然无法缩短时间。您能否更新您的问题,显示新的查询计划和索引?在这种情况下,您可能在其他地方遇到瓶颈-您是否检查了内存,CPU和磁盘利用率?我不认为这是问题所在,因为在其他表上,甚至在同一个表上进行的所有其他查询都运行良好。countuser\u id可能比count*慢,因为它检查user\u id是否不为NULL。主键是什么?请提供SHOW CREATE TABLE,它比DESC更具描述性。countuser\u id可能比count*慢,因为它检查user\u id是否不为NULL。主键是什么?请提供SHOW CREATE TABLE,它比DESC更具描述性。