【问题标题】:MySQL Partition pruning on variableMySQL分区修剪变量
【发布时间】:2012-10-01 20:08:47
【问题描述】:

我正在尝试优化运行大约需要 30 分钟的查询。我想要做的是利用分区修剪来最小化搜索的行。对表进行分区的变量范围是来自不同表的变量。好像mysql正在搜索所有分区。

这里是表格(删掉不相关的部分): (顺便说一句,我确实意识到 int(x) 并没有改变 int 的大小。我在知道更好之前就设计了那个表,但还没有修复它)

expectedEvent | CREATE TABLE `expectedevent` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`eventId` int(5) NOT NULL,
`unitGroup_id` int(6) NOT NULL,
`minOccur` int(9) NOT NULL,
`periodInDays` int(4) NOT NULL,
PRIMARY KEY (`id`),
KEY `eventId` (`eventId`),
KEY `unitGroup_id` (`unitGroup_id`),
CONSTRAINT `expectedevent_ibfk_1` FOREIGN KEY (`unitGroup_id`) REFERENCES `unitgroup` (`id`)

event_message | CREATE TABLE `event_message` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`unitId` varchar(15) NOT NULL,
`eventId` smallint(6) NOT NULL,
`eventName` varchar(50) NOT NULL,
`gpsDateTime` datetime NOT NULL,
`weekInfo` tinyint(4) NOT NULL,
`odometer` int(11) NOT NULL,
...
KEY `id` (`id`),
KEY `unitId` (`unitId`,`eventId`),
KEY `eventId` (`eventId`)
...
!50100 PARTITION BY RANGE (weekInfo)
ARTITION p0 VALUES LESS THAN (1) ENGINE = InnoDB,
ARTITION p1 VALUES LESS THAN (2) ENGINE = InnoDB,

unitGroup | CREATE TABLE `unitgroup` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(60) DEFAULT NULL,

unitGroup_devices | CREATE TABLE `unitg
`id` int(11) NOT NULL AUTO_INCREMENT,
`unitGroup_id` int(11) NOT NULL,
`scopeDevice_id` int(11) NOT NULL,
PRIMARY KEY (`id`),
KEY `unitGroup_id` (`unitGroup_id`),
KEY `scopeDevice_id` (`scopeDevice_id`)
...

这个查询大约需要 30 分钟(选择,没有解释):

explain partitions
select ee.eventId, scopeDevice_id as scopeDevId, sd.unitId as unitId, count(em.id) as evtCount, minOccur, periodInDays
from expectedEvent ee left join unitGroup ug on ee.unitGroup_id=ug.id
left join unitGroup_devices ugd on ug.id=ugd.unitGroup_id
left join scopeDevice sd on ugd.scopeDevice_id=sd.id
left join event_message em on sd.unitId=em.unitId and em.eventId=ee.eventId
where gpsDateTime>=DATE_SUB(DATE(now()),INTERVAL periodInDays DAY)
and weekInfo>=WEEKOFYEAR(DATE_SUB(DATE(now()),INTERVAL periodInDays DAY))
and weekInfo <=WEEKOFYEAR(DATE(now()))
group by ee.id, ugd.scopeDevice_id;

| id | select_type | table | partitions                                                                                                                                                | type   | possible_keys               | key          | key_len | ref                                            | rows | Extra                           |
+----+-------------+-------+-----------------------------------------------------------------------------------------------------------------------------------------------------------+--------+-----------------------------+--------------+---------+------------------------------------------------+------+---------------------------------+
|  1 | SIMPLE      | ee    | NULL                                                                                                                                                      | ALL    | eventId,unitGroup_id        | NULL         | NULL    | NULL                                           |    1 | Using temporary; Using filesort |
|  1 | SIMPLE      | ug    | NULL                                                                                                                                                      | eq_ref | PRIMARY                     | PRIMARY      | 4       | navsat_scope.ee.unitGroup_id                   |    1 | Using where; Using index        |
|  1 | SIMPLE      | ugd   | NULL                                                                                                                                                      | ref    | unitGroup_id,scopeDevice_id | unitGroup_id | 4       | navsat_scope.ee.unitGroup_id                   |   11 | Using where                     |
|  1 | SIMPLE      | sd    | NULL                                                                                                                                                      | eq_ref | PRIMARY,unitId              | PRIMARY      | 4       | navsat_scope.ugd.scopeDevice_id                |    1 | Using where                     |
|  1 | SIMPLE      | em    | p0,p1,p2,p3,p4,p5,p6,p7,p8,p9,p10,p11,p12,p13,p14,p15,p16,p17,p18,p19,p20,p21,p22,p23,p24,p25,p26,p27,p28,p29,p30,p31,p32,p33,p34,p35,p36,p37,p38,p39,p40 | ref    | unitId,eventId              | unitId       | 19      | navsat_scope.sd.unitId,navsat_scope.ee.eventId |  682 | Using where                     |
+----+-------------+-------+-----------------------------------------------------------------------------------------------------------------------------------------------------------+--------+-----------------------------+--------------+---------+------------------------------------------------+------+---------------------------------+

expectedEvent中只有一个入口,所以基本上相当于做了下面的事情。 这个查询大约需要 3 分钟(选择,没有解释):

select ee.eventId, scopeDevice_id as scopeDevId, sd.unitId as unitId, count(em.id) as evtCount, minOccur, periodInDays
from expectedEvent ee left join unitGroup ug on ee.unitGroup_id=ug.id
left join unitGroup_devices ugd on ug.id=ugd.unitGroup_id
left join scopeDevice sd on ugd.scopeDevice_id=sd.id
left join event_message em on sd.unitId=em.unitId and em.eventId=ee.eventId
where gpsDateTime>="2012-09-29"
and weekInfo>=WEEKOFYEAR("2012-09-29")
and weekInfo <=WEEKOFYEAR("2012-10-01")
group by ee.id, ugd.scopeDevice_id;

| id | select_type | table | partitions | type   | possible_keys               | key          | key_len | ref                                            | rows | Extra                           |
+----+-------------+-------+------------+--------+-----------------------------+--------------+---------+------------------------------------------------+------+---------------------------------+
|  1 | SIMPLE      | ee    | NULL       | ALL    | eventId,unitGroup_id        | NULL         | NULL    | NULL                                           |    1 | Using temporary; Using filesort |
|  1 | SIMPLE      | ug    | NULL       | eq_ref | PRIMARY                     | PRIMARY      | 4       | navsat_scope.ee.unitGroup_id                   |    1 | Using where; Using index        |
|  1 | SIMPLE      | ugd   | NULL       | ref    | unitGroup_id,scopeDevice_id | unitGroup_id | 4       | navsat_scope.ee.unitGroup_id                   |   11 | Using where                     |
|  1 | SIMPLE      | sd    | NULL       | eq_ref | PRIMARY,unitId              | PRIMARY      | 4       | navsat_scope.ugd.scopeDevice_id                |    1 | Using where                     |
|  1 | SIMPLE      | em    | p39,p40    | ref    | unitId,eventId              | unitId       | 19      | navsat_scope.sd.unitId,navsat_scope.ee.eventId |  682 | Using where                     |
+----+-------------+-------+------------+--------+-----------------------------+--------------+---------+------------------------------------------------+------+---------------------------------+

我的解决方法是先在我的 C# 应用程序中读取 expectedEvent 表,然后使用实际日期而不是变量构建查询。

然而,我更愿意在 MySQL 中完成这一切。如何优化查询? ExpectedEvent 最终会包含很多行。

谢谢!

【问题讨论】:

  • “分区修剪”在准备好SQL语句时进行;基本上,您需要在解析时已知的值;这些值不能是在执行之前未知的值。执行SQL语句时,MySQL首先准备一个执行计划,然后执行该计划。
  • Spencer,如果您将其从评论移至答案,我会将其标记为答案。感谢您的见解!!理解这一点时更有意义!
  • 看起来你还欠@spencer 一个闪亮的绿色复选标记。 :)

标签: mysql variables database-partitioning pruning


【解决方案1】:

MySQL 在准备好 SQL 语句时进行“分区修剪”(当 MySQL 为该语句生成执行计划时)。为了将“分区修剪”作为计划的一部分,MySQL 需要在解析时已知的值;对于解析时未知的值,不实施分区修剪。

当 MySQL 执行一条 SQL 语句时,MySQL 首先准备一个执行计划,然后执行该计划。 “分区修剪”是在准备阶段决定的执行计划的细节。 (这解释了为什么您的谓词中带有常量的语句显示分区修剪,但您的引用列的语句没有显示任何修剪。)

【讨论】:

    【解决方案2】:

    您需要在 WHERE 子句中使用实际值,而不是通过 JOIN 获取值。想一想,当 MySQL 为查询准备执行计划时,它不知道表中的值对于 gpsDateTime 是什么。因此它无法知道它只需要查询中的某些分区来获取它需要的数据。

    在您的情况下,事先通过查询获取过滤器中使用的日期值,然后在进行选择查询时使用实际值会快得多,就像您在第二个示例中所做的那样。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-03-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-04-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多