为最近的事件组优化大型MySQL查询(73MM行)

时间:2017-08-17 18:49:56

标签: mysql indexing query-optimization query-performance

我试图为每个领导者抓住最近的活动'。我已创建索引,此查询仍将花费超过30分钟。

SELECT  l.id,
        l.home_number,
        l.mobile_number,
        CASE WHEN l.soldprice < 2 THEN 0 ELSE 1 END as sold,
        l.lead_date
FROM (
    SELECT  l.home_number, MAX(l.id) as id
    FROM lead l
    WHERE l.lead_date >= DATE_SUB(NOW(), INTERVAL 52 WEEK)
    AND l.state NOT IN ('NY','AR','VT','WV','GA','CT','DC','SD')
    GROUP BY l.home_number) a 
JOIN lead l ON l.id=a.id;

我的表索引如下:

Table Non_unique Key_name Seq_in_index Column_name Collation Cardinality Sub_part Packed Null Index_typ    
lead    0   PRIMARY     1   id          A   63123648    NULL    NULL        BTREE       
lead    1   id          1   id          A   63266540    NULL    NULL        BTREE       
lead    1   soldprice   1   soldprice   A   14715       NULL    NULL    YES BTREE       
lead    1   lead_date   1   lead_date   A   15351477    NULL    NULL    YES BTREE

我的表架构:

CREATE TABLE lead
( 
  id                BIGINT unsigned NOT NULL, 
  lead_date         DATETIME NULL,
  first_name        VARCHAR(50) NULL,
  last_name         VARCHAR(50) NULL,
  hashed_ssn        VARCHAR(34) NULL,
  city              VARCHAR(50) NULL,
  state             VARCHAR(2) NULL,
  home_number       VARCHAR(10) NULL,
  mobile_number     VARCHAR(10) NULL,
  email             VARCHAR(255) NULL,
  soldprice         DECIMAL(5,2) NULL,
  requested_amount  INT NULL,
  time_zone         VARCHAR(5),
  camp_id           VARCHAR(9),
  leadtype_id       VARCHAR(3),
  hittype_id        VARCHAR(3),
  PRIMARY KEY       (id)                           
);

任何建议都将不胜感激。

编辑:我使用的是MySQL 5.7.19-0ubuntu0.16.04.1

3 个答案:

答案 0 :(得分:1)

Tl; dr您需要一个复合(多列)索引。

专业提示:除非您知道需要,否则不要创建大量单列索引。它们很少在复杂查询中提供帮助,并且会减慢插入和更新的速度。

您已经很好地使用子查询来获取要获取的行的.row > div { border: 1px solid black; } .vertical-center .row { display: flex; align-items: center; } /*maybe create an id instead of h1*/ h1 { margin: 0; } 值。当然,大部分时间都进入你的子查询,这个:

id

调试子查询通常很聪明,然后将它们加入主查询中。

首先要做的是:在SELECT l.home_number, MAX(l.id) as id FROM lead l WHERE l.lead_date >= DATE_SUB(NOW(), INTERVAL 52 WEEK) AND l.state NOT IN ('NY','AR','VT','WV','GA','CT','DC','SD') GROUP BY l.home_number 上创建一个复合索引。然后运行这个简化的子查询,省略对状态的排除。这应该很快,因为它可以随机访问日期,然后使用索引来处理分组,并使用松散的索引扫描来获取最大id值。

(lead_date, home_number, id)

接下来,尝试在SELECT l.home_number, MAX(l.id) as id FROM lead l WHERE l.lead_date >= DATE_SUB(NOW(), INTERVAL 52 WEEK) GROUP BY l.home_number 上创建复合索引并尝试原始查询。如果速度相当快,你就完成了。您的查询会更快。删除第一个复合索引。

但它可能不是,因为MySQL在高容量的(lead_date, state, home_number, id)条款中并不是很好。

在这种情况下,保留第一个复合索引并删除第二个复合索引,并将状态排除移动到外部查询。

这将是这样的:

NOT IN

这应该有所帮助。

http://use-the-index-luke.com/是这类工作的一个很好的参考。

答案 1 :(得分:0)

由于您的子查询具有条件,这是一个棘手的优化查询。

作为一般规则,您可以使用索引来优化某些条件,但只能使用一个范围谓词或GROUP BY或ORDER BY。

但是你有两个范围谓词和一个GROUP BY:

  • l.lead_date >= DATE_SUB(NOW(), INTERVAL 52 WEEK)
  • l.state NOT IN ('NY','AR','VT','WV','GA','CT','DC','SD')
  • GROUP BY l.home_number

您可以使用lead_date上的索引来缩小行选择范围。您可以使用state上的索引来缩小行选择范围。或者,您可以使用索引来帮助按组顺序读取查询,并尝试避免使用临时表。 但在给定查询中,您只能拥有这三种优化中的一种

然后诀窍就是选择你要优先考虑哪一个。它归结为每个人在给定数据分布的情况下改进查询的程度。这取决于您的数据,这不是我们可以回答的问题。因此,您必须使用EXPLAIN测试所有三种情况,或者只运行带有分析的查询以查看它有多大帮助。

通常,使用范围谓词缩小到最小的行子集。然后,即使其他范围谓词和GROUP BY必须在没有索引帮助的情况下工作,它们只需要处理较小的行集,因此总成本不是那么糟糕(希望如此)。

答案 2 :(得分:0)

我正在走出困境并对数据做出一些假设。

SELECT  l.id, l.home_number, l.mobile_number,
        (l.soldprice < 2) as sold,
        l.lead_date
    FROM  
    (
        SELECT  l.home_number, MAX(l.id) as maxid
            FROM  lead l
            GROUP BY  l.home_number
    ) a
    JOIN  lead l  ON l.id = a.maxid;
            WHERE  l.lead_date >= DATE_SUB(NOW(), INTERVAL 52 WEEK)
              AND  l.state NOT IN ('NY','AR','VT', 'WV','GA','CT','DC', 'SD' )

并且

INDEX(home_number, id)

假设:

  • 由于索引,子查询将非常快。
  • 优化程序将在查看WHERE之前运行子查询。 (如果失败,请将其更改为HAVING。)
  • MAX(id)和“去年”非常相关
  • 每个home_number都特定于特定的state

让我们知道这是否会得到相同的结果,但要快得多。