我有一个按日期划分的分区表,这是我的表定义:
CREATE TABLE `BBDD` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`MSISDN` int(11) DEFAULT NULL,
`Nombre` varchar(255) DEFAULT NULL,
`CIF_NIF` varchar(255) DEFAULT NULL,
`phone` int(11) DEFAULT NULL,
`PLANDEPRECIOS` varchar(255) DEFAULT NULL,
.
..
...
`Operador` varchar(150) DEFAULT NULL,
PRIMARY KEY (`id`,`fecha_carga`),
KEY `MSISDN` (`MSISDN`),
KEY `MSISDN_2` (`MSISDN`),
KEY `BBDD` (`BBDD`)
) ENGINE=InnoDB AUTO_INCREMENT=1607074 DEFAULT CHARSET=latin1
/*!50100 PARTITION BY RANGE ( to_days(fecha_carga))
(PARTITION p20120701 VALUES LESS THAN (735050) ENGINE = InnoDB,
PARTITION p20120801 VALUES LESS THAN (735081) ENGINE = InnoDB,
.
..
...
PARTITION p20181001 VALUES LESS THAN (737333) ENGINE = InnoDB,
PARTITION p20181101 VALUES LESS THAN (737364) ENGINE = InnoDB,
PARTITION p20181201 VALUES LESS THAN (737394) ENGINE = InnoDB,
PARTITION pdefault VALUES LESS THAN MAXVALUE ENGINE = InnoDB) */
行数大约是1330122,所以现在是时候测试我的分区和我的分区扫描的行数了,我们走了:
EXPLAIN PARTITIONS SELECT *
FROM adsl.BBDD
WHERE fecha_carga >=
cast(date_format(DATE_ADD(now(),INTERVAL -1 month),
'%Y-%m-01') as date )
and (MSISDN=622605810
or CIF_NIF=622605810
) ;
The analizer returns:
id: 1
select_type: SIMPLE
table: BBDD_adsl
partitions: p20120701,p20151001,p20151101,p20151201,p20160101,p20160201,p20160301,p20160401,p20160501,p20160601,p20160701,p20160801,p20160901,p20161001,p20161101,p20161201,p20170101,p20170201,p20170301,p20170401,p20170501,p20170601,p20170701,p20170801,p20170901,p20171001,p20171101,p20171201,p20180101,p20180201,p20180301,p20180401,p20180501,p20180601,p20180701,p20180801,p20180901,p20181001,p20181101,p20181201,pdefault
type: ALL
possible_keys: MSISDN,MSISDN_2
key: NULL
key_len: NULL
ref: NULL
rows: 1351342
Extra: Using where
1 row in set (0.08 sec)
正如您所看到的,它扫描正确的分区,但行数太高(1351342),就像我正在进行全扫描一样。
答案 0 :(得分:0)
唯一有用的索引是INDEX(fecha_carga)
,您没有。因此,它能做的最好的事情就是扫描一年的分区。这里有很多要点,请耐心等待......
OR
基本上无法优化MSISDN=622605810 or CIF_NIF=622605810
。 UNION
(见下文)可能会有所帮助,也许是显着的。
对于与日期相关的RANGE
分区,"首先"无论来自哪个值,都会扫描分区。这是一个允许NULL
或无效DATEs
进入的怪癖。(是的,它应该足够智能,以确保您的日期有效,但它不是。)你最好的防御是在开始时有一个虚拟分区 - 它应该足够老了'没有数据。这至少会使扫描更快。
没有" future"的分区。而是在需要时创建一个新的。我喜欢有一个名为future
的分区。然后,就在时钟滴答之前,我REORGANIZE PARTITION future INTO ...
创建下个月的分区和一个新的future
。由于future
为空,因此此操作基本上是即时的。 my partition blog。
CIF_NIF varchar(255)
和CIF_NIF=622605810
不能很好地协同工作。执行必须解析每个值并将其转换为数字以进行比较。这使得任何索引都无法使用。将字段更改为INT UNSIGNED
(或某些数字数据类型)或在数字周围加上引号(以便它是字符串比较)。
这里是OR
- > UNION
(有一些简化):
SELECT *
FROM adsl.BBDD
WHERE fecha_carga >= ...
and MSISDN=622605810
UNION DISTINCT -- or ALL
SELECT *
FROM adsl.BBDD
WHERE fecha_carga >= ...
and CIF_NIF='622605810'
此外,使用这些复合索引替换MSISDN
上的两个索引:
INDEX(CIF_NIF, fecha_carga), INDEX(MSISDN, fecha_carga)
通过这些更改(SELECT
的重新制定,加上更好的索引),无论有没有PARTITIONing
,它都会快得多。实际上,PARTITIONing
将提供零性能提升。
如果你正在"清洗"通过DROP PARTITION
获取旧数据,这是保留PARTITIONing
的一个很好的理由。 (再次,请参阅我的博客。)