MySQL 表按月分区

Posted

技术标签:

【中文标题】MySQL 表按月分区【英文标题】:MySQL table partition by month 【发布时间】:2013-10-21 17:03:50 【问题描述】:

我有一个巨大的表,其中存储了许多跟踪的事件,例如用户点击。

这张桌子已经有几十万,而且每天都在变大。 当我尝试从大的时间范围内获取事件时,查询开始变慢,并且在阅读了相当多的主题后,我了解到对表进行分区可能会提高性能。

我想做的是按月对表进行分区。

我只找到了显示如何每月手动分区的指南,有没有办法告诉 mysql 按月分区,它会自动执行?

如果不是,考虑到我的按列分区是日期时间,手动执行的命令是什么?

【问题讨论】:

【参考方案1】:

如手册所述:http://dev.mysql.com/doc/refman/5.6/en/partitioning-overview.html

这很容易通过月份输出的哈希分区来实现。

CREATE TABLE ti (id INT, amount DECIMAL(7,2), tr_date DATE)
    ENGINE=INNODB
    PARTITION BY HASH( MONTH(tr_date) )
    PARTITIONS 6;

请注意,这只是按月分区而不是按年分区,在这个例子中也只有 6 个分区(所以 6 个月)。

对于现有表的分区(手动:https://dev.mysql.com/doc/refman/5.7/en/alter-table-partition-operations.html):

ALTER TABLE ti
    PARTITION BY HASH( MONTH(tr_date) )
    PARTITIONS 6;

查询可以从整个表中完成:

SELECT * from ti;

或来自特定分区:

SELECT * from ti PARTITION (HASH(MONTH(some_date)));

【讨论】:

你的意思是我分区一整年,下一年会转到同一个分区? 确实,使用这种特殊的散列方法。或者你当然可以做YEAR(tr_date) * 12 + MONTH(tr_date) 我更喜欢使用PARTITION BY HASH((YEAR(TIMESTAMP) * 100) + MONTH(TIMESTAMP)),它提供了格式良好的日期(201511、201512、201601 等)——尽管与 concat 函数相同 您能否解释一下为什么您只有 6 个分区而不是 12 个 - 这是否意味着每个分区将存储 2 个月的数据? 这个查询语法不正确:SELECT * from ti PARTITION (HASH(MONTH(some_date)));尝试运行它。请告知查询分区表的正确语法如下:PARTITION BY HASH(MONTH(tr_date))【参考方案2】:

使用访问时间与表大小无关的 TokuDb。

【讨论】:

【参考方案3】:

HASHing by month with 6 partitions 意味着一年有两个月将落在同一个分区中。这有什么好处?

不要打扰分区,索引表。

假设您只使用以下两个查询:

SELECT * from ti;
SELECT * from ti PARTITION (HASH(MONTH(some_date)));

然后以the_date 开头PRIMARY KEY

第一个查询只是读取整个表;分区和不分区之间没有变化。

第二个查询,假设您想要一个月份,而不是映射到同一分区的所有月份,则需要

SELECT * FROM ti  WHERE the_date >= '2019-03-01'
                    AND the_date  < '2019-03-01' + INTERVAL 1 MONTH;

如果您还有其他疑问,我们来看看。

(我没有找到任何使用 PARTITION BY HASH 的性能理由。)

【讨论】:

嗯,我觉得不错,但我知道什么?【参考方案4】:
CREATE TABLE `mytable` (
  `post_id` int DEFAULT NULL,
  `viewid` int DEFAULT NULL,
  `user_id` int DEFAULT NULL,
  `post_Date` datetime DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
PARTITION BY RANGE (extract(year_month from `post_Date`))
(PARTITION P0 VALUES LESS THAN (202012) ENGINE = InnoDB,
 PARTITION P1 VALUES LESS THAN (202104) ENGINE = InnoDB,
 PARTITION P2 VALUES LESS THAN (202108) ENGINE = InnoDB,
 PARTITION P3 VALUES LESS THAN (202112) ENGINE = InnoDB,
 PARTITION P4 VALUES LESS THAN MAXVALUE ENGINE = InnoDB)

【讨论】:

添加上下文以防止投票失败。审查结束。 优化器是否足够智能以处理extract(year_month...)? (PARTITION 语句中可以有效使用的表达式数量非常有限。)【参考方案5】:

注意通过哈希进行分区的“懒惰”效应:

正如文档所说:

您还应该记住,每次插入或更新(或可能删除)行时都会计算此表达式;这意味着非常复杂的表达式可能会导致性能问题,尤其是在执行一次影响大量行的操作(例如批量插入)时。

最有效的散列函数是对单个表列进行操作并且其值随列值一致增加或减少的散列函数,因为这允许对分区范围进行“修剪”。也就是说,表达式与它所基于的列的值变化越紧密,MySQL 可以更有效地使用该表达式进行哈希分区。

例如,如果 date_col 是 DATE 类型的列,则表达式 TO_DAYS(date_col) 直接随 date_col 的值而变化,因为对于 date_col 的值的每一次变化,表达方式变化一致。表达式YEAR(date_col) 相对于date_col 的方差并不像TO_DAYS(date_col) 那样直接,因为并非date_col 中的所有可能变化都会在YEAR(date_col) 中产生等效变化。

【讨论】:

但是按日期分区会创建太多的数据文件。从长远来看,这不应该是一个问题吗?

以上是关于MySQL 表按月分区的主要内容,如果未能解决你的问题,请参考以下文章

Oracle普通表按月转分区表,通过PLSQL包一键生成分区表

Oracle普通表按月转分区表,通过PLSQL包一键生成分区表

php mysql 数据按月统计 没有的月份怎样补0

mysql 如何按月分组查询出当前年度每个月的短信数量(数据库中这个月要是为空的话就用0条怎么显示出来)

Mysql PARTITION BY分区记录

mycat:水平分库之按月分库