Mysql分区无法正常工作

时间:2016-02-05 01:45:05

标签: mysql database mariadb mysql-5.5

我有一个按日期划分的分区表,这是我的表定义:

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),就像我正在进行全扫描一样。

1 个答案:

答案 0 :(得分:0)

唯一有用的索引是INDEX(fecha_carga),您没有。因此,它能做的最好的事情就是扫描一年的分区。这里有很多要点,请耐心等待......

  • OR基本上无法优化MSISDN=622605810 or CIF_NIF=622605810UNION(见下文)可能会有所帮助,也许是显着的。

  • 对于与日期相关的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的一个很好的理由。 (再次,请参阅我的博客。)