一种相对有效的方法是使用数据库分区并通过删除分区来删除旧数据。它当然需要更复杂的维护,但它确实有效。
首先,启用 innodb_file_per_table 以便每个表(和分区)转到自己的文件而不是单个巨大的 ibdata 文件。
然后,创建一个分区表,每个时间范围(日、月、周,您选择它)有一个分区,这会为您的数据集生成一些合理大小的文件。
create table foo(
tid INT(7) UNSIGNED NOT NULL,
yearmonth INT(6) UNSIGNED NOT NULL,
data varbinary(255) NOT NULL,
PRIMARY KEY (tid, yearmonth)
) engine=InnoDB
PARTITION BY RANGE(yearmonth) (
PARTITION p201304 VALUES LESS THAN (201304),
PARTITION p201305 VALUES LESS THAN (201305),
PARTITION p201306 VALUES LESS THAN (201306)
);
查看数据库数据目录,您会发现每个分区都有一个文件。在此示例中,分区“p201304”将包含 yearmonth < 201304 的所有行,“p201305”将包含 2013-04 的行,“p201306”将包含 2013-05 的所有行。
在实践中,我实际上使用了一个包含 UNIX 时间戳的整数列作为分区键 - 这样随着时间的推移调整分区的大小会更容易。分区边缘不需要匹配任何日历边界,它们可以每 100000 秒发生一次,或者任何导致合理数量的分区(数十个分区)的结果,同时仍然具有足够小的数据文件。
然后,设置一个维护过程,为新数据创建新分区:ALTER TABLE foo ADD PARTITION (PARTITION p201307 VALUES LESS THAN (201307))
并删除旧分区:ALTER TABLE foo DROP PARTITION p201304
。删除大分区几乎与删除文件一样快,而且实际上会释放磁盘空间。此外,它不会通过在其中分散空白空间来分散其他分区。
如果可能,通过在 WHERE 子句中指定分区键(上例中的年月)或它的范围,确保您的频繁查询只访问一个或几个分区 - 这将使它们运行得更快作为数据库无需查看所有分区内部即可找到您的数据。