使用分区的Mysql查询比没有分区花费更多时间

时间:2017-01-11 10:24:49

标签: mysql database-partitioning

我有一个300k行的表。该表非常重,因此它使每个查询变慢。在尝试了很多索引和其他优化之后,我决定在桌面上创建分区。

现在我有3个版本的表

  1. e_update
  2. e_update_partition(使用HASH进行20分区(在event_id上))
  3. e_update_partition_event(每个分区中有12个条目的12个分区(在event_id上))
  4. 现在我逐个在每个表上运行相同的查询并比较时间

    SELECT eu.event_id
    FROM e_update eu
    INNER JOIN event e ON e.id=eu.event_id
    WHERE eu.start_date > 2010-10-15
      AND e.published=1
      AND eu.event_id > 25000
      AND eu.event_id < 50000;
    

    时间 - 189911行,2次警告(14.43秒)

    SELECT eu.event_id
    FROM e_update_partition eu
    INNER JOIN event e ON e.id=eu.event_id
    WHERE eu.start_date > 2010-10-15
      AND e.published=1
      AND eu.event_id > 25000
      AND eu.event_id < 50000;
    

    时间 - 189911行,2次警告(15.87秒)

    解释结果 -

    +----+-------------+-------+-----------------------------------------------------------------------+-------+--------------------------------+-----------+---------+--------------------+--------+-----------------------+
    | id | select_type | table | partitions                                                            | type  | possible_keys                  | key       | key_len | ref                | rows   | Extra                 |
    +----+-------------+-------+-----------------------------------------------------------------------+-------+--------------------------------+-----------+---------+--------------------+--------+-----------------------+
    |  1 | SIMPLE      | e     | NULL                                                                  | range | PRIMARY,published              | published | 6       | NULL               | 120674 | Using index condition |
    |  1 | SIMPLE      | eu    | p0,p1,p2,p3,p4,p5,p6,p7,p8,p9,p10,p11,p12,p13,p14,p15,p16,p17,p18,p19 | ref   | event_id,start_date,event_id_2 | event_id  | 4       | biztradeshows.e.id |      1 | Using where           |
    +----+-------------+-------+-----------------------------------------------------------------------+-------+--------------------------------+-----------+---------+--------------------+--------+-----------------------+
    
    
    SELECT eu.event_id
    FROM e_update_partition_event eu
    INNER JOIN event e ON e.id=eu.event_id
    WHERE eu.start_date > 2010-10-15
      AND e.published=1
      AND eu.event_id > 25000
      AND eu.event_id < 50000;
    

    时间 - 189911行,2次警告(20.56秒)

    解释结果 -

    +----+-------------+-------+----------------------------------+--------+--------------------------------+-----------+---------+--------------------+--------+-----------------------+
    | id | select_type | table | partitions                       | type   | possible_keys                  | key       | key_len | ref                | rows   | Extra                 |
    +----+-------------+-------+----------------------------------+--------+--------------------------------+-----------+---------+--------------------+--------+-----------------------+
    |  1 | SIMPLE      | e     | NULL                             | range  | PRIMARY,published              | published | 6       | NULL               | 120674 | Using index condition |
    |  1 | SIMPLE      | eu    | p3,p4,p5,p6,p7,p8,p9,p10,p11,p12 | eq_ref | event_id,start_date,event_id_2 | event_id  | 4       | biztradeshows.e.id |      1 | Using where           |
    +----+-------------+-------+----------------------------------+--------+--------------------------------+-----------+---------+--------------------+--------+-----------------------+
    

    第三次查询的分区架构

    (PARTITION p1 VALUES LESS THAN (25000) ENGINE = InnoDB,
    PARTITION p2 VALUES LESS THAN (50000) ENGINE = InnoDB,
    PARTITION p3 VALUES LESS THAN (75000) ENGINE = InnoDB,
    PARTITION p4 VALUES LESS THAN (100000) ENGINE = InnoDB,
    PARTITION p5 VALUES LESS THAN (125000) ENGINE = InnoDB,
    PARTITION p6 VALUES LESS THAN (150000) ENGINE = InnoDB,
    PARTITION p7 VALUES LESS THAN (175000) ENGINE = InnoDB,
    PARTITION p8 VALUES LESS THAN (200000) ENGINE = InnoDB,
    PARTITION p9 VALUES LESS THAN (225000) ENGINE = InnoDB,
    PARTITION p10 VALUES LESS THAN (250000) ENGINE = InnoDB,
    PARTITION p11 VALUES LESS THAN (275000) ENGINE = InnoDB,
    PARTITION p12 VALUES LESS THAN (300000) ENGINE = InnoDB)
    

    为什么我的第三个查询比其他两个查询占用更多时间并使用几乎所有分区?

2 个答案:

答案 0 :(得分:3)

没有多少分区可以帮助您解决这个问题:

e.published=1 

布尔字段无法有效索引。为什么?因为他们只有两个值中的一个。这看起来像一个可变字段(你可以更新的字段,因为发布后可能会打开和关闭)。这样的字段也不能用于分区。

您的第一个选择是将此published字段与另一个字段合并,并创建一个复合索引,并希望它具有足够的基数作为有用的索引。

您的第二个选择是创建存档表并将未发布的项目移出存档表。

顺便说一句,你的查询有一个没有多大意义的条件:

 and eu.event_id >25000 and eu.event_id>50000;

这可以缩短为

 and eu.event_id > 50000;

更新

为什么要查询所有分区?那么你的第一个分区方案是hash partitioning

  

HASH分区主要用于确保均匀分布   预定数量的分区中的数据。

因此,您所有分区中的数据

第二种方案,如果你仔细观察,你会发现有两个分区没有被使用。那些是你的where子句遗漏的分区。

所以问题出在你的Where子句中: - )

答案 1 :(得分:0)

BY HASH无用

拥有event_id > ...BY HASH(event_id)是完全没用的组合。散列不知道哪个值将在哪个分区中,除了一个接一个。因此,它只是假设需要所有分区。

然后,它必须打开每个分区,执行查找,通常在那里找不到任何所需的值,然后转到下一个分区。因此PARITIIONing需要更多时间而不是没有。即使event_id上没有索引,非分区版本也可能稍快一些。使用INDEX(event_id),非分区版本可能会快得多。

我还没有找到BY HASH提供任何性能优势的任何用例。

未分区选项1

对于您提出的一个查询,我的第一个猜测是不分区,但我会

INDEX(start_date),
INDEX(event_id)

优化工具会查看其微薄的统计数据并在它们之间进行选择。

未分区选项2

再次,假设那个查询,我的第二个猜测是这个“覆盖”索引:

INDEX(start_date, event_id)

分区提示:对于小于一百万行的表,甚至不要考虑它。

More讨论。

2-D分区

由于两个“范围”,该查询本质上是一个二维问题。但要使分区有用,您必须使用BY RANGE,而不是BY HASH。所以,通过

进行分区
 BY RANGE(TO_DAYS(start_date))  together with
 PRIMARY KEY(event_id, ..., start_date)

 BY RANGE(event_id)  together with
 PRIMARY KEY(start_date, ..., event_id)

一定要使用InnoDB来利用它在PK上的聚类。 (上面我的链接讨论了移动时间作为分区键的一些问题。)