问题描述
我有一个 MysqL 8 数据库表 accounts
,其中包含以下列:
- id(主要)
- city_id(外键)
- province_id(外键)
- country_id(外键)
- school_id(外键)
- 年龄(已编入索引)
编辑:完整表格结构见底部。
SELECT
COUNT(`id`) AS AGGREGATE
FROM
`accounts`
WHERE
`city_id` = 1
AND
`country_id` = 7
AND
`age` = 3
在 100 万条记录时,此查询变慢(约 200 毫秒)。
运行 EXPLAIN
时,我收到以下输出:
id | 选择类型 | 表 | 分区 | 类型 | possible_keys | 键 | key_len | 参考 | 行 | 过滤 | 额外 |
---|---|---|---|---|---|---|---|---|---|---|---|
1 | 简单 | 帐户 | NULL | index_merge | accounts_city_id_foreign accounts_country_id_foreign accounts_age_index | accounts_city_id_foreign accounts_country_id_foreign accounts_age_index | 9,2,9 | NULL | 15542 | 100.00 | 使用 intersect(accounts_city_id_foreign,accounts_country_id_foreign,accounts_age_index);使用哪里;使用索引 |
鉴于 MysqL 似乎正在使用索引,我不确定我能做些什么来缩短执行时间。有人有什么想法吗?
编辑:将来,该表将包含更多列,这将导致无法使用复合索引,因为它将超过 16 列的限制。
编辑:这是完整的表结构:
CREATE TABLE `accounts` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,`city_id` bigint unsigned DEFAULT NULL,`school_id` bigint unsigned DEFAULT NULL,`country_id` bigint unsigned DEFAULT NULL,`province_id` bigint unsigned DEFAULT NULL,`age` tinyint unsigned DEFAULT NULL,PRIMARY KEY (`id`),KEY `accounts_city_id_foreign` (`city_id`),KEY `accounts_school_id_foreign` (`school_id`),KEY `accounts_country_id_foreign` (`country_id`),KEY `accounts_province_id_foreign` (`province_id`),KEY `accounts_age_index` (`age`),CONSTRAINT `accounts_city_id_foreign` FOREIGN KEY (`city_id`) REFERENCES `cities` (`id`) ON DELETE SET NULL,CONSTRAINT `accounts_country_id_foreign` FOREIGN KEY (`country_id`) REFERENCES `countries` (`id`) ON DELETE SET NULL,CONSTRAINT `accounts_province_id_foreign` FOREIGN KEY (`province_id`) REFERENCES `provinces` (`id`) ON DELETE SET NULL,CONSTRAINT `accounts_school_id_foreign` FOREIGN KEY (`school_id`) REFERENCES `schools` (`id`) ON DELETE SET NULL
) ENGINE=InnoDB AUTO_INCREMENT=1000002 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
解决方法
尝试在所有三列上创建一个复合索引,例如CREATE INDEX idx_city_country_age ON table (city_id,country_id,age)
索引是为了帮助您进行查询。因此,正如 Marko 所建议并得到其他人同意的那样,在 (city_id,age) 上建立索引应该会有很大帮助。现在,是的,您将向表中添加其他列,但是您是否尝试根据 16 个以上的条件进行过滤?我对此表示怀疑。在您将运行的查询中,即使您有多个复合索引来帮助优化这些查询,在任何时候您可能需要多少列? 4、5、6?在那之后,我的意思是你打算如何细化你的数据。国家、州/省、城市、城镇、村庄、社区、街道、房屋?当您的数据如此之低时,您无论如何都会处于页面级别的数据,不是吗?
因此,您对 Country = 7 的查询已经削减了大量内容。然后去那个国家的某个城市?太好了,现在您处于有限级别。
如果您确实要对需要任何聚合的大数据进行查询,并且数据从历史角度来看是相当固定的,那么按一些常见元素预先聚合表可能会有所帮助。
反馈
查询的性能不一定是你会被击中的地方,它会在插入、更新、删除中,因为任何可能的变化都必须更新表上的所有索引 - 单个或复合。如果索引中的列超过 5 列,问问自己,真的吗???优化索引所需的粒度。使用适当的索引查询数据应该非常快。更新索引也很快,但如果您在一个月、一个季度、一年内处理数百万次插入呢?用户做他们的可能会有轻微的延迟(1/4 秒?)但加起来一百万秒开始延迟。但是,无论如何,插入/更新/删除将在多长时间内完成。
,您询问什么会缩短查询时间,而使用复合索引可以做到这一点。搜索单个复合索引比搜索多个单列索引并对结果执行交集合并要快。
您评论说您将来会添加更多列,最终将超过 16 列。
您不必将所有列都添加到复合索引中!
索引设计并不神奇。它遵循规则。您将创建旨在支持您需要运行的特定查询的索引。除非它们有助于给定的查询,否则不要向索引添加添加列。表中可能有多个复合索引,用于帮助不同的查询。
您可能会喜欢我的演示文稿 How to Design Indexes,Really(或 video)。
重新评论:
我不会提前知道所有可能的查询组合。
是的,确实如此。您只能为您知道的查询创建索引。其他查询不会被优化。如果您以后需要优化查询,您可能需要添加新索引来支持它们。
根据我的经验,这种情况经常发生,我在演示文稿中解决了这个问题。您将不时查看您的查询,因为您的应用程序代码当然会更改并且您需要的查询也会更改。您可以添加新索引,或者用不同的索引替换一个索引,或者删除不再需要的索引。