拥有8000万条记录并添加索引的表需要超过18个小时(或永远)!怎么办?

时间:2010-09-12 17:23:37

标签: mysql database database-design partitioning

简要回顾发生的事情。我正在处理7100万条记录(与其他人处理的数十亿条记录相比并不多)。在另一个thread上,有人建议我的群集的当前设置不适合我的需要。我的表结构是:

CREATE TABLE `IPAddresses` (
  `id` int(11) unsigned NOT NULL auto_increment,
  `ipaddress` bigint(20) unsigned default NULL,
  PRIMARY KEY  (`id`)
) ENGINE=MyISAM;

我添加了7100万条记录,然后做了一个:

ALTER TABLE IPAddresses ADD INDEX(ipaddress);

已经14个小时,操作仍未完成。通过谷歌搜索,我发现有一个众所周知的方法来解决这个问题 - 分区。我知道我现在需要根据ipaddress对我的表进行分区,但是我可以在不重新创建整个表的情况下执行此操作吗?我的意思是,通过ALTER声明?如果是,则有一个要求说要分区的列应该是主键。我将使用这个ipaddress的id来构建一个不同的表,所以ipaddress不是我的主键。在这种情况下,如何对表格进行分区?

5 个答案:

答案 0 :(得分:37)

好的事实证明,这个问题不仅仅是一个简单的创建表,索引它并忘记问题:)这是我做的,以防万一其他人面临同样的问题(我使用了IP地址的例子,但它适用于其他数据类型):

问题:您的桌子有数百万条记录,您需要快速添加索引

用例:考虑在查找表中存储数百万个IP地址。添加IP地址应该不是一个大问题,但在它们上创建索引需要超过14个小时。

解决方案:使用MySQL's Partitionin g策略

对您的表格进行分区

案例#1:当您尚未创建所需的表时

CREATE TABLE IPADDRESSES(
  id INT UNSIGNED NOT NULL AUTO_INCREMENT,
  ipaddress BIGINT UNSIGNED,
  PRIMARY KEY(id, ipaddress)
) ENGINE=MYISAM
PARTITION BY HASH(ipaddress)
PARTITIONS 20;

案例#2:当您想要的表格已经创建时 似乎有一种方法可以使用ALTER TABLE来做到这一点,但我还没有想出一个适当的解决方案。相反,有一个效率稍低的解决方案:

CREATE TABLE IPADDRESSES_TEMP(
  id INT UNSIGNED NOT NULL AUTO_INCREMENT,
  ipaddress BIGINT UNSIGNED,
  PRIMARY KEY(id)
) ENGINE=MYISAM;

将您的IP地址插入此表。然后使用分区创建实际表:

CREATE TABLE IPADDRESSES(
  id INT UNSIGNED NOT NULL AUTO_INCREMENT,
  ipaddress BIGINT UNSIGNED,
  PRIMARY KEY(id, ipaddress)
) ENGINE=MYISAM
PARTITION BY HASH(ipaddress)
PARTITIONS 20;

最后

INSERT INTO IPADDRESSES(ipaddress) SELECT ipaddress FROM IPADDRESSES_TEMP;
DROP TABLE IPADDRESSES_TEMP;
ALTER TABLE IPADDRESSES ADD INDEX(ipaddress)

然后你去了......新表上的索引花了我大约2个小时在3.2GB机器上用1GB RAM :)希望这有帮助。

答案 1 :(得分:7)

使用MySQL创建索引很慢,但速度不慢。拥有7100万条记录,需要几分钟而不是14小时。可能的问题是:

  • 您尚未配置排序缓冲区大小和其他配置选项

看这里:http://dev.mysql.com/doc/refman/5.5/en/server-system-variables.html#sysvar_myisam_sort_buffer_size

如果您尝试使用8MB排序缓冲区生成1GB索引,则需要大量传递。但是如果缓冲区大于CPU缓存,它将变慢。所以你必须测试并看看什么效果最好。

  • 有人锁定桌面
  • 你的IO系统很糟糕
  • 您的服务器正在交换

像往常一样检查iostat,vmstat,logs等。在你的桌子上发出一个LOCK TABLE,检查是否有人锁定了它。

我的64位桌面上的FYI在10M随机BIGINT上创建索引需要17秒......

答案 2 :(得分:5)

我遇到了一个问题,我希望通过添加索引来加快查询速度。该表只有大约300,000条记录,但也花了太长时间。当我检查mysql服务器进程时,结果发现我试图优化的查询仍然在后台运行。 4次!在我杀死这些查询之后,索引是在快速完成的。也许同样的问题适用于您的情况。

答案 3 :(得分:3)

您正在使用即将弃用的MyISAM。另一种选择是InnoDB。

“InnoDB是一种适用于MySQL的事务安全(ACID兼容)存储引擎,具有提交,回滚和崩溃恢复功能,可保护用户数据.InnoDB行级锁定(无需升级到更粗略的粒度锁定)和Oracle-样式一致的非锁定读取增加了多用户并发性和性能.InnoDB将用户数据存储在聚簇索引中,以减少基于主键的常见查询的I / O.为了保持数据完整性,InnoDB还支持FOREIGN KEY参照完整性约束。您可以自由将InnoDB表与来自其他MySQL存储引擎的表混合,甚至在同一语句中。“

http://dev.mysql.com/doc/refman/5.0/en/innodb.html

根据:

http://dev.mysql.com/tech-resources/articles/storage-engine/part_1.html

,你应该能够通过一个简单的alter命令在不同的引擎之间切换,这可以让你有一些灵活性。它还声明可以单独配置数据库中的每个表。

答案 4 :(得分:0)

在你的桌子上。你已经插入了710亿条记录。现在,如果要在表的主键列上创建分区,可以使用alter table选项。举一个例子供您参考。

CREATE TABLE t1 (
    id INT,
    year_col INT
);

ALTER TABLE t1
    PARTITION BY HASH(id)
    PARTITIONS 8;