如何按日期时间列对表进行分区?

时间:2011-05-23 06:27:28

标签: mysql datetime hash database-partitioning

我想按datetime列对mysql表进行分区。有一天分区。创建表脚本是这样的:

CREATE TABLE raw_log_2011_4 (
  id bigint(20) NOT NULL AUTO_INCREMENT,
  logid char(16) NOT NULL,
  tid char(16) NOT NULL,
  reporterip char(46) DEFAULT NULL,
  ftime datetime DEFAULT NULL,
  KEY id (id)
) ENGINE=InnoDB AUTO_INCREMENT=286802795 DEFAULT CHARSET=utf8
PARTITION BY hash (day(ftime)) partitions 31;

但是当我选择某天的数据时,它找不到分区。选择语句是这样的:

explain partitions select * from raw_log_2011_4 where day(ftime) = 30;

当我使用另一个语句时,它可以找到分区,但我没有选择某天的数据。

explain partitions select * from raw_log_2011_4 where ftime = '2011-03-30';

有没有人告诉我如何选择某天的数据并利用分区。谢谢!

4 个答案:

答案 0 :(得分:18)

HASH的分区对于datetime列来说是一个非常糟糕的主意,因为它不能使用partition pruning。来自MySQL文档:

  

修剪只能用于分区的整数列   HASH或KEY。例如,表t4上的此查询无法使用修剪   因为dob是DATE列:

SELECT * FROM t4 WHERE dob >= '2001-04-14' AND dob <= '2005-10-15';
  

但是,如果表将年份值存储在INT列中,则a   具有WHERE year_col&gt; = 2001 AND year_col&lt; = 2005的查询可以   修剪。

因此,您可以将TO_DAYS(DATE())的值存储在额外的INTEGER列中以使用修剪。

另一种选择是使用RANGE分区:

CREATE TABLE raw_log_2011_4 (
  id bigint(20) NOT NULL AUTO_INCREMENT,
  logid char(16) NOT NULL,
  tid char(16) NOT NULL,
  reporterip char(46) DEFAULT NULL,
  ftime datetime DEFAULT NULL,
  KEY id (id)
) ENGINE=InnoDB AUTO_INCREMENT=286802795 DEFAULT CHARSET=utf8
  PARTITION BY RANGE( TO_DAYS(ftime) ) (
    PARTITION p20110401 VALUES LESS THAN (TO_DAYS('2011-04-02')),
    PARTITION p20110402 VALUES LESS THAN (TO_DAYS('2011-04-03')),
    PARTITION p20110403 VALUES LESS THAN (TO_DAYS('2011-04-04')),
    PARTITION p20110404 VALUES LESS THAN (TO_DAYS('2011-04-05')),
    ...
    PARTITION p20110426 VALUES LESS THAN (TO_DAYS('2011-04-27')),
    PARTITION p20110427 VALUES LESS THAN (TO_DAYS('2011-04-28')),
    PARTITION p20110428 VALUES LESS THAN (TO_DAYS('2011-04-29')),
    PARTITION p20110429 VALUES LESS THAN (TO_DAYS('2011-04-30')),
    PARTITION future VALUES LESS THAN MAXVALUE
  );

现在,以下查询将仅使用分区p20110403:

SELECT * FROM raw_log_2011_4 WHERE ftime = '2011-04-03';

答案 1 :(得分:9)

您好,您正在对表定义所希望的表的定义执行错误的分区:

CREATE TABLE raw_log_2011_4 (
  id bigint(20) NOT NULL AUTO_INCREMENT,
  logid char(16) NOT NULL,
  tid char(16) NOT NULL,
  reporterip char(46) DEFAULT NULL,
  ftime datetime DEFAULT NULL,
  KEY id (id)
) ENGINE=InnoDB AUTO_INCREMENT=286802795 DEFAULT CHARSET=utf8
PARTITION BY hash (TO_DAYS(ftime)) partitions 31;

你的选择命令是:

explain partitions 
    select * from raw_log_2011_4 where TO_DAYS(ftime) = '2011-03-30';

上述命令将选择所需的所有日期,就像使用TO_DAYS命令

一样
mysql> SELECT TO_DAYS(950501);
        -> 728779
mysql> SELECT TO_DAYS('2007-10-07');
        -> 733321

为什么要使用TO_DAYS AS MySQL优化器将识别两个基于日期的函数以进行分区修剪: 1.TO_DAYS() 2.YEAR()

这可以解决你的问题..

答案 2 :(得分:1)

我刚刚在http://dev.mysql.com/tech-resources/articles/mysql_55_partitioning.html阅读了与此相关的MySQL博客文章。

早于5.1的版本需要特殊体操才能根据日期进行分区。上面的链接讨论了它并显示了示例。

版本5.5及更高版本允许您使用非数字值(如日期和字符串)进行直接分区。

答案 3 :(得分:0)

请勿使用CHAR,请使用VARCHAR。这将节省大量空间,从而减少I / O,从而加快查询速度。

reporterip:(46)对于IP地址来说是不必要的,甚至是IPv6。有关进一步的讨论,请参阅My blog,包括如何将其缩小为16个字节。

@Steyx建议

PARTITION BY RANGE(TO_DAYS(...)),但不要超过50个分区。尽管有&#34;修剪&#34;你拥有的分区越多,查询就越慢。 HASH分区基本没用。

More discussion of partitioning, especially the type you are looking at。这包括一段时间内滑动分区的代码。