我如何在连接的 InnoDB 表上优化这个 COUNT DISTINCT?
How can I optimise this COUNT DISTINCT on joined InnoDB tables?
SELECT COUNT(DISTINCT r.id)
FROM views v
INNER JOIN emails e ON v.email_id = e.id
INNER JOIN recipients r ON e.recipient_id = r.id
INNER JOIN campaigns c ON e.campaign_id = c.id
WHERE c.centre_id IS NULL;
... 或者,"how many unique email opens have we had? (on general campaigns)"
目前在 Amazon RDS 实例上 运行 大约需要一分半钟。所涉及表格的总行数大致为:
- 活动:250
- 收件人:330,000
- 观看次数:530,000
- 电子邮件:1,380,000
EXPLAIN 给我:
1 SIMPLE r index PRIMARY UNIQ_146632C4E7927C74 767 NULL 329196 Using index
1 SIMPLE e ref PRIMARY,IDX_4C81E852E92F8F78,IDX_4C81E852F639F774 IDX_4C81E852E92F8F78 111 ecomms.r.id 1 Using where
1 SIMPLE v ref IDX_11F09C87A832C1C9 IDX_11F09C87A832C1C9 111 ecomms.e.id 1 Using where; Using index
1 SIMPLE c eq_ref PRIMARY,IDX_E3737470463CD7C3 PRIMARY 110 ecomms.e.campaign_id 1 Using where
我怎样才能更快地得到这个总数?
如果您不在recipients.id
和emails.recipent_id
、[=35之间强制执行外键约束,则只需要加入recipients
=] 和 您想要排除未(不再)在 recipients
table 中登记的收件人。否则,直接从连接中省略 table ;您可以使用 emails.recipient_id
而不是 recipients.id
。省略加入应该是一个巨大的胜利。
或者,从联接中省略 recipients
,因为它与所提出的问题无关,该问题是关于打开的唯一 电子邮件 ,而不是关于唯一 recipients 打开任何电子邮件。在那种情况下,您应该可以 SELECT COUNT(*) FROM ...
因为每个 emails
行已经是唯一的。
除此之外,您似乎已经很好地利用了索引,但我承认我发现 EXPLAIN PLAN
输出难以阅读,尤其是没有标题。不过,看起来您的查询根本没有读取基础 table,因此添加新索引不太可能有帮助。
您可以尝试对查询中涉及的 table 执行 OPTIMIZE TABLE
,尽管这听起来可能比实际情况更有希望。
您应该定期运行 ANALYZE TABLE
对涉及此查询的table 进行查询,以使查询优化器有最大可能选择最佳计划。不过,看起来优化器已经在选择一个合理的计划,所以这可能帮不上什么忙。
如果您仍然需要更好的性能,那么有 other possibilities(包括移动到更快的硬件),但它们太多了,无法在这里讨论。
您希望 MySQL 能够利用 WHERE
子句立即限制结果集。为此,您需要适当的索引来加入从活动到电子邮件,然后从电子邮件到收件人和视图。
在 campaigns.centre_id 上建立索引以帮助搜索(满足 WHERE
子句)。我假设 campaigns.id 是 table.
上的主键
在 emails.campaign_id 上建立索引以帮助加入来自活动的电子邮件。将 recipient_id 和 email_id 添加到该索引以提供覆盖索引。
现在,EXPLAIN
结果应按顺序显示 table,从营销活动开始,然后是电子邮件,然后是其他两个。 MySQL 仍需要一个内部临时 table 来应用 DISTINCT
。你确定你需要那个吗?
我假设 emails.id 和 recipients.id 是主键。
SELECT COUNT(DISTINCT r.id)
FROM views v
INNER JOIN emails e ON v.email_id = e.id
INNER JOIN recipients r ON e.recipient_id = r.id
INNER JOIN campaigns c ON e.campaign_id = c.id
WHERE c.centre_id IS NULL;
... 或者,"how many unique email opens have we had? (on general campaigns)"
目前在 Amazon RDS 实例上 运行 大约需要一分半钟。所涉及表格的总行数大致为:
- 活动:250
- 收件人:330,000
- 观看次数:530,000
- 电子邮件:1,380,000
EXPLAIN 给我:
1 SIMPLE r index PRIMARY UNIQ_146632C4E7927C74 767 NULL 329196 Using index
1 SIMPLE e ref PRIMARY,IDX_4C81E852E92F8F78,IDX_4C81E852F639F774 IDX_4C81E852E92F8F78 111 ecomms.r.id 1 Using where
1 SIMPLE v ref IDX_11F09C87A832C1C9 IDX_11F09C87A832C1C9 111 ecomms.e.id 1 Using where; Using index
1 SIMPLE c eq_ref PRIMARY,IDX_E3737470463CD7C3 PRIMARY 110 ecomms.e.campaign_id 1 Using where
我怎样才能更快地得到这个总数?
如果您不在recipients.id
和emails.recipent_id
、[=35之间强制执行外键约束,则只需要加入recipients
=] 和 您想要排除未(不再)在 recipients
table 中登记的收件人。否则,直接从连接中省略 table ;您可以使用 emails.recipient_id
而不是 recipients.id
。省略加入应该是一个巨大的胜利。
或者,从联接中省略 recipients
,因为它与所提出的问题无关,该问题是关于打开的唯一 电子邮件 ,而不是关于唯一 recipients 打开任何电子邮件。在那种情况下,您应该可以 SELECT COUNT(*) FROM ...
因为每个 emails
行已经是唯一的。
除此之外,您似乎已经很好地利用了索引,但我承认我发现 EXPLAIN PLAN
输出难以阅读,尤其是没有标题。不过,看起来您的查询根本没有读取基础 table,因此添加新索引不太可能有帮助。
您可以尝试对查询中涉及的 table 执行 OPTIMIZE TABLE
,尽管这听起来可能比实际情况更有希望。
您应该定期运行 ANALYZE TABLE
对涉及此查询的table 进行查询,以使查询优化器有最大可能选择最佳计划。不过,看起来优化器已经在选择一个合理的计划,所以这可能帮不上什么忙。
如果您仍然需要更好的性能,那么有 other possibilities(包括移动到更快的硬件),但它们太多了,无法在这里讨论。
您希望 MySQL 能够利用 WHERE
子句立即限制结果集。为此,您需要适当的索引来加入从活动到电子邮件,然后从电子邮件到收件人和视图。
在 campaigns.centre_id 上建立索引以帮助搜索(满足 WHERE
子句)。我假设 campaigns.id 是 table.
在 emails.campaign_id 上建立索引以帮助加入来自活动的电子邮件。将 recipient_id 和 email_id 添加到该索引以提供覆盖索引。
现在,EXPLAIN
结果应按顺序显示 table,从营销活动开始,然后是电子邮件,然后是其他两个。 MySQL 仍需要一个内部临时 table 来应用 DISTINCT
。你确定你需要那个吗?
我假设 emails.id 和 recipients.id 是主键。