【问题标题】:Can i set up Mysql to auto-partition?我可以将 Mysql 设置为自动分区吗?
【发布时间】:2015-07-20 17:48:52
【问题描述】:

我想对一个非常大的表进行分区。随着业务的增长,按日期进行分区并不是那么好,因为分区每年都变得越来越大。我真正想要的是每 1000 万条记录的分区。

Mysql 手册显示了这个简单的例子:

CREATE TABLE employees (
id INT NOT NULL,
fname VARCHAR(30),
lname VARCHAR(30),
hired DATE NOT NULL DEFAULT '1970-01-01',
separated DATE NOT NULL DEFAULT '9999-12-31',
job_code INT NOT NULL,
store_id INT NOT NULL
)
PARTITION BY RANGE (store_id) (
PARTITION p0 VALUES LESS THAN (6),
PARTITION p1 VALUES LESS THAN (11),
PARTITION p2 VALUES LESS THAN (16),
PARTITION p3 VALUES LESS THAN MAXVALUE
);

但这意味着大于 16 且小于 MAXVALUE 的所有内容都会被扔到最后一个分区中。有没有办法在每个间隔(在我的例子中,1000 万条记录)自动生成一个新分区,这样我就不必继续修改活动数据库了?我正在运行 Mysql 5.5

谢谢!

编辑:这是我的实际表格

CREATE TABLE `my_table` (
`row_id` int(11) NOT NULL AUTO_INCREMENT,
`filename` varchar(50) DEFAULT NULL,
`timestamp` datetime DEFAULT NULL,
`unit_num` int(3) DEFAULT NULL,
`string` int(3) DEFAULT NULL,
`voltage` float(6,4) DEFAULT NULL,
`impedance` float(6,4) DEFAULT NULL,
`amb` float(6,2) DEFAULT NULL,
`ripple_v` float(8,6) DEFAULT NULL,
 PRIMARY KEY (`row_id`),
 UNIQUE KEY `timestamp` (`timestamp`,`filename`,`string`,`unit_num`),
 KEY `index1` (`filename`),
 KEY `index2` (`timestamp`),
 KEY `index3` (`timestamp`,`filename`,`string`),
 KEY `index4` (`filename`,`unit_num`)
 ) ENGINE=MyISAM AUTO_INCREMENT=690892041 DEFAULT CHARSET=latin1

图表的示例查询是...

SELECT DATE_FORMAT(timestamp,'%Y/%m/%d %H:%i:%s') as mytime,voltage,impedance,amb,ripple_v,unit_num 
FROM my_table WHERE timestamp >= DATE_SUB('2015-07-31 00:05:59', INTERVAL 90 DAY) 
AND filename = 'dlrphx10s320upsab3' and unit_num='5' and string='2'ORDER BY timestamp asc;

这里是查询的解释...

mysql> explain SELECT DATE_FORMAT(timestamp,'%Y/%m/%d %H:%i:%s') as mytime,voltage,impedance,amb,ripple_v,unit_num FROM my_table WHERE timestamp >= DATE_SUB('2015-07-31 00:05:59', INTERVAL 90 DAY) AND filename = 'dlrphx10s320upsab3' and unit_num='5' and string='2'ORDER BY timestamp asc;
+----+-------------+------------+------+-------------------------+--------+---------+-------------+-------+----------------------------------------------------+
| id | select_type | table      | type | possible_keys           | key    | key_len | ref         | rows  | Extra                                              |
+----+-------------+------------+------+-------------------------+--------+---------+-------------+-------+----------------------------------------------------+
|  1 | SIMPLE      | unit_tarma | ref  | timestamp,index3,index4 | index4 | 58      | const,const | 13440 | Using index condition; Using where; Using filesort | 
+----+-------------+------------+------+-------------------------+--------+---------+-------------+-------+----------------------------------------------------+

【问题讨论】:

  • 您的姓名字段被硬封顶为 30 个字符?时间到re-evaluate your assumptions。另外据我所知,添加 PARTITION 需要更改架构,但其他人应该确认这一点。
  • 您可以创建存储过程来更改您的表以根据您的要求创建新分区。创建 proc 后,您可以使用 mysql 事件调度程序,它在特定时间间隔后执行该过程。这样就可以实现动态自动分区了。
  • 哈哈大笑。我说的是Mysql提供的例子。 :)

标签: mysql partitioning


【解决方案1】:

(这个答案是针对模式和SELECT的。)

由于您预计会有数百万行,所以首先我想指出对架构的一些改进。

  • FLOAT(m,n) 通常是“错误”的做法,因为它会导致两次舍入。要么使用普通的FLOAT(这对于电压等指标来说似乎是“正确的”)或使用DECIMAL(m,n)FLOAT 是 4 个字节;在给定的情况下,DECIMAL 将是 3 或 4 个字节。

  • 当你同时拥有INDEX(a)INDEX(a,b) 时,前者是不必要的,因为后者可以覆盖。您有 3 个不必要的 KEY。这会减慢INSERTs

  • INT(3) -- 你说的是“三位数字”吗?如果是这样,请考虑使用 1 个字节的 TINYINT UNSIGNED(值 0..255)而不是 4 个字节的 INT。这将节省许多 MB 的磁盘空间,从而提高速度。 (另见SMALLINT等,以及SIGNEDUNSIGNED。)

  • 如果filename 重复很多次,您可能需要“规范化”它。这样可以节省很多 MB。

  • 使用NOT NULL,除非您需要NULL

  • AUTO_INCREMENT=690892041 表示您与id 的灾难距离约为 1/3,最高将达到约 20 亿。你用id 做任何事吗?摆脱柱子可以避免这个问题;并将UNIQUE KEY 更改为PRIMARY KEY。 (如果您确实需要id,我们再谈。)

  • ENGINE=MyISAM -- 切换有一些影响,包括有利的和不利的。桌子会变大 2-3 倍。 PRIMARY KEY 的“正确”选择将进一步加快this SELECT 的速度。 (并且可能会或可能不会减慢其他SELECTs。)

关于SELECT 的注释:由于stringunit_num 在查询中是常量,所以ORDER BY timestamp asc, string asc, unit_num asc 的最后两个字段是不必要的。如果它们与 SELECT 中未明确的原因相关,那么我的建议可能不完整。

这个

WHERE filename = 'foobar'
  AND unit_num='40'
  AND string='2' 
  AND timestamp >= ...

INDEX(filename, unit_name, string, timestamp) 进行最佳处理。列的顺序并不重要除了timestamp 需要最后。重新排列当前的 UNIQUE 键,您可以得到最佳索引。 (同时,对于这个SELECT,没有一个索引非常好。)将其设为PRIMARY KEY,InnoDB 表将使其更快。

分区?没有优势。不是为了表现;不是因为你提到的任何其他事情。分区的一个常见用途是清除“旧”。如果您打算这样做,让我们进一步讨论。

在大表中,最好同时查看所有重要的SELECTs,这样我们就不会在破坏其他人的速度的同时加快其中的速度。 可能甚至证明分区有助于这种权衡。

【讨论】:

  • 我正在做的这个图形查询只是在这个表上完成的数百个其他查询之一,因此,其他索引也是如此。 20亿条记录?我以为Mysql摆脱了这个限制?它不能支持完整的 INT 11 自动增量吗?这里的弱设计的一部分是这张桌子有 12 年的历史。例如,文件名是 legacy。它可能是一个 long int 并且索引速度要快得多。我只是想找到一种方法来提高我的查询速度。分区有帮助吗?就 row_id 而言,这也是遗留问题,并且有大量代码使用它进行查询和编辑。
  • (11) 是显示值所需的最大字符数(减号,加 10 位数字)。它控制数字的大小。 INT(2)INT(99) 是相同的。
  • 在您添加我建议的INDEX 后,分区将不会 帮助 this SELECT。分区可能有助于其他查询,但不太可能。在my blog 中,我列出了分区有益的仅有的 4 种情况(我发现)。
  • INT SIGNED 是一个有符号的 32 位数字,占用 4 个字节,因此最大值为 20 亿 (2^32-1)。 INT UNSIGNED 是一个 32 位数字,占用 4 个字节,因此最大值为 40 亿。 BIGINT 是 8 个字节(一个巨大的限制)。 (我重新插入了这条评论以修正一个严重的错字。)
  • 我阅读了您的博客,我认为这里的大量记录使分区有益?我很快就会有超过 10 亿条记录。
【解决方案2】:

首先,我必须问一下分区给你带来了什么好处?是否有一些查询因此而运行得更快?

没有自动分区。

相反,您应该有一个每天运行的作业,它计算“最后一个活动”分区中的行数,看看它是否约为 10M。如果是这样,请添加另一个分区。

我建议保留“最后一个”分区(带有MAXVALUE 的分区)为空。这样你就可以REORGANIZE PARTITION 将它分成两个空分区,开销基本为零。我建议您不要使用ADD PARTITION,因为您可能会滑倒并在最后一个分区中放一些东西。

目前还不清楚什么会触发 10M。每个 store_id 是否有多行?每个商店都有新的行吗?如果是这样,则在 store_id 上进行分区,因为所有分区都会一直在增长。

好的,所以 store_id 只是参考手册中的一个蹩脚的例子。请提供SHOW CREATE TABLE,这样我们就可以谈具体,而不是挥手。完成这项任务的方法实在太多了。

活动是什么?

如果您主要点击“最近的”分区,则可能会导致分布不均匀 - 定期添加新分区并合并相邻的一对旧分区。 (我在一个系统中成功地做到了这一点。)

如果您要清除“旧”数据,那么显然您需要使用PARTITION BY RANGE(TO_DAYS(...)) 并使用DROP PARTITION 加上REORGANIZE PARTITION

还有很多其他场景。但我只知道分区提供任何性能优势的 4 种情况。见my blog

【讨论】:

  • 我有超过 600M 的行,我通常一次提取 100K 的图表。 90% 的时间 100K 来自最后 50M 行中的某个地方。那会更快吗?
  • 视情况而定。我没有看到任何索引——它们对性能至关重要。你用什么SELECT?由此,我应该能够推荐一个INDEX 并说明分区是否有帮助。
  • 另外...id 来自哪里?此表中的某些列组合是否“唯一”?
  • 上面的例子不是实际的表。我为我的表添加了 show create table 的结果。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-04-05
  • 1970-01-01
  • 1970-01-01
  • 2014-10-18
  • 2020-09-20
  • 1970-01-01
相关资源
最近更新 更多