MySQL JOIN 和 ORDER BY - 性能问题

标签 mysql performance database-performance

这个问题让我发疯了很长一段时间。它有 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 作为第一个表,尽管您采用了 MaxAgeWHERE。这意味着数据库从 md_prodtypes 开始,然后向上移动到 d_products,最后移动到 d_baseservices,然后才根据日期进行过滤。我猜您希望它首先过滤日期,然后仅在决定要包含哪些基本服务记录时才加入。根据您提供的信息不可能知道为什么会发生这种情况。也许您缺少索引。
另一种可能性可能与您的 CreationDate 列中的差异有关。让我通过示例进行解释:假设您有一个用户表,每个用户都有一个 gender 列,可以是 fm。假设女性和男性的比例为 50%/50%。现在,如果您在 gender 列上添加索引,并执行按 WHERE sex='f' 过滤的查询,期望索引会过滤掉一半的记录,那么您会惊讶地发现数据库将完全忽略索引而只扫描表。原因是,如果您知道索引过滤得不够,则读取整个表会更便宜(另一种选择是不断从索引跳转到主表数据)。在您的情况下,如果 CreationDate 列上的 WHERE 没有过滤出足够的记录,那么即使您有索引,也不会使用它。

关于MySQL JOIN 和 ORDER BY - 性能问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34100506/

相关文章:

MYSQL 拒绝远程连接,错误 "ERROR 2003 (HY000)"和 TCP 错误 111

java - utf8_unicode_ci 字符串插入错误?

performance - AWS S3 超慢速度

database-performance - ArangoDB 中 UPDATE 出现奇怪的性能问题

mysql - MySQL docker compose后,SQL脚本执行不起作用

php - MySQL全文搜索 boolean 模式混淆

JavaScript 循环性能 - 为什么将迭代器递减到 0 比递增更快

python - 搁置对于大型词典来说太慢了,我该怎么做才能提高性能?

mysql - 如何提高SQL的性能?(使用子查询)

sql-server - 将数据库和 Web 服务器分开时,RDS 性能真的很差吗?