加入表时性能缓慢

Posted

技术标签:

【中文标题】加入表时性能缓慢【英文标题】:Slow performance on joining table 【发布时间】:2015-06-01 08:32:46 【问题描述】:

我正在尝试优化如下查询,我研究了如何添加索引以提高性能,但结果仍然很慢。下面的查询运行了 20 秒,Transaction 包含大约 100k 条记录,连接表 TransactionDetail 包含大约 500k 条记录。

    SELECT Transaction.id ....
    FROM Transaction
    INNER JOIN Agent ON Agent.id = Transaction.agent_id
    INNER JOIN Distributor ON Distributor.id = Transaction.distributor_id
    INNER JOIN TransactionDetail ON Transaction.id = TransactionDetail.transaction_id
    WHERE TransactionDetail.type =  'Admin'
    AND Transaction.status IN ('pending',  'processing',  'success',  'rejected')
    ORDER BY issued_date DESC LIMIT 0 , 10

从上面的查询中,我尝试应用我对索引的理解

    由于内部连接,我添加了 3 个索引, Transaction(agent_id,distributor_id) 和 Transaction(transaction_id)

    从 where 子句中我添加了 Transaction(status)

    由于 ORDER BY 我添加了 Transaction(issued_date)

但它没有显示出任何改进,这是我从 EXPLAIN 中得到的

这是来自 phpmyadmin 的屏幕截图,显示了表事务的索引

有没有办法改进这个查询?或者它已经优化了,我应该专注于 mysql 配置?

【问题讨论】:

如果您认为我应该添加更多信息,请给我写评论 【参考方案1】:

您的查询

SELECT Transaction.id ....
FROM Transaction
INNER JOIN Agent ON Agent.id = Transaction.agent_id
INNER JOIN Distributor ON Distributor.id = Transaction.distributor_id
INNER JOIN TransactionDetail ON Transaction.id = TransactionDetail.transaction_id
WHERE TransactionDetail.type =  'Admin'
AND Transaction.status IN ('pending',  'processing',  'success',  'rejected')
ORDER BY issued_date DESC LIMIT 0 , 10

现在您已经在表上应用了索引,这很好,但是in clause 更像or 这会产生一个真正的性能问题。在小数据集的情况下,这无法观察到,但在大数据集中 性能会大幅下降。

优化它的一种方法是将in clause 转换为union,它比or in 效果更好

(
 SELECT Transaction.id ....
 FROM Transaction
 INNER JOIN Agent ON Agent.id = Transaction.agent_id
 INNER JOIN Distributor ON Distributor.id = Transaction.distributor_id
 INNER JOIN TransactionDetail ON Transaction.id = TransactionDetail.transaction_id
 WHERE TransactionDetail.type =  'Admin'
 AND Transaction.status = 'pending'
)
union
(
 SELECT Transaction.id ....
 FROM Transaction
 INNER JOIN Agent ON Agent.id = Transaction.agent_id
 INNER JOIN Distributor ON Distributor.id = Transaction.distributor_id
 INNER JOIN TransactionDetail ON Transaction.id = TransactionDetail.transaction_id
 WHERE TransactionDetail.type =  'Admin'
 AND Transaction.status = 'processing'
)
union
(
 SELECT Transaction.id ....
 FROM Transaction
 INNER JOIN Agent ON Agent.id = Transaction.agent_id
 INNER JOIN Distributor ON Distributor.id = Transaction.distributor_id
 INNER JOIN TransactionDetail ON Transaction.id = TransactionDetail.transaction_id
 WHERE TransactionDetail.type =  'Admin'
 AND Transaction.status = 'success'
)
union
(
 SELECT Transaction.id ....
 FROM Transaction
 INNER JOIN Agent ON Agent.id = Transaction.agent_id
 INNER JOIN Distributor ON Distributor.id = Transaction.distributor_id
 INNER JOIN TransactionDetail ON Transaction.id = TransactionDetail.transaction_id
 WHERE TransactionDetail.type =  'Admin'
 AND Transaction.status = 'rejected'
)
order by issued_date DESC LIMIT 0 , 10

为了解决订单,您可能需要将索引添加为

alter table Transaction add index status_created_idx(status,issued_date);

【讨论】:

这很好,只是这是一个小数据集。 @Strawberry 对于在statusissued_date 上有索引的小数据集可以解决问题,即使是10 万条记录,我可以告诉你结果应该在0.2 秒内返回最大。 你错过了一部分issued_date ;) 其余 100% 同意。 我已经尝试重写我的代码,但它几乎没有改进,有没有其他方法可以改进它? 您可以尝试您的原始查询,但请确保您的索引为alter table Transaction add index status_created_idx(status,issued_date); 备份表并应用索引,然后查看性能。【参考方案2】:

添加所有INDEXes 是徒劳的,因为优化器将(几乎总是)只使用一个索引。

原始查询可能受益于INDEX(issue_date)。添加该索引,然后向我们展示EXPLAIN SELECT ... 的输出,看看它是否正在使用它。

如果它正在使用它,那只会通过避免“文件排序”而受益,这通常只是总时间的一小部分。由于LIMIT,它也可能受益于停止。但是,它可能必须读取超过 10 行,因为 WHERE 包含其他表。

由于TransactionDetail.type = 'Admin',它必须通过JOINs 才能决定保留哪些行。

可能导致其他选择的两个问题:

TransactionDetail 的行中有多少百分比具有 type = 'Admin'? Transaction 行的状态为 IN(“待处理”、“处理中”、“成功”、“已拒绝”)的百分比是多少?

在您进行实验时,发布 (1) 索引和 (2) EXPLAIN。

【讨论】:

以上是关于加入表时性能缓慢的主要内容,如果未能解决你的问题,请参考以下文章

加入大表时,postgres 查询速度慢

大表 (EF) 上的插入性能非常慢

剑道网格:性能缓慢问题

Tableau Online 性能缓慢

Firestore 获取数据时性能缓慢的问题

使用大表时提高 MERGE 性能