【发布时间】:2015-08-13 05:44:23
【问题描述】:
TL;DR: 我有 2 个大表的查询。它们不是索引。它很慢。因此,我建立索引。它更慢。为什么这有意义?优化它的正确方法是什么?
背景:
我有两张桌子
-
person,包含人员信息的表格 (id, birthdate) -
works_in,person与部门之间的0-N关系;works_in包含id, person_id, department_id。
它们是 InnoDB 表,遗憾的是不能切换到 MyISAM,因为要求数据完整性。
这 2 个表很大,除了各自的 id 上的 PRIMARY 之外不包含任何索引。
我正在尝试获取每个部门中最年轻的人的年龄,这是我提出的问题
SELECT MAX(YEAR(person.birthdate)) as max_year, works_in.department as department
FROM person
INNER JOIN works_in
ON works_in.person_id = person.id
WHERE person.birthdate IS NOT NULL
GROUP BY works_in.department
查询有效,但我对性能不满意,因为它需要大约 17 秒才能运行。这是意料之中的,因为数据很大,需要写入磁盘,而且它们不是表上的索引。
EXPLAIN 这个查询给出了
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
|----|-------------|---------|--------|---------------|---------|---------|--------------------------|----------|---------------------------------|
| 1 | SIMPLE | works_in| ALL | NULL | NULL | NULL | NULL | 22496409 | Using temporary; Using filesort |
| 1 | SIMPLE | person | eq_ref | PRIMARY | PRIMARY | 4 | dbtest.works_in.person_id| 1 | Using where |
我为 2 个表建立了一堆索引,
/* For works_in */
CREATE INDEX person_id ON works_in(person_id);
CREATE INDEX department_id ON works_in(department_id);
CREATE INDEX department_id_person ON works_in(department_id, person_id);
CREATE INDEX person_department_id ON works_in(person_id, department_id);
/* For person */
CREATE INDEX birthdate ON person(birthdate);
EXPLAIN 显示了改进,至少我是这么理解的,因为它现在使用索引并扫描更少的行。
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
|----|-------------|---------|-------|--------------------------------------------------|----------------------|---------|------------------|--------|-------------------------------------------------------|
| 1 | SIMPLE | person | range | PRIMARY,birthdate | birthdate | 4 | NULL | 267818 | Using where; Using index; Using temporary; Using f... |
| 1 | SIMPLE | works_in| ref | person,department_id_person,person_department_id | person_department_id | 4 | dbtest.person.id | 3 | Using index |
但是,查询的执行时间增加了一倍(从 ~17s 到 ~35s)。
为什么这有意义,优化它的正确方法是什么?
编辑
使用 Gordon Linoff 的答案(第一个),执行时间约为 9 秒(初始的一半)。选择好的索引似乎确实有帮助,但执行时间仍然相当长。关于如何改进这一点的任何其他想法?
有关数据集的更多信息:
-
person表中有大约 5'000'000 条记录。 - 其中只有 130'000 人拥有有效的(不是
NULL)生日 - 我确实有一个
department表,其中包含大约3'000'000 条记录(它们实际上是项目,而不是部门)
【问题讨论】:
标签: mysql sql database performance indexing