【问题标题】:SQL - speed up querySQL - 加速查询
【发布时间】:2023-04-08 09:52:01
【问题描述】:

我目前使用以下查询,由于数据量大,大约需要 8 分钟才能返回结果(大约 14 个月)。请问有什么办法可以加快速度吗?

有问题的数据库是带有 InnoDb 引擎的 MySQL

select
    CUSTOMER as CUST,
    SUM(IF(PAGE_TYPE = 'C',PAGE_TYPE_COUNT,0)) AS TOTAL_C,
    SUM(IF(PAGE_TYPE = 'D',PAGE_TYPE_COUNT,0)) AS TOTAL_D
from
        PAGE_HITS
where
    EVE_DATE >= '2016-01-01' and EVE_DATE <= '2016-01-05'
    and SITE =  'P'
    and SITE_SERV like 'serv1X%'
group by
    CUST

数据按 6 个月划分。进入 where 子句的每一列都被索引。有相当多的索引 & 将是一个很大的列表要在这里列出。因此,只能用文字来概括。对于此查询,EVE_DATE + PAGE_TYPE_COUNT 是复合索引之一,CUST + SITE_SERV + EVE_DATEEVE_DATE + SITE_SERVEVE_DATE + SITE

主键实际上是一个虚拟的自增数字。它不习惯诚实。我无权访问解释计划。我会看看我能为此做些什么。

如果有任何帮助来改进这一点,我将不胜感激。

【问题讨论】:

  • 你能指定使用什么索引(如果有的话)以及结构是什么样的吗?正在使用主键等?
  • 能否提供更多细节:行数、索引、存储引擎等
  • 非常感谢。抱歉,我错过了更新这些详细信息。现在让我来做吧。
  • 使用现有查询作为内部选择并将SITE_SERV like 'serv1X%' 移动到外部查询。
  • 如果没有EXPLAINSELECT 查询,就很难(如果不是不可能的话)判断哪些部分可以改进。

标签: mysql sql query-optimization


【解决方案1】:

添加这两个索引:

INDEX(site, date)
INDEX(site, site_serv)

优化器将查看统计信息并在它们之间进行选择。粗略地说,如果在该范围内包含 'P' 和 DATE 的行数少于 'P' 和 'serv1X%' 的行数,第一个会更好。

是的,Thorsten 的“覆盖”索引可能会更好,但它的字段比我想放入索引的要多。

PARTITIONing可能帮助。但是没有太多的信息可以肯定地说。分区可能有帮助的原因是您有一个“二维”查找——一个日期范围和“serv1X%”。您需要在 date 或 site_serv 上进行分区,然后将 PRIMARY KEY(site, ..., ...) 与(date 或 site_serv)中的另一个作为第二列。其余列将需要同时包含分区键和某些列以使其唯一。这变得如此混乱,以至于我不想仔细考虑。

【讨论】:

    【解决方案2】:

    好的,由于表范围分区在 EVE_DATE,DBMS 应该很容易看到要读取哪个分区。所以关键是要使用什么索引。

    您检查一列是否相等 (SITE = 'P')。这应该在您的索引中排在第一位。然后,您可以按我猜的任何顺序添加EVE_DATESITE_SERV。因此,您的索引应该能够尽快找到有问题的表记录。

    但是,如果您将查询中使用的其他字段添加到索引中,则甚至不必读取该表,因为所有数据都可以在索引本身中使用:

    create index on page_hits(site, eve_date, site_serv, customer, page_type, page_type_count);
    

    如果我没记错的话,这应该是您查询的最佳索引。

    【讨论】:

    • 谢谢托尔斯滕。这在一定程度上提高了性能。
    【解决方案3】:

    我没有数据,所以我无法测试它的速度,但我认为它会更快。

    select
        CUSTOMER as CUST,
        SUM(PAGE_TYPE_COUNT * (PAGE_TYPE = 'C')) AS TOTAL_C,
        SUM(PAGE_TYPE_COUNT * (PAGE_TYPE = 'D')) AS TOTAL_D
    from
            PAGE_HITS
    where
        EVE_DATE >= '2016-01-01' and EVE_DATE <= '2016-01-05'
        and SITE =  'P'
        and SITE_SERV like 'serv1X%'
    group by
        CUST
    

    它在我的 MySql 5.6 上工作得很好

    【讨论】:

    • 不错的技巧,我一定会尝试这个来简化我的一些查询;性能方面,你碰巧有任何指标吗?
    • 谢谢你 Xpy。这看起来棒极了。我肯定会在其他地方使用它。在我的情况下,没有性能改进。这是一个真正的好一个虽然
    【解决方案4】:

    主要的优化因素是索引。应尽可能匹配您的查询,例如:

    EVE_DATE, SITE, CUST, SITE_SERV
    

    顺序很重要,至少对于 SITE_SERV 作为最后一个值;当您在其上使用 LIKE 时,您将不会使用完整值,这会降低下一列的索引效率。

    您还可以通过删除IF 并返回类型和计数来获得一点好处;也许您可以在前端应用程序中处理/格式化此值?

    无论如何,您应该从使用EXPLAIN 分析当前查询开始,看看出了什么问题。如果你不能,你可以尝试在本地数据库上复制结构、索引和一些虚拟数据,那里的卷是无关紧要的。

    【讨论】:

    • 谢谢普鲁克。我很高兴删除IF,我怎样才能以有效的方式计算条件SUM?你能帮忙吗?
    • 我想说只需选择PAGE_TYPE, SUM(PAGE_TYPE_COUNT) AS TOTAL 并在您的前端应用程序中管理“C”或“D”大小写;但正如我所说,这可能甚至不值得。更正了几个错别字,我的句子没有任何意义
    • 谢谢普鲁克。我将在我的开发的其他地方使用这个建议。关于这个问题,我需要在数据库层处理数据:(
    • 好吧,如果您不能跳过 IF,请保留它们 :) 检查您是否有包含 eve_date、site、cust 和 site_serv 的索引,如果您可以尝试将此架构放在您的数据库中可以运行EXPLAINon,这就是我所能推荐的。
    猜你喜欢
    • 2021-06-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-06-26
    • 1970-01-01
    • 2015-07-23
    • 2015-06-11
    • 1970-01-01
    相关资源
    最近更新 更多