使用MySQL / InnoDB重新平衡表分片

时间:2015-06-26 15:01:26

标签: mysql innodb sharding rebalancing

我有一个巨大的InnoDB表(> 1TB,> 1B行)我想要分片:我想从那个大表中创建多个较小的独立表。

怎么做?

我已经尝试过:

  • 通过从原始表中选择行来将行移动到分区,并将它们插入分区。这需要很长时间,并且在操作期间保持行同步是很困难的(但是对于触发器似乎是可行的,只要分区在同一服务器上)。我还没有找到一个可以使用的工具来做到这一点。
  • 复制整个表,然后删除不属于该分区的行。仍然很慢,特别是考虑到桌子的大小。这显然是MySQL Fabric所做的。

随机疯狂的想法:

  • .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进行聚类。

是否有任何工具可以自动执行此任务?有没有更好的方法呢?

2 个答案:

答案 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 /版本/架构/任何内容创建新的分片,并将用户迁移到该分片。

此工具的简单方法是

  1. "座"写一个帐户
  2. 将记录复制到新分片
  3. 更改网守以了解该帐户现在位于新分片上
  4. 取消阻止写入
  5. 旧碎片上的最后(和graduallyDELETE
  6. 如果一个帐户是"小"这不是什么大问题。但是如果您需要最少的停机时间(写入被阻止),那么我们可以讨论更复杂的方法。

    (如果您没有猜到,我已经去过那里,做过那样的事情。)

答案 1 :(得分:0)

您可以修改表格结构。此表格不是2NF,因为idprimary key中出现时是唯一的(候选键)(表格的任何其他属性,如fooaccount_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表上进行分区(通过您选择的任何方法)。