MySQL在`ALTER TABLE ... ENABLE KEYS`时完全挂起

Ala*_*air 8 mysql myisam index

我对数据库管理知之甚少,但我必须处理我网站上的一些非常大的表。

该服务器具有 64GB 内存和 Intel Core i7-3820 (4 x 3600 MHz)。它所做的大部分事情都是 MySQL。我使用一半 MyISAM 和一半 InnoDB 表。

我在 MyISAM 中有几个表,有数十亿行。每天我都有一个脚本禁用键,再添加几百万行,然后再次启用键。这ALTER TABLE... ENABLE KEYS会导致服务器基本上停止几个小时。任何使用 MySQL 的网站都不会加载,即使它们根本不访问正在更改的表。

还请告诉我如何设置 my.cnf 文件以解决此问题并优化以尽快重建这些索引。有人告诉我增加key_buffer_size,但我不确定这是否好,因为每个人似乎都有不同的意见..?目前它看起来像这样:

[client]
port        = 3306
socket      = /var/lib/mysql/mysql.sock

[mysqld]
port = 3306
socket = /var/lib/mysql/mysql.sock
skip-external-locking
max_allowed_packet = 512M
table_open_cache = 1024
sort_buffer_size = 128M
read_buffer_size = 2M
read_rnd_buffer_size = 8M
myisam_sort_buffer_size = 24G
thread_cache_size = 12
query_cache_size = 256M
thread_concurrency = 16
log-bin=mysql-bin
binlog_format=mixed
server-id   = 1
innodb_file_per_table = 1
table_cache = 1024
key_buffer = 256M
key_buffer_size = 12G
myisam_repair_threads = 4
big-tables
bind-address = 127.0.0.1
max_connections = 400
tmp_table_size = 4G
max_heap_table_size = 4G
log_bin = /backup/mysql-bin-logs/mysql-bin.log
expire_logs_days        = 10
max_binlog_size         = 100M
innodb_buffer_pool_size = 12G
local-infile=1
net_read_timeout = 1800
net_write_timeout = 1800


[mysqldump]
quick
max_allowed_packet = 16M

[mysql]
no-auto-rehash
local-infile=1

[myisamchk]
key_buffer_size = 256M
sort_buffer_size = 256M
read_buffer = 2M
write_buffer = 2M
key_buffer = 256M

[mysqlhotcopy]
interactive-timeout
Run Code Online (Sandbox Code Playgroud)

MySQL 版本

innodb_version 5.5.30
protocol_version 10
version 5.5.30-log
version_comment MySQL Community Server (GPL) by Remi
version_compile_machine x86_64
version_compile_os Linux
Run Code Online (Sandbox Code Playgroud)

更新

我已经开始赏金了。我更改了一些 my.conf 设置(也在这篇文章中更新)。然后,当我尝试在大表上重建索引时,它开始时Repair with 8 threads(即使修复线程的数量设置为 4),然后当我几小时后检查它时,相同的命令打开了Repair with keycache,现在是它所在的位置坐着。所以不知何故它从 sort 降级为 keycache 方法(我不知道为什么!)

请帮我优化一下!它应该每天运行,但目前需要几天才能运行ALTER TABLE... ENABLE KEYS

以下是我被要求提供的其他一些变量,我不明白,但可能会帮助您帮助我:

+----------------------+-------+
| Variable_name        | Value |
+----------------------+-------+
| key_cache_block_size | 1024  |
+----------------------+-------+
+-----------------+-------------+
| Variable_name   | Value       |
+-----------------+-------------+
| key_buffer_size | 12884901888 |
+-----------------+-------------+
+-------------------+-------+
| Variable_name     | Value |
+-------------------+-------+
| Key_blocks_unused | 0     |
+-------------------+-------+
Run Code Online (Sandbox Code Playgroud)

更新日期 2 (5/21)

认为它会解决我的问题,我已经更改了我的脚本以完全截断表,重新插入所有行,并一次添加一个索引。不是禁用密钥,而是添加新行然后启用密钥。

不幸的是它没有帮助,因为索引创建仍然转到repair with keycache.

这是结果SHOW CREATE TABLE research_storage1

CREATE TABLE `research_storage1` (
  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
  `word1` mediumint(8) unsigned NOT NULL,
  `word2` mediumint(8) unsigned NOT NULL,
  `origyear` smallint(5) unsigned NOT NULL,
  `cat` tinyint(3) unsigned NOT NULL,
  `pibn` int(10) unsigned NOT NULL,
  `page` smallint(5) unsigned NOT NULL,
  `pos` smallint(5) unsigned NOT NULL,
  `num` tinyint(3) unsigned NOT NULL,
  PRIMARY KEY (`id`),
  KEY `pibnpage` (`pibn`,`page`,`word2`,`word1`),
  KEY `word21pibn` (`word2`,`word1`,`pibn`,`num`),
  KEY `word12num` (`word1`,`word2`,`num`),
  KEY `cat1` (`cat`,`word1`),
  KEY `year1` (`origyear`,`word1`),
  KEY `catyear1` (`cat`,`origyear`,`word1`),
  KEY `pibn` (`pibn`,`word1`)
) ENGINE=MyISAM DEFAULT CHARSET=ascii COLLATE=ascii_bin DATA DIRECTORY='/storage/researchdb/' INDEX DIRECTORY='/storage/researchdb/';
Run Code Online (Sandbox Code Playgroud)

我也运行了这个命令: SELECT data_length/power(1024,3) dat,index_length/power(1024,3) ndx FROM information_schema.tables WHERE table_schema='dbname' AND table_name='tablename'; 但问题是我目前有 2 个表用于这个表,1 个被截断,1 个包含所有数据但没有索引(一旦索引完成,前者将被后者替换)...原因是因为我无法构建该死的索引(因此出现问题)。这是截断表的信息,然后是包含数据但没有索引的表:

+------+------------------------+
| dat  | ndx                    |
+------+------------------------+
|    0 | 0.00000095367431640625 |
+------+------------------------+
+-------------------+--------------------+
| dat               | ndx                |
+-------------------+--------------------+
| 51.61232269741595 | 27.559160232543945 |
+-------------------+--------------------+
Run Code Online (Sandbox Code Playgroud)

另请注意,在接收到所有数据之前,该表将比此大 10 倍。

Rol*_*DBA 3

现在,你处于一个非常幸运的位置。我注意到你已经big-tables定义了。这可以防止您遇到“表已满”错误。为什么这样好呢?

每当您获得“使用 Keycache 修复”状态时,您就没有可用空间来进行文件排序。增大 sort_buffer_size 不一定是答案,因为临时表会立即变成磁盘文件。

你有两个选择

选项#1:增加datadir的磁盘空间

所在的数据卷/var/lib/mysql(或任何datadir内容)可能没有足够的空间来容纳磁盘上的物化临时表。我建议将磁盘卷的大小增加到至少两倍。

缺点:将数据库移动到更大的磁盘的一次性维护。

选项#2:用于临时表的单独磁盘

也许应该设置一个单独的磁盘卷,其唯一的目的是容纳临时表。尝试这个

  • 步骤01:安装与home大小相同的磁盘datadir
  • 步骤02:mkdir /tmptables
  • 步骤03:将新磁盘卷挂载到文件夹/tmptables
  • 步骤04:chown mysql:mysql /tmptables
  • 步骤05:将此添加到my.cnf
    • tmpdir=/tmptables
  • 步骤06:service mysql restart

一旦你制作了磁盘并添加了tmpdir,你应该有更多的活动空间。

缺点:将临时表内容从某些 SQL 命令(例如 DDL)/tmptables的主目录传输回。datadir

更新 2013-05-21 00:10 美国东部时间

你根本就没有足够的内存。世界上所有的核心都无法帮助MyISAM。

建议#1:缩短你的钥匙

我可以从键中看出您正在尝试从索引中检索所有数据并避免接触表。如果不是数十亿行的话,一切都很好。

这里有一些值得考虑的事情:缩短每个索引的键

CREATE TABLE `research_storage1` (
  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
  `word1` mediumint(8) unsigned NOT NULL,
  `word2` mediumint(8) unsigned NOT NULL,
  `origyear` smallint(5) unsigned NOT NULL,
  `cat` tinyint(3) unsigned NOT NULL,
  `pibn` int(10) unsigned NOT NULL,
  `page` smallint(5) unsigned NOT NULL,
  `pos` smallint(5) unsigned NOT NULL,
  `num` tinyint(3) unsigned NOT NULL,
  PRIMARY KEY (`id`),
  KEY `pibnpage` (`pibn`,`page`),
  KEY `word21` (`word2`,`word1`),
  KEY `cat1` (`cat`,`word1`),
  KEY `year1` (`origyear`,`word1`),
  KEY `catyear1` (`cat`,`origyear`),
  KEY `pibn` (`pibn`,`word1`)
) ENGINE=MyISAM DEFAULT CHARSET=ascii COLLATE=ascii_bin
DATA DIRECTORY='/storage/researchdb/'
INDEX DIRECTORY='/storage/researchdb/';
Run Code Online (Sandbox Code Playgroud)

建议#2:停止使用ALTER TABLE...ENABLE KEYS;

您应该尝试将 MyISAM 导入到新表中而不使用ENABLE KEYS.

CREATE TABLE `research_storagenew` (
  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
  `word1` mediumint(8) unsigned NOT NULL,
  `word2` mediumint(8) unsigned NOT NULL,
  `origyear` smallint(5) unsigned NOT NULL,
  `cat` tinyint(3) unsigned NOT NULL,
  `pibn` int(10) unsigned NOT NULL,
  `page` smallint(5) unsigned NOT NULL,
  `pos` smallint(5) unsigned NOT NULL,
  `num` tinyint(3) unsigned NOT NULL,
  PRIMARY KEY (`id`),
  KEY `pibnpage` (`pibn`,`page`,`word2`,`word1`),
  KEY `word21pibn` (`word2`,`word1`,`pibn`,`num`),
  KEY `word12num` (`word1`,`word2`,`num`),
  KEY `cat1` (`cat`,`word1`),
  KEY `year1` (`origyear`,`word1`),
  KEY `catyear1` (`cat`,`origyear`,`word1`),
  KEY `pibn` (`pibn`,`word1`)
) ENGINE=MyISAM DEFAULT CHARSET=ascii COLLATE=ascii_bin
DATA DIRECTORY='/storage/researchdb/'
INDEX DIRECTORY='/storage/researchdb/';
INSERT INTO `research_storagenew` SELECT * FROM `research_storage1`;
DROP TABLE `research_storage1`;
ALTER TABLE `research_storagenew` RENAME `research_storage1`;
Run Code Online (Sandbox Code Playgroud)

概括

再看一下表定义。仅索引上的索引条目就有 18 个字节pibnpage。即每十亿行 18G。同样适用于word21pibn. 你只是没有足够的空间。必须尝试我的最新建议之一来绕过对所有这些键进行排序的需要。

更新 2013-05-22 12:15 美国东部时间

你问

关于缩短按键:这会导致什么样的性能影响?我们所说的时间是两倍、十倍还是 1000 倍?

我无法确定会对运行时间产生什么影响。不过,我可以这样说:可能会有一些额外的磁盘 I/O,因为索引将不再包含所需的列信息。查询不必转向.MYD文件来检索其他列信息。请记住,MyISAM 适合重读查询。

如果在一天中的重读期间应该有 INSERT 和 DELETE,我可以进一步建议的唯一调整是启用并发 INSERT。

[mysqld]
concurrent_insert=1
Run Code Online (Sandbox Code Playgroud)

这将允许 INSERT 到 MyISAM 中,而无需交叉检查表中的空闲块。这可能会使表增长得更快一些。

就维护而言,您必须使用SUGGESTION #2,而不是过分依赖ALTER TABLE ... ENABLE KEYS;如此臃肿的表。也许您应该考虑转向/storage/researchdb/SSD。