Win*_*nks 6 mysql sql database indexing performance
长话短说: 我对 2 个巨大的表有一个查询。它们不是索引。它很慢。因此,我建立索引。它比较慢。为什么这是有道理的?正确的优化方法是什么?
的背景:
我有2张桌子
person,包含人员信息的表 ( id, birthdate)works_inperson,与部门之间的0-N关系;works_in包含id, person_id, department_id.它们是 InnoDB 表,遗憾的是不能选择切换到 MyISAM,因为数据完整性是一项要求。
这两个表很大,除了各自PRIMARY的id.
我正在尝试获取每个部门中最年轻的人的年龄,这是我提出的查询
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
Run Code Online (Sandbox Code Playgroud)
该查询有效,但我对性能不满意,因为运行需要大约 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 |
Run Code Online (Sandbox Code Playgroud)
我为这两个表建立了一堆索引,
/* 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);
Run Code Online (Sandbox Code Playgroud)
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 |
Run Code Online (Sandbox Code Playgroud)
然而,查询的执行时间增加了一倍(从约 17 秒到约 35 秒)。
为什么这是有道理的,优化它的正确方法是什么?
编辑
使用 Gordon Linoff 的答案(第一个),执行时间约为 9 秒(初始时间的一半)。选择好的索引似乎确实有帮助,但执行时间仍然相当长。关于如何改进这一点还有其他想法吗?
有关数据集的更多信息:
person。NULL)出生日期department表,其中包含大约 3'000'000 条记录(它们实际上是项目而不是部门)对于此查询:
SELECT MAX(YEAR(p.birthdate)) as max_year, wi.department as department
FROM person p INNER JOIN
works_in wi
ON wi.person_id = p.id
WHERE p.birthdate IS NOT NULL
GROUP BY wi.department;
Run Code Online (Sandbox Code Playgroud)
最好的索引是: person(birthdate, id)和works_in(person_id, department)。这些覆盖查询的索引并节省读取数据页的额外成本。
顺便说一句,除非很多人都有NULL出生日期(即有部门每个人都有NULL出生日期),否则查询基本上相当于:
SELECT MAX(YEAR(p.birthdate)) as max_year, wi.department as department
FROM person p INNER JOIN
works_in wi
ON wi.person_id = p.id
GROUP BY wi.department;
Run Code Online (Sandbox Code Playgroud)
为此,最好的索引是person(id, birthdate)和works_in(person_id, department)。
编辑:
我想不出一个简单的方法来解决这个问题。一种解决方案是更强大的硬件。
如果您确实快速需要此信息,则需要进行额外的工作。
一种方法是向表中添加最大出生日期departments,并添加触发器。对于works_in,您需要update、insert和 的触发器delete。仅对于persons, update(大概insert和delete将由 处理works_in)。这样就节省了最后的group by,这应该是一笔很大的节省。
一种更简单的方法是将最大出生日期添加到works_in。但是,您仍然需要最终聚合,这可能会很昂贵。
| 归档时间: |
|
| 查看次数: |
7208 次 |
| 最近记录: |