【问题标题】:How to improve my sql select with indexes?如何使用索引改进我的 sql 选择?
【发布时间】:2024-01-23 12:46:01
【问题描述】:

我需要有关我的 sql 选择的帮助。 MySQL 5.7 版本

这是我的桌子

create table if not exists OffersDayReport
(
    id int auto_increment
        primary key,
    aff_id int not null,
    aff_manager_id int not null,
    source text null,
    adv_id int not null,
    adv_manager_id int not null,
    offer_id int not null,
    offer_category_id int not null,
    country char(2) null,
    browser varchar(255) null,
    deviceType varchar(255) null,
    deviceOS varchar(255) null,
    preLander varchar(11) null,
    goal int null,
    offerPage int null,
    visits int not null,
    clicks int not null,
    conversions int not null,
    payoutInUSD decimal(10,2) not null,
    revenueInUSD decimal(10,2) not null,
    profitInUSD decimal(10,2) not null,
    brokenRevenueInUSD decimal(10,2) not null,
    time int not null,
    constraint unique_row
        unique (time, aff_id, offer_id, source, country, browser, deviceType, deviceOS, preLander, goal, offerPage)
)
charset=utf8;

这里是一个选择的例子:

SELECT OffersModel.lead                                                     as default_lead,
       OffersDayReport.offer_id                                             as report_title_offer_id,
       OffersModel.name                                                     as offers_name,
       sum(OffersDayReport.visits)                                          as report_title_visits,
       sum(OffersDayReport.clicks)                                          as report_title_clicks,
       sum(OffersDayReport.conversions)                                     as report_title_conversions,
       sum(OffersDayReport.payoutInUSD)                                     as report_title_payout,
       sum(OffersDayReport.revenueInUSD)                                    as report_title_revenue,
       sum(OffersDayReport.profitInUSD)                                     as report_title_profit,
       sum(OffersDayReport.conversions) / sum(OffersDayReport.clicks) * 100 as report_title_CR
FROM OffersDayReport
         LEFT OUTER JOIN Offers as OffersModel ON OffersModel.id = OffersDayReport.offer_id
WHERE OffersDayReport.aff_manager_id IN ({numbers})
  AND OffersDayReport.time >= {some start time}
  AND OffersDayReport.time <= {some end time}
GROUP BY OffersDayReport.offer_id;

范围可以是 1 天和 6 个月。 where 子句的可变性可能不同 - aff_manager_idadv_manager_id 或两者等等。

所有行的数量很大 - 大约 1000 亿。 现在我的选择大约需要 3-4 分钟,有时需要 10 分钟。 我尝试了不同类型的索引,但 mysql 分析器不喜欢使用它们。 即使我使用FORCE INDEX() - 我尝试过的所有类型的索引,我的选择也只是缓慢。

【问题讨论】:

  • 你看过范围分区吗?
  • @Strawberry 实际上不是。
  • 那么 aff_manager 的 IN 数字它包含多少个值?它们可以是查询的结果吗??
  • @scaisEdge 没有多少数字。最大值为 5。
  • 我已经发布了一个答案希望有用

标签: mysql sql database indexing innodb


【解决方案1】:

确保你有正确的复合索引

  table  OffersDayReport  columns (time, aff_manager_id )

对于 Offers 表,您可以使用冗余复合索引(从索引中检索所有值) 用于过滤值并避免访问表数据

  table Offers columns  ( id, name, lead)

最后你可以尝试用内部连接改变你的 IN 子句..

另一个可能的改进是在 INNER JOIN 中更改 IN 子句。这是因为一个 IN 子句与几个 OR 子句相同,而不是一个 INNER JOIN 只执行一次。 为此

如果 ({numbers}) 来自子查询,您可以尝试使用

  FROM OffersDayReport
  INNER JOIN  (
    select your_id
    from your_table
    .....
  ) t on t.your_id =  OffersDayReport.aff_manager
  LEFT OUTER JOIN Offers as OffersModel ON OffersModel.id = OffersDayReport.offer_id
  WHERE OffersDayReport.time >= {some start time}
  AND OffersDayReport.time <= {some end time}

否则,如果 ({numbers}) 未通过查询获得,您可以使用联合构建等效结果

  select  numbers1 numbers
  UNION
  select  numbers2 
  UNION 
  select  numbers3
  .....
  UNION 
  select  numbersN

对于查询

   FROM OffersDayReport
  INNER JOIN  (
    select  numbers1 my_number
    UNION
    select  numbers2 
    UNION 
    select  numbers3
    .....
    UNION 
    select  numbersN
  ) t on t.my_number =  OffersDayReport.aff_manager
  LEFT OUTER JOIN Offers as OffersModel ON OffersModel.id = OffersDayReport.offer_id
  WHERE OffersDayReport.time >= {some start time}
  AND OffersDayReport.time <= {some end time}

【讨论】:

  • 什么时候?什么时候?
【解决方案2】:

首先offers 与查询无关,所以我只是不考虑它(之后加入它应该是次要的。

您的查询本质上是:

SELECT odr.OffersDayReport.offer_id, 
       sum(odr.visits) as report_title_visits,
       . . .
FROM OffersDayReport odr
WHERE odr.aff_manager_id IN ({numbers}) AND
      odr.time >= {some start time} ODR
      odr.time <= {some end time}
GROUP BY odr.offer_id;

如果您可以重组数据库,我建议在aff_manager_id 上建立索引并按time 进行分区。在现有数据库上需要做很多工作。

相反,您可以尝试这种更复杂的形式:

SELECT odr.OffersDayReport.offer_id, 
       sum(odr.visits) as report_title_visits,
       . . .
FROM ((SELECT odr.*  -- list specific columns you want here
       FROM OffersDayReport odr
       WHERE odr.aff_manager_id = number1 AND
             odr.time >= {some start time} ODR
             odr.time <= {some end time}
      ) UNION ALL
      (SELECT odr.*  -- list specific columns you want here
       FROM OffersDayReport odr
       WHERE odr.aff_manager_id = number2 AND
             odr.time >= {some start time} ODR
             odr.time <= {some end time}
      ) UNION ALL
      . . .
   ) odr
GROUP BY odr.offer_id;

也就是说,每个比较管理器都被分解为一个单独的查询,结果是UNIONed 一起。

然后,确保您在(aff_manager_id, time) 上有一个索引。

每个子查询都应该使用索引——应该更快。然后将数据汇集在一起​​进行最终聚合。

您可以在外部FROM 子句中加入优惠元数据。

【讨论】:

    【解决方案3】:

    重新制定查询以避免爆炸内爆

    SELECT ...
        FROM ( SELECT stuff from OffersDayReport GROUP BY offers_id ) a
        LEFT JOIN the other table
    

    索引

    带有 aff_manager_id 或 adv_manager_id 或两者等等。

    INDEX(aff_manager_id, time)
    INDEX(adv_manager_id, time)
    

    范围部分 (time) 故意放在最后。我不担心“或两者兼而有之”;当您同时拥有两者时,优化器将使用其中哪一个“更好”;这可能会“足够好”。当只指定一个经理时,这些索引可能会更好地工作,但我认为 5.7 有足够好的方式在IN 的索引中跳转,因此UNION 技巧可能是不必要的。

    缩小足迹

    您真的需要 40 亿个可能的 INT 值(每个 4 个字节)吗?考虑更小的 INT 变体。

    如果source text 通常很大并且不适合规范化,请考虑压缩它(在客户端)并将列更改为BLOB。类似文本的内容通常压缩为 3:1。

    国家代码只能是ascii,不能是utf8。

    另一方面,clicksvisits 会溢出 INT SIGNED,限制在 20 亿左右。 (INT UNSIGNED 转到 4B。)

    标准化

    在 100B 行中会有几千个不同的“浏览器”。找出有多少,然后使用合适的 INT 并规范化该值。其他 VARCHAR(255) 值同上。 (小心“燃烧”的 id。)

    这是一个非常大的UNIQUE 索引。请解释一下。

    汇总表

    这是一个潜在的重大胜利。创建一个带有小计的表。它的PRIMARY KEY 将是(aff_manager_id, adv_manager_id, dy),其中dy 是time 的“日”。然后,每天早上,总结昨天的数据。 (为此,您可能需要INDEX(time)。)然后设计一个查询来对总和等进行求和,以从汇总表中快速获取所需的“报告”。它可能(应该)有几个额外的索引。此外,不需要我为主(“Fact”)表建议的两个索引。

    Summary Tables

    【讨论】: