我有以下查询:
SELECT `assignments`.`id`
FROM `assignments`
WHERE
`assignments`.`account_id` = 742
AND `assignments`.`method` != 'stray'
AND (
`assignments`.`judge_id` = 2349724
OR (
`assignments`.`role_id` IN (234, 8745)
AND `assignments`.`judge_id` IS null
)
);
此表目前有660万条记录,并且流量很大。我们最慢的查询是上面的查询,即使使用针对account_id,method,法官,id和role_id的索引,它也需要大约0.5 s来运行。
该查询确实使用了提供的索引,但似乎没有太大的帮助。
我在这里可以做些什么来改善查询并将其降低到100ms以下? 660万条记录确实不是那么多= \
我还想补充一点,如果我仅将查询限制在account_id子句(具有自己的索引)中,则速度大致相同。所以我真的很困惑。
以下是仅使用account_id的执行计划:
EXPLAIN select `assignments`.id FROM `assignments`WHERE `assignments`.`account_id` = 374;
+----+-------------+-------------+------------+------+----------------------------------------------------------------------+------------------------------+---------+-------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------------+------------+------+----------------------------------------------------------------------+------------------------------+---------+-------+------+----------+-------------+
| 1 | SIMPLE | assignments | NULL | ref | assignments_account_id_index,assignments_account_id_updated_at_index | assignments_account_id_index | 9 | const | 965 | 100.00 | Using index |
+----+-------------+-------------+------------+------+----------------------------------------------------------------------+------------------------------+---------+-------+------+----------+-------------+
创建表语法:
CREATE TABLE `assignments` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`key` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`batch` int(10) unsigned NOT NULL,
`account_id` bigint(20) unsigned DEFAULT NULL,
`season_id` bigint(20) unsigned DEFAULT NULL,
`judge_id` bigint(20) unsigned DEFAULT NULL,
`role_id` bigint(20) unsigned DEFAULT NULL,
`entry_id` bigint(20) unsigned NOT NULL,
`score_set_id` bigint(20) unsigned NOT NULL,
`slug` char(8) COLLATE utf8_unicode_ci DEFAULT NULL,
`method` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`original_method` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
`status` varchar(255) COLLATE utf8_unicode_ci NOT NULL DEFAULT 'none',
`locked` tinyint(1) NOT NULL DEFAULT '0',
`conflict_of_interest` tinyint(1) NOT NULL DEFAULT '0',
`raw_score` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
`raw_total` double NOT NULL DEFAULT '0',
`weighted_score` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
`weighted_total` double NOT NULL DEFAULT '0',
`weight_sum` decimal(8,2) NOT NULL DEFAULT '0.00',
`progress` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
`consensus` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
`top_pick_preference` tinyint(3) unsigned DEFAULT NULL,
`top_pick_winner` tinyint(1) NOT NULL DEFAULT '0',
`top_pick_rank` int(11) DEFAULT NULL,
`total_votes` bigint(20) unsigned NOT NULL DEFAULT '0',
`scored_at` timestamp NULL DEFAULT NULL,
`created_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
`updated_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
PRIMARY KEY (`id`),
UNIQUE KEY `assignments_key_unique` (`key`),
KEY `assignments_account_id_index` (`account_id`),
KEY `assignments_judge_id_index` (`judge_id`),
KEY `assignments_role_id_index` (`role_id`),
KEY `assignments_entry_id_index` (`entry_id`),
KEY `assignments_score_set_id_index` (`score_set_id`),
KEY `assignments_season_id_index` (`season_id`),
KEY `assignments_slug_index` (`slug`),
KEY `assignments_status_index` (`status`),
KEY `assignments_method_index` (`method`),
KEY `assignments_original_method_index` (`original_method`),
KEY `assignments_account_id_updated_at_index` (`account_id`,`updated_at`)
) ENGINE=InnoDB AUTO_INCREMENT=661994447 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
由于范围条件会对MySQL利用索引的能力产生负面影响,因此有时可以使用UNION(以查询语法的重复为代价:]
SELECT a.id
FROM `assignments` AS a
WHERE a.`account_id` = 742
AND a.`judge_id` = 2349724
AND a.`method` != 'stray'
UNION ALL
SELECT a.id
FROM `assignments` AS a
WHERE a.`account_id` = 742
AND a.`judge_id` IS NULL AND a.`role_id` IN (234, 8745)
AND a.`method` != 'stray'
;
account_id, judge_id, method_id
或account_id, judge_id, role_id
上的复合索引将大大有利于上述查询的性能。 ...如果我没记错的话,其中的第一个可能会使上半年受益,而其中的第二个可能会使下半年受益(但也存在过度索引的情况)。
如果您是我,为了解决问题,我会考虑并做的事情:
SELECT count(*)
FROM `assignments`
WHERE
`assignments`.`account_id` = 742
AND `assignments`.`method` != 'stray'
AND (
`assignments`.`judge_id` = 2349724
OR (
`assignments`.`role_id` IN (234, 8745)
AND `assignments`.`judge_id` IS null
)
);
告诉我们您的特定查询应产生多少条记录。
SELECT `assignments`.`id`
FROM `assignments`
WHERE
`assignments`.`account_id` = 742;
告诉您与给定帐户关联的分配数量。如果计数比实际选择要快得多,那可能意味着一些。另外,如果有很多记录,则可能需要很长时间才能将其加载到内存中并通过网络发送到另一台计算机。
SELECT `assignments`.`id`
FROM `assignments` limit 0, 100;
如果这很慢,则您的网络可能有问题。
进行转储并重新创建数据库,然后在此新创建的沙箱中运行查询,因此您将看到其他命令是否使您变慢。如果其他查询使您减速,则可能是写入操作导致了减速。如果写锁使您慢下来,那么您可能希望将写操作分组并在特定时间一起执行。
由于UUeerdo和GMB已经在其答案中建议了使用where
子句中用作过滤器的字段在表上创建多维索引。
这可能不是一个完整的答案,但是:您没有正确的索引。复合索引与每列上的individual索引不同。
考虑,改为:
(account_id, judge_id, role_id, method, id)
由于AND
/OR
/ IN
,可能实际上并未使用整个索引,但这至少为查询计划者提供了机会。您可能还想针对Uueerdo的union all
查询(已批准)进行尝试。
我想到的第一件事是,您不必在查询中的任何地方都具有“赋值”。
select id FROM `assignments`
WHERE `account_id` = 742
AND `method` != 'stray'
AND (`judge_id` = 2349724
OR (`role_id` IN (234, 8745)
AND `judge_id` IS null));
应该工作正常。好的,那不会解决您的问题。
也许您可以先查询ID,然后再查询方法,就像这样:
select id FROM `assignments`
WHERE `account_id` = 742
AND (`judge_id` = 2349724
OR (`role_id` IN (234, 8745)
AND `judge_id` IS null))
AND `method` != 'stray';
只是一个想法。