【问题标题】:Mysql query with partition taking more time than without partitioning带分区的 MySQL 查询比不带分区的查询花费更多时间
【发布时间】:2017-01-11 10:24:48
【问题描述】:

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

现在我有 3 个版本的表格

  1. e_update
  2. e_update_partition(20 个分区使用 HASH(on event_id))
  3. e_update_partition_event(12 个分区,每个分区有 25K 个条目(在 event_id 上))

现在我在每个表上一个一个地运行相同的查询并比较时间

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 秒)

解释结果-

+----+-------------+--------+---------- -------------------------------------------------+ --------+--------------------------------+--------- --+---------+--------+--------+------ ----------------+ |编号 |选择类型 |表|隔断 |类型 |可能的键 |关键 | key_len |参考 |行 |额外 | +----+-------------+--------+---------- -------------------------------------------------+ --------+--------------------------------+--------- --+---------+--------+--------+------ ----------------+ | 1 |简单 |电子|空 |范围 |初级,出版 |发表 | 6 |空 | 120674 |使用索引条件 | | 1 |简单 |欧盟 | p0,p1,p2,p3,p4,p5,p6,p7,p8,p9,p10,p11,p12,p13,p14,p15,p16,p17,p18,p19 |参考 | event_id,开始日期,event_id_2 | event_id | 4 | biztradeshows.e.id | 1 |使用位置 | +----+-------------+--------+---------- -------------------------------------------------+ --------+--------------------------------+--------- --+---------+--------+--------+------ ----------------+
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 秒)

解释结果-

+----+-------------+--------+---------- ------------+--------+---------------- ----+-----------+---------+--------------------+-- ------+------------------------+ |编号 |选择类型 |表|隔断 |类型 |可能的键 |关键 | key_len |参考 |行 |额外 | +----+-------------+--------+---------- ------------+--------+---------------- ----+-----------+---------+--------------------+-- ------+------------------------+ | 1 |简单 |电子|空 |范围 |初级,出版 |发表 | 6 |空 | 120674 |使用索引条件 | | 1 |简单 |欧盟 | p3,p4,p5,p6,p7,p8,p9,p10,p11,p12 | eq_ref | event_id,开始日期,event_id_2 | event_id | 4 | biztradeshows.e.id | 1 |使用位置 | +----+-------------+--------+---------- ------------+--------+---------------- ----+-----------+---------+--------------------+-- ------+------------------------+

第三次查询的分区架构

(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)

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

【问题讨论】:

    标签: mysql database-partitioning


    【解决方案1】:

    再多的分区也无济于事:

    e.published=1 
    

    布尔字段无法有效索引。为什么?因为它们只有两个值之一。这看起来像一个可变字段(您可以对其进行更新,因为已发布可能会被打开和关闭)。这样的字段也不能用于分区。

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

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

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

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

    这可以缩短为

     and eu.event_id > 50000;
    

    更新

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

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

    所以您的数据在所有分区中

    第二种方案,仔细看会发现有两个分区没有被使用。这些是您的 where 子句遗漏的分区。

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

    【讨论】:

    • 我尝试删除 e.published=1(timing - 15.97 sec, 17.59 sec, 6.34 sec) 但效果不佳。为什么我的第三个查询使用了几乎所有的分区?
    • 不能说,因为你还没有展示你的分区方案是什么。无论您是否使用 published=1 都不会对查询产生任何影响,这就是我的答案。如果你没有它,你将加入整个桌子。如果你有它,你可能会加入一半的表,但必须扫描整个表,因为已发布的无法正确索引
    【解决方案2】:

    BY HASH 没用

    拥有event_id &gt; ...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讨论。

    二维分区

    该查询本质上是一个二维问题,因为有两个“范围”。但要使分区有用,您必须使用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 上的集群。 (上面我的链接讨论了将移动时间作为分区键的一些问题。)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-10-20
      • 2018-01-08
      • 2012-09-03
      • 2012-06-04
      相关资源
      最近更新 更多