我有一个300k行的表。该表非常重,因此它使每个查询变慢。在尝试了很多索引和其他优化之后,我决定在桌面上创建分区。
现在我有3个版本的表
现在我逐个在每个表上运行相同的查询并比较时间
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)
为什么我的第三个查询比其他两个查询占用更多时间并使用几乎所有分区?
答案 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上的聚类。 (上面我的链接讨论了移动时间作为分区键的一些问题。)