数据库
首页 > 数据库> > mysql – 大表上的“创建唯一索引”花了太长时间

mysql – 大表上的“创建唯一索引”花了太长时间

作者:互联网

我正在尝试恢复GHTorrent的数据库转储(包含GitHub元数据的CSV文件).提交表有超过891百万行,project_commits有超过54亿行.由于这些表非常大,我不得不使用带外键检查的LOAD DATA INFILE加载它们.我正在使用MyISAM引擎.完成将记录导入表后,我正在尝试为这些表创建索引.

我正在为提交表运行以下mysql命令,但它在12小时内没有完成.

CREATE UNIQUE INDEX `sha` ON `ghtorrent_restore`.`commits` (`sha` ASC)  COMMENT '';

提交表如下所示:

enter image description here

我已经阅读了有关慢速索引的其他stackexchange问​​题,并在/ etc / mysql目录中的my.cnf文件中设置了以下内容.

[mysqld]
bulk_insert_buffer_size=1G
myisam_sort_buffer_size=8G
key_buffer_size=6G
sort_buffer_size=10M

由于上一个命令没有及时完成,我不得不从控制台用ctrl z停止它.我检查了MySQL工作台上的表,它没有显示为已损坏,但它显示了一些36GB作为索引长度.

enter image description here

导入此表大约需要25分钟,所以我期望索引它不应该超过一个小时,但我现在运行’创建唯一索引’命令大约2个小时没有任何进展迹象.

当我运行命令时,mysqld需要大量的cpu并且它会继续记忆.达到6GB后,它变得不那么活跃,似乎什么都不做.

enter image description here

以下是命令(在下图中选择一个)从mysql工作台查看的方式.

enter image description here

我在具有16GB RAM的Linux Mint 17.03机器上运行Mysql 5.7.22-0ubuntu0.16.04.1.

由于我不是高级用户,任何帮助都会有很大帮助.

更新[如Wilson H.建议]:

my.cnf文件[06/05/2018 01:06]

!includedir /etc/mysql/conf.d/
!includedir /etc/mysql/mysql.conf.d/

[mysqld]
secure-file-priv = ""

[mysqld]
bulk_insert_buffer_size=1G
myisam_sort_buffer_size=8G
key_buffer_size=6G
sort_buffer_size=10M

以下是缓冲区和缓存相关变量的快照.

buffer and cache related system variables

我没有修改我的mysql安装中的任何其他内容. Mysqld根据系统中可用的总16GB内存中的不同表格占用不同的内存量.我没有在mysql旁边运行任何cpu /内存密集型应用程序.

有趣的观察:一些测试与其他表显示增加行数的时间增加.趋势看起来是多项式的.

Polynomial increase of time

在执行上图中的最后一个mysql命令时捕获了以下统计信息(即,对表’项目’进行索引).

最佳:
top

iostat -x:
iostat -x

ulimit -a:

ulimit -a

df -h:

df -h

更新2:
由于在’commits’表上创建索引没有完成,我正在尝试其他表,最后我尝试在昨晚睡觉之前对’project_commits’表进行索引.令我惊讶的是,我发现完成索引只需要18分钟.

enter image description here

我没有做任何额外的修改,我不明白为什么’提交’表永远不会完成.我正在’commits’表上再次运行索引,看看它走了多远.

更新3:

‘SHOW CREATE TABLE提交;’

CREATE TABLE `commits` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `sha` varchar(40) DEFAULT NULL,
  `author_id` int(11) DEFAULT NULL,
  `committer_id` int(11) DEFAULT NULL,
  `project_id` int(11) DEFAULT NULL,
  `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`),
  KEY `commits_ibfk_1` (`author_id`),
  KEY `commits_ibfk_2` (`committer_id`),
  KEY `commits_ibfk_3` (`project_id`)
) ENGINE=MyISAM AUTO_INCREMENT=922093754 DEFAULT CHARSET=utf8

SHOW PROCESSLIST:
SHOW PROCESSLIST

底线:

除了’commit’表之外,所有表的索引都已完成(即,以下命令未完成执行).

CREATE UNIQUE INDEX `sha` ON `ghtorrent_restore`.`commits` (`sha` ASC)  COMMENT '';

当我运行查询(见下图)从8.91亿行计算大约19000行时,大约需要76秒.这个时间太高,因为我有一台配备Core i707700HQ CPU @ 2.8Ghz x 4,16GB DDR4 Ram的电脑,数据库安装在7200RPM的硬盘上? 76秒是否表示索引在“提交”表中无法正常工作?请注意,此查询是在启动计算机后立即执行的,以避免缓冲区的影响.

enter image description here

解决方法:

有很多问题.

Auto_inc为922M,是INT SIGNED的20亿限制的一半.建议您在下一个ALTER期间更改为INT UNSIGNED(40亿限制).

MyISAM节省了磁盘空间,但是比InnoDB更糟糕.注意:更改为InnoDB将需要更改多个设置.

MyISAM忽略了外键.

如果sha是SHA-1哈希,则索引很糟糕.

如果sha是SHA-1哈希,则可以通过UNHEX()将其压缩为BINARY(20).这会使桌子缩小20GB以上,占当前尺寸的30%!

如果sha是SHA-1哈希,请不要使用utf8;使用ascii或latin1.

如果sha是SHA-1哈希,并且您正在创建UNIQUE索引的列,请检查SHOW PROCESSLIST.如果它显示“通过key_buffer修复”,那么你应该杀掉它;完成需要几个月的时间.如果它说“按类别修复”,那么它将有希望完成.

考虑使用比4字节INT更小的ID用于作者,提交者和项目 – 除非你真的有数十亿个不同的值.等待!什么?每一个都将是独一无二的?我对此表示怀疑.

你有什么选择?他们中的一些人可能需要“复合”指数吗?

将多个ALTER(包括创建索引)放入单个语句中.每个ALTER都是表的完整副本(在MyISAM中).

myisam_sort_buffer_size = 8G是RAM的一半.这是不好的.建议3G.

多项式

The trend looks polynomial.

我希望您不要在已经与PRIMARY KEY具有相同列的表上的列上添加UNIQUE INDEX.那将是完全多余的.请为其中一个提供SHOW CREATE TABLE.

为什么“多项式”?如果它(我问这样),那将是因为“通过key_buffer修复”的性质:

对于每一行,在所有唯一(包括主要)索引中查找列,以确保它不是“重复”.此查找需要获取索引的BTree块并将其放入“key_buffer”进行测试.根据密钥的“顺序”与数据的“顺序”:

>对于auto_increment或timestamp,按时间顺序插入行(并且表没有变成碎片),所需的下一个块很可能是刚触及的块.这是最有效的,因为它需要最少的I / O.
>对于SHA1 / MD5 / UUID / etc,此查找将在索引周围跳转.因此,所需的下一个块越来越不可能出现在RAM内的key_buffer中.在极端情况下,这导致每次查找几乎1个磁盘读取!
>对于其他索引,时间介于两者之间.

标签:mysql-5-7,mysql,myisam,unique-key,index
来源: https://codeday.me/bug/20190806/1601033.html