这个问题让我发疯了很长一段时间。它有 3 个表(最初有更多表,但我隔离了性能问题)、1 个基表、1 个添加更多数据的产品表和 1 个产品类型表。 产品类型表包含一个“最大年龄”列,该列指示我想要获取的行的最大年龄(任何较旧的内容都被视为“已存档”),并且其值根据产品类型而不同。 我的性能较差的查询是这样的,对于 250,000 行的基表需要 50 秒:
(select d_baseservices.ID
from d_baseservices
inner join d_products on d_baseservices.ServiceID = d_products.ServiceID
inner join md_prodtypes on d_products.ProdType = md_prodtypes.ProdType
where
(d_baseservices.CreationDate > (curdate() - INTERVAL md_prodtypes.MaxAge DAY))
order by CreationDate desc
limit 750);
以下是此查询的说明:
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE md_prodtypes index PRIMARY,ProdType_UNIQUE,ID_MAX_AGE MAX_AGE 5 23 Using index; Using temporary; Using filesort
1 SIMPLE d_products ref PRIMARY,ServiceID_UNIQUE,fk_Products_BaseServices1,fk_d_products_md_prodtypes1 fk_d_products_md_prodtypes1 4 combina.md_prodtypes.ProdType 8625
1 SIMPLE d_baseservices eq_ref PRIMARY,CreationDateDesc_index,CreationDate_index PRIMARY 8 combina.d_products.ServiceID 1 Using where
几天前我发现了一条线索,当时我能够确定将查询限制为 750 条记录会导致速度加快,但 751 条记录会带来较差的性能。
我尝试创建多种索引,但没有成功。 我尝试删除对 MAX_AGE 和 curdate 函数的引用,只设置一个固定值,但收效甚微,因为查询现在需要 20 秒:
(select d_baseservices.ID
from d_baseservices
inner join d_products on d_baseservices.ServiceID = d_products.ServiceID
inner join md_prodtypes on d_products.ProdType = md_prodtypes.ProdType
where
(d_baseservices.CreationDate > '2015-09-21 19:02:25')
order by CreationDate desc
limit 750);
EXPLAIN 命令输出:
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE md_prodtypes index PRIMARY,ProdType_UNIQUE,ID_MAX_AGE ProdType_UNIQUE 4 23 Using index; Using temporary; Using filesort
1 SIMPLE d_products ref PRIMARY,ServiceID_UNIQUE,fk_Products_BaseServices1,fk_d_products_md_prodtypes1 fk_d_products_md_prodtypes1 4 combina.md_prodtypes.ProdType 8625
1 SIMPLE d_baseservices eq_ref PRIMARY,CreationDateDesc_index,CreationDate_index PRIMARY 8 combina.d_products.ServiceID 1 Using where\
有人可以帮忙吗?我被困了快一个月了
最佳答案
如果不了解更多关于您拥有的具体数据(每个表中有多少行,您期望查询返回多少行,数据值的分布等),很难准确地说出该怎么做,但我我们将做出一些有根据的猜测,并希望为您指明正确的方向。
首先解释一下为什么从查询中取出 md_prodtypes.MaxAge
大大减少了运行时间:在此更改之前,数据库根本无法使用索引进行过滤,因为为了查看是否行是包含的候选者,必须连接三个表,以便将第一个表中的 CreationDate
与第三个表中的 MaxAge
进行比较。根本没有可以添加的索引来关联这两个值。您强制数据库引擎查看每一行。
至于 750 个魔数(Magic Number) - 我猜测过去的 750 个结果数据库必须对数据进行分页,或者它根据特定 MySQL 配置文件中的值达到了其他内存限制。我不会过多解读 750 这个数字。
最后我想指出,您的第二个查询的 EXPLAIN 有点奇怪,因为它显示 md_prodtypes
作为第一个表,尽管您采用了 MaxAge
出WHERE
。这意味着数据库从 md_prodtypes
开始,然后向上移动到 d_products
,最后移动到 d_baseservices
,然后才根据日期进行过滤。我猜您希望它首先过滤日期,然后仅在决定要包含哪些基本服务记录时才加入。根据您提供的信息不可能知道为什么会发生这种情况。也许您缺少索引。
另一种可能性可能与您的 CreationDate
列中的差异有关。让我通过示例进行解释:假设您有一个用户表,每个用户都有一个 gender
列,可以是 f
或 m
。假设女性和男性的比例为 50%/50%。现在,如果您在 gender
列上添加索引,并执行按 WHERE sex='f'
过滤的查询,期望索引会过滤掉一半的记录,那么您会惊讶地发现数据库将完全忽略索引而只扫描表。原因是,如果您知道索引过滤得不够,则读取整个表会更便宜(另一种选择是不断从索引跳转到主表数据)。在您的情况下,如果 CreationDate
列上的 WHERE 没有过滤出足够的记录,那么即使您有索引,也不会使用它。
关于MySQL JOIN 和 ORDER BY - 性能问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34100506/