我有一个巨大的InnoDB表(> 1TB,> 1B行)我想要分片:我想从那个大表中创建多个较小的独立表。
怎么做?
我已经尝试过:
随机疯狂的想法:
.idb
文件脱机并将其导入服务器,但我不知道是否存在能够执行此操作的工具。SHOW CREATE TABLE:
CREATE TABLE `Huge` (
`account_id` int(11) NOT NULL,
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`foo` varchar(255) NOT NULL,
`bar` int(11) NOT NULL,
`baz` char(2) NOT NULL,
PRIMARY KEY (`account_id`,`id`),
UNIQUE KEY `id` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=COMPRESSED;
分片键是account_id。主键目前是(account_id,id)
,因此行按account_id
进行聚类。
是否有任何工具可以自动执行此任务?有没有更好的方法呢?
答案 0 :(得分:8)
"拆分"是跨多个服务器分割数据(通常是一个表)。 "分区"将表拆分为相同服务器上的多个子表。你在做什么? Fabric进行分片。您对.ibd的评论暗示您正在考虑分区。
假设你想要PARTITION
一张大桌子,我首先要问为什么。这是一个严肃的问题,因为大多数人(我的意思是大多数)人们认为分区会神奇地产生一些好处,而实际上它不会。我相信有only 4 use cases for partitioning。你的情况是否属于他们中的任何一个?
另一方面,如果您想要Sharding,请提供SHOW CREATE TABLE
并讨论您要对哪个列进行整理。
修改(在明确目标后)
我希望你没有明确的FOREIGN KEYs
;它们不适用于分区或分片。
`id` bigint(20) NOT NULL AUTO_INCREMENT,
UNIQUE KEY `id` (`id`)
有两个方面的问题。
无需id
UNIQUE
; AUTO_INCREMENT
的唯一要求是它是某个索引中的第一列。因此,这将减轻系统的负担:
INDEX(ID)
AUTO_INCREMENT
适用于PARTITIONed
表,但它不适用于分片表。您需要评估id
的目的。它可以只在一个碎片中唯一,然后没有真正的问题。如果id
需要在所有分片中都是唯一的,那就更难了。可能唯一的解决方案是使用从某个中央服务器获取id的技术。但是,这会导致单点故障和瓶颈。如果你需要走这个方向,我可以建议如何避免这些缺点。
如何迁移到最终的分片系统?
我不建议对PARTITIONing
进行任何改编,从长远来看,它确实没有帮助。并且REORGANIZE PARTITION
的成本很高 - 复制所有行,包括提取的行和剩余的行。 编辑:如果您确实使用了分区,请使用pt-online-schema-change
以最短的停机时间进行拆分。
相反,我建议完善一个工具,将一个account_id
从一个分片迁移到另一个分片。这必须是自定义代码,因为可能会通过将帐户移动到其他服务器来影响其他表(以及表之间的引用)。从长远来看,这对于负载平衡,硬件升级,软件升级甚至架构更改都很有用。当您需要更改某些内容时,请使用新的OS /版本/架构/任何内容创建新的分片,并将用户迁移到该分片。
此工具的简单方法是
DELETE
行如果一个帐户是"小"这不是什么大问题。但是如果您需要最少的停机时间(写入被阻止),那么我们可以讨论更复杂的方法。
(如果您没有猜到,我已经去过那里,做过那样的事情。)
答案 1 :(得分:0)
您可以修改表格结构。此表格不是2NF
,因为id
在primary key
中出现时是唯一的(候选键)(表格的任何其他属性,如foo
和account_id
取决于在主键的一个子集-ie id
上。以下可以使用较少的约束来完成相同的工作:
id bigint(20) not null auto_increment primary key
现在,通过在account_id
上创建索引,您可以获得当前主键(account_id,id
)的所有好处。
作为第二个建议,您可以将表拆分为两部分:一部分包含foo
,另一部分包含其余列。这样,您将拥有一个相对较小的表(第二个表),其具有固定的行长度(因此更快),用于存储大多数数据(列),以及一个变量行长度表,其小于当前表并且不经常调用。
总之,在对表进行分区之前,我建议您将其拆分为:
CREATE TABLE `fixed_length` (
`id` bigint(20) NOT NULL AUTO_INCREMENT primary key,
`account_id` int(11) NOT NULL,
`bar` int(11) NOT NULL,
`baz` char(2) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=COMPRESSED;
和
create table `variable_length`(
`id` bigint(20) NOT NULL primary key,
`foo` varchar(255) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=COMPRESSED;
和index
的{{1}}:
account_id
现在,如果您希望按 create index ix_account_ix on fixed_length(account_id);
对数据进行分区,则可以保持account_id
完整,并仅在fixed_length
表上进行分区(通过您选择的任何方法)。