Mysql:优化派生表的计数查询
为了分页的目的,我正在尝试对派生表进行计数查询。查询如下所示:Mysql:优化派生表的计数查询,mysql,performance,Mysql,Performance,为了分页的目的,我正在尝试对派生表进行计数查询。查询如下所示: SELECT assignment_completions.id as id, assignment_completions.first_name, assignment_completions.last_name, groups.name FROM assignment_completions LEFT JOIN groups_users ON assignmen
SELECT
assignment_completions.id as id,
assignment_completions.first_name,
assignment_completions.last_name,
groups.name
FROM
assignment_completions
LEFT JOIN
groups_users ON assignment_completions.user_id = groups_users.user_id
LEFT JOIN
groups ON groups_users.group_id = groups.id
WHERE
assignment_completions.handler = 'course'
GROUP BY assignment_completions.id
SELECT COUNT(*) FROM (...) AS assignment_count
count查询只是像这样包装了上述查询:
SELECT
assignment_completions.id as id,
assignment_completions.first_name,
assignment_completions.last_name,
groups.name
FROM
assignment_completions
LEFT JOIN
groups_users ON assignment_completions.user_id = groups_users.user_id
LEFT JOIN
groups ON groups_users.group_id = groups.id
WHERE
assignment_completions.handler = 'course'
GROUP BY assignment_completions.id
SELECT COUNT(*) FROM (...) AS assignment_count
没有计数的查询将在.005秒内执行。
带有计数的查询将在1.5秒内执行
我尝试了以下方法,但没有成功:
1) 使用索引列(此处没有性能增益):
2) 我尝试过使用SQL\u CALC\u FOUND\u ROWS
,但实际上速度稍慢(2秒左右)
详细信息:
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: assignment_completions
type: index
possible_keys: PRIMARY,index_assignment_completions_on_first_name,index_assignment_completions_on_last_name,index_assignment_completions_on_user_id
key: PRIMARY
key_len: 4
ref: NULL
rows: 199088
filtered: 100.00
Extra: Using where
*************************** 2. row ***************************
id: 1
select_type: SIMPLE
table: users
type: eq_ref
possible_keys: PRIMARY
key: PRIMARY
key_len: 4
ref: lms.assignment_completions.user_id
rows: 1
filtered: 100.00
Extra: Using index
*************************** 3. row ***************************
id: 1
select_type: SIMPLE
table: groups_users
type: ref
possible_keys: index_groups_users_on_user_id
key: index_groups_users_on_user_id
key_len: 5
ref: lms.users.id
rows: 1
filtered: 100.00
Extra: NULL
*************************** 4. row ***************************
id: 1
select_type: SIMPLE
table: groups
type: eq_ref
possible_keys: PRIMARY
key: PRIMARY
key_len: 4
ref: lms.groups_users.group_id
rows: 1
filtered: 100.00
Extra: Using index
*************************** 1. row ***************************
id: 1
select_type: PRIMARY
table: <derived2>
type: ALL
possible_keys: NULL
key: NULL
key_len: NULL
ref: NULL
rows: 199088
filtered: 100.00
Extra: NULL
*************************** 2. row ***************************
id: 2
select_type: DERIVED
table: assignment_completions
type: index
possible_keys: PRIMARY,index_assignment_completions_on_first_name,index_assignment_completions_on_last_name,index_assignment_completions_on_user_id
key: PRIMARY
key_len: 4
ref: NULL
rows: 199088
filtered: 100.00
Extra: Using where
*************************** 3. row ***************************
id: 2
select_type: DERIVED
table: users
type: eq_ref
possible_keys: PRIMARY
key: PRIMARY
key_len: 4
ref: lms.assignment_completions.user_id
rows: 1
filtered: 100.00
Extra: Using index
*************************** 4. row ***************************
id: 2
select_type: DERIVED
table: groups_users
type: ref
possible_keys: index_groups_users_on_user_id
key: index_groups_users_on_user_id
key_len: 5
ref: lms.users.id
rows: 1
filtered: 100.00
Extra: NULL
*************************** 5. row ***************************
id: 2
select_type: DERIVED
table: groups
type: eq_ref
possible_keys: PRIMARY
key: PRIMARY
key_len: 4
ref: lms.groups_users.group_id
rows: 1
filtered: 100.00
Extra: Using index
作业完成:20万行
用户:35k行
用户组:500k行
分组:1k行
表格定义
CREATE TABLE `assignment_completions` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_id` int(11) DEFAULT NULL,
`assignment_id` int(11) DEFAULT NULL,
`handler` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
`handler_id` int(11) DEFAULT NULL,
`time_started` datetime DEFAULT NULL,
`time_end` datetime DEFAULT NULL,
`status` int(11) DEFAULT NULL,
`application_instance_id` int(11) DEFAULT NULL,
`created_at` datetime DEFAULT NULL,
`updated_at` datetime DEFAULT NULL,
`first_name` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
`last_name` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `index_assignment_completions_on_first_name` (`first_name`) USING BTREE,
KEY `index_assignment_completions_on_last_name` (`last_name`) USING BTREE,
KEY `index_assignment_completions_on_user_id` (`user_id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=200001 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
CREATE TABLE `users` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`email` varchar(255) COLLATE utf8_unicode_ci NOT NULL DEFAULT '',
`encrypted_password` varchar(255) COLLATE utf8_unicode_ci NOT NULL DEFAULT '',
`reset_password_token` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
`reset_password_sent_at` datetime DEFAULT NULL,
`remember_created_at` datetime DEFAULT NULL,
`sign_in_count` int(11) NOT NULL DEFAULT '0',
`current_sign_in_at` datetime DEFAULT NULL,
`last_sign_in_at` datetime DEFAULT NULL,
`current_sign_in_ip` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
`last_sign_in_ip` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
`created_at` datetime DEFAULT NULL,
`updated_at` datetime DEFAULT NULL,
`application_instance_id` int(11) DEFAULT NULL,
`username` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
`first_name` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
`last_name` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
`status` int(11) DEFAULT NULL,
`group_list_cache` text COLLATE utf8_unicode_ci,
PRIMARY KEY (`id`),
UNIQUE KEY `index_users_on_reset_password_token` (`reset_password_token`) USING BTREE,
UNIQUE KEY `index_users_on_username_and_application_instance_id` (`username`,`application_instance_id`) USING BTREE,
KEY `index_users_on_application_instance_id` (`application_instance_id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=30006 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
CREATE TABLE `groups_users` (
`group_id` int(11) DEFAULT NULL,
`user_id` int(11) DEFAULT NULL,
UNIQUE KEY `index_groups_users_on_group_id_and_user_id` (`group_id`,`user_id`) USING BTREE,
KEY `index_groups_users_on_group_id` (`group_id`) USING BTREE,
KEY `index_groups_users_on_user_id` (`user_id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
CREATE TABLE `groups` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
`application_instance_id` int(11) DEFAULT NULL,
`created_at` datetime DEFAULT NULL,
`updated_at` datetime DEFAULT NULL,
`description` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
`group_type` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1045 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
解释查询:
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: assignment_completions
type: index
possible_keys: PRIMARY,index_assignment_completions_on_first_name,index_assignment_completions_on_last_name,index_assignment_completions_on_user_id
key: PRIMARY
key_len: 4
ref: NULL
rows: 199088
filtered: 100.00
Extra: Using where
*************************** 2. row ***************************
id: 1
select_type: SIMPLE
table: users
type: eq_ref
possible_keys: PRIMARY
key: PRIMARY
key_len: 4
ref: lms.assignment_completions.user_id
rows: 1
filtered: 100.00
Extra: Using index
*************************** 3. row ***************************
id: 1
select_type: SIMPLE
table: groups_users
type: ref
possible_keys: index_groups_users_on_user_id
key: index_groups_users_on_user_id
key_len: 5
ref: lms.users.id
rows: 1
filtered: 100.00
Extra: NULL
*************************** 4. row ***************************
id: 1
select_type: SIMPLE
table: groups
type: eq_ref
possible_keys: PRIMARY
key: PRIMARY
key_len: 4
ref: lms.groups_users.group_id
rows: 1
filtered: 100.00
Extra: Using index
*************************** 1. row ***************************
id: 1
select_type: PRIMARY
table: <derived2>
type: ALL
possible_keys: NULL
key: NULL
key_len: NULL
ref: NULL
rows: 199088
filtered: 100.00
Extra: NULL
*************************** 2. row ***************************
id: 2
select_type: DERIVED
table: assignment_completions
type: index
possible_keys: PRIMARY,index_assignment_completions_on_first_name,index_assignment_completions_on_last_name,index_assignment_completions_on_user_id
key: PRIMARY
key_len: 4
ref: NULL
rows: 199088
filtered: 100.00
Extra: Using where
*************************** 3. row ***************************
id: 2
select_type: DERIVED
table: users
type: eq_ref
possible_keys: PRIMARY
key: PRIMARY
key_len: 4
ref: lms.assignment_completions.user_id
rows: 1
filtered: 100.00
Extra: Using index
*************************** 4. row ***************************
id: 2
select_type: DERIVED
table: groups_users
type: ref
possible_keys: index_groups_users_on_user_id
key: index_groups_users_on_user_id
key_len: 5
ref: lms.users.id
rows: 1
filtered: 100.00
Extra: NULL
*************************** 5. row ***************************
id: 2
select_type: DERIVED
table: groups
type: eq_ref
possible_keys: PRIMARY
key: PRIMARY
key_len: 4
ref: lms.groups_users.group_id
rows: 1
filtered: 100.00
Extra: Using index
计数查询解释:
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: assignment_completions
type: index
possible_keys: PRIMARY,index_assignment_completions_on_first_name,index_assignment_completions_on_last_name,index_assignment_completions_on_user_id
key: PRIMARY
key_len: 4
ref: NULL
rows: 199088
filtered: 100.00
Extra: Using where
*************************** 2. row ***************************
id: 1
select_type: SIMPLE
table: users
type: eq_ref
possible_keys: PRIMARY
key: PRIMARY
key_len: 4
ref: lms.assignment_completions.user_id
rows: 1
filtered: 100.00
Extra: Using index
*************************** 3. row ***************************
id: 1
select_type: SIMPLE
table: groups_users
type: ref
possible_keys: index_groups_users_on_user_id
key: index_groups_users_on_user_id
key_len: 5
ref: lms.users.id
rows: 1
filtered: 100.00
Extra: NULL
*************************** 4. row ***************************
id: 1
select_type: SIMPLE
table: groups
type: eq_ref
possible_keys: PRIMARY
key: PRIMARY
key_len: 4
ref: lms.groups_users.group_id
rows: 1
filtered: 100.00
Extra: Using index
*************************** 1. row ***************************
id: 1
select_type: PRIMARY
table: <derived2>
type: ALL
possible_keys: NULL
key: NULL
key_len: NULL
ref: NULL
rows: 199088
filtered: 100.00
Extra: NULL
*************************** 2. row ***************************
id: 2
select_type: DERIVED
table: assignment_completions
type: index
possible_keys: PRIMARY,index_assignment_completions_on_first_name,index_assignment_completions_on_last_name,index_assignment_completions_on_user_id
key: PRIMARY
key_len: 4
ref: NULL
rows: 199088
filtered: 100.00
Extra: Using where
*************************** 3. row ***************************
id: 2
select_type: DERIVED
table: users
type: eq_ref
possible_keys: PRIMARY
key: PRIMARY
key_len: 4
ref: lms.assignment_completions.user_id
rows: 1
filtered: 100.00
Extra: Using index
*************************** 4. row ***************************
id: 2
select_type: DERIVED
table: groups_users
type: ref
possible_keys: index_groups_users_on_user_id
key: index_groups_users_on_user_id
key_len: 5
ref: lms.users.id
rows: 1
filtered: 100.00
Extra: NULL
*************************** 5. row ***************************
id: 2
select_type: DERIVED
table: groups
type: eq_ref
possible_keys: PRIMARY
key: PRIMARY
key_len: 4
ref: lms.groups_users.group_id
rows: 1
filtered: 100.00
Extra: Using index
因此,有必要与groups表连接。我相信COUNT(DISTINCT…)aggregate函数将解决这个问题:
SELECT COUNT(DISTINCT assignment_completions.id) AS assignment_count
FROM assignment_completions
LEFT JOIN users ON assignment_completions.user_id = users.id
LEFT JOIN groups_users ON users.id = groups_users.user_id
LEFT JOIN groups ON groups_users.group_id = groups.id
WHERE assignment_completions.handler = 'course';
为了加快速度,您可以将其与其他查询分开运行,如下所示:
SELECT COUNT(DISTINCT id) AS assignment_count
FROM assignment_completions
WHERE assignment_completions.handler = 'course';
由于您最终通过赋值\u completions.id对组进行了分组,并且由于此表与所有其他表进行了左联接,因此所需的查询相当于:
SELECT COUNT(DISTINCT id) FROM assignment_completions;
最后,id
可能是主键(因此是唯一的),因此您只需要:
SELECT COUNT(id) FROM assignment_completions;
由于您确实需要
组
表对其进行筛选,因此不需要左连接
。也许乐观主义者自己就意识到了这一点,但如果没有,用常规的[internal]连接替换所有左连接
,我将在数据上有以下覆盖索引,这样引擎就不必返回原始数据页。另外,由于从用户到组用户的唯一目的是,但是组用户无论如何都是基于用户id的,因此从分配完成直接到组用户表
table index
assignment_completions ( handler, id, user_id, first_name, last_name )
groups_users ( user_id, group_id )
groups ( id, name )
SELECT STRAIGHT_JOIN
assignment_completions.id as id,
assignment_completions.first_name,
assignment_completions.last_name,
groups.name
FROM
assignment_completions
LEFT JOIN groups_users
ON assignment_completions.user_id = groups_users.user_id
LEFT JOIN groups
ON groups_users.group_id = groups.id
WHERE
assignment_completions.handler = 'course'
GROUP BY
assignment_completions.id
@奥鲁克德,我又看了看桌子的结构。真的,你会期望一个名字(第一个/最后一个)超过20-25个字符吗?另外,处理程序字段是否为255个字符?真正地如果这导致数据页过度膨胀,这是否是延迟问题的一部分?在用户的表中也是如此。此外,如果任务完成的名字/姓氏是完成任务的用户的名字/姓氏,那么从长远来看,只将用户的ID存储到表中会容易得多,并缩短任务完成时的磁盘需求。是的,是一个额外的连接,但由于它将基于一个“ID”列,因此速度会很快,特别是如果用户的表是(ID、first\u name、last\u name)上的覆盖索引,但肯定会缩小这些列的大小。类似于组“名称”列。因为我不知道实际索引是如何准备的,如果每个字段分配255个索引,可能会影响索引性能
在重新审视我自己的评论之后。您似乎有一个要查找的处理程序查找表(通过引用handler\u id列)。我会在assignment_completions表上将索引从(handler…)更改为(handler_id…),并删除handler列,因为与单个查找表id指针相比,超过200k行的重复将减少要浏览的页面数量。同样,在分配完成表中保存“userID”与完整的姓/名。这就迫使200k条记录有3列,每列最多255个字符(到目前为止,显然还会增加)
为了处理SELECT COUNT(*)FROM t语句,InnoDB扫描表的索引,如果索引不完全在缓冲池中,这需要一些时间。如果您的表不经常更改,那么使用MySQL查询缓存是一个很好的解决方案。快速计数
您还可以强制InnoDB使用索引:
SELECT COUNT(id) FROM assignment_completions USE INDEX (PRIMARY);
除此之外,我看到您使用了许多索引,这将减慢查询速度
请尝试仅使用您将依赖的id上的索引
Innodb没有缓存行计数。因此,使用Innodb表时,count(*)without where子句的速度很慢
您可以尝试以下方法:
CREATE TEMPORARY TABLE `tmp_stuff` SELECT
assignment_completions.id as id,
assignment_completions.first_name,
assignment_completions.last_name,
groups.name
FROM
assignment_completions
LEFT JOIN
groups_users ON assignment_completions.user_id = groups_users.user_id
LEFT JOIN
groups ON groups_users.group_id = groups.id
WHERE
assignment_completions.handler = 'course'
GROUP BY assignment_completions.id
然后这个:
SELECT count(*) FROM `tmp_stuff`
如果您的计数(*)需要更复杂,您可以始终向临时表添加必要的索引
它不会变得非常快,但是当复杂的查询开始变慢时,即使有正确的索引,临时表也会挽救您的生命。您能添加您的表定义和解释选择的输出吗代码>?您有10万组(100万组,或仅1千组)。至于计数,你想得到什么计数。打字错误!1k组。我正在努力计算总的结果。对于常规查询,我对其进行了限制。然后我再次执行它,不受限制地获得分页的总结果。这在正常情况下效果很好,与will paginate gem for rails使用的技术相同。@orourkedd Hm。。。目前没有任何好主意。也许可以使用mysql探查器来找出究竟需要比预期更长的时间(请阅读此处的工作原理:)。探查器说瓶颈在于“发送数据”,这意味着“您的查询速度很慢”。只是尝试了一下,它并没有提供任何性能提升。顺便说一句,您的初始查询有点失败。在<代码> >选择< /代码>部分中,您拥有<>代码>组。对于一个给定的赋值\u completions.id
,可能存在多个组。name
。您将在结果集中得到的是未定义的。问题是,此查询有时会添加一个附加的“where”子句,如:groups.name,如“%abc%”
如果在group
表上添加条件,则不必使用左连接。使用常规的内部联接
。请注意,没有一个用于所有查询。如果您关心的是性能,那么您通常需要制定一个特定的