如何将66,862,521行表从MyISAM转换为InnoDB,而又不用离线几个小时?


18

是否有可能(以及如何)在不使应用程序脱机的情况下将巨大的MyISAM表转换为InnoDB。它需要每秒在该表中插入几行,但是可以将其挂起大约2分钟。

显然ALTER TABLE ... engine = innodb无法正常工作。因此,我计划使用innodb引擎创建一个新表并将内容复制到其中。最后,挂起应用程序日志线程和RENAME TABLE。

不幸的是,即使以100行的小批量进行复制,在一段时间后也会产生明显的延迟。

编辑:现有行永远不会更改,此表用于记录。



3
好吧,这个问题是关于减少对话时间。我不在乎对话是否需要几天或几周的时间。但是它必须在后台运行,而不需要关闭应用程序并且不造成明显的延迟。
Hendrik Brummermann 2011年

Answers:


15

创建主-主设置,如下所示:

  • 创建第二个母版MasterB
  • MasterB充当 logTable
  • 创建logTable_new为innodb
  • INSERT INTO logTable_new SELECT * FROM logTable在MasterB上运行(伪代码),它将复制发送到MasterA
  • logTable_new上MasterA完成同步,换出表

10

鉴于以下约束:

我不在乎对话是否需要几天或几周的时间。但是它必须在后台运行,而不需要关闭应用程序并且不造成明显的延迟

在进行日志记录时,如果您有一些好的方法来设置标记,以便可以告诉您启动过程的目的,那么您就可以重新应用任何日志,或者将日志写到文本文件中,以便您可以稍后将它们摄入 LOAD DATA INFILE

问题的一部分是较小的批量写入意味着必须一遍又一遍地重新计算索引。您最好一次全部运行它,但这可能会导致系统出现一些“明显的”延迟。.但是您不必在生产服务器上这样做。

  1. 暂停日志记录或设置一些标记,以便以后可以重新应用日志。
  2. 将MyISM表复制到另一个系统
  3. 在另一个系统上,以不同的名称创建一个InnoDB表并迁移数据 (转储和使用它甚至可能更快LOAD DATA INFILE
  4. 将InnoDB表复制回原始系统
  5. 为日志记录设置另一个标记。
  6. 从最后两个标记之间将所有日志重新应用于新表。
  7. (如果第6步花费了大约一分钟以上的时间,请重复第5步和第6步,直到只有几秒钟的时间为止)
  8. 交换表(将旧表重命名为table_BACKUP,将旧表重命名为旧表)
  9. 自上一个标记以来追赶日志。

9

不幸的是,即使以100行的小批量进行复制,在一段时间后也会产生明显的延迟。

您是在每个批处理之间添加任何延迟,还是只是将更新分批处理并在上一个批处理之后直接运行每个批处理?

如果是这样,请尝试使用您喜欢的语言编写转换脚本,如下所示:

repeat
    copy oldest 100 rows that haven't been copied yet to new table
    sleep for as long as that update took
until there are <100 rows unprocessed
stop logging service
move the last few rows
rename tables
restart logging
delete the old table when you are sure the conversion has worked

这应该确保转换不会占用服务器容量的一半以上,甚至允许随着系统使用时间的变化而造成的负载差异。

或者,如果您想在服务相对空闲时使用尽可能多的时间,但在数据库需要为其用户做一些工作时退后(可能会暂停相当长的时间),请替换sleep for as long as the update tookif the server's load is above <upper measure>, sleep for some seconds then check again, loop around the sleep/check until the load drops below <lower measure>。这意味着它可以在安静的时候继续前进,但是在服务器忙于执行正常工作负载时将完全暂停。确定负载将取决于您的操作系统-在Linux和类似操作系统下,1分钟负载平均值/proc/loadavguptime应该输出的负载。<lower measure><upper measure>可能是相同的值,尽管在此类控件中通常会有所不同,因此您的过程不会继续启动,而是由于其自身的重启而对负载量度产生影响而立即暂停。

当然,这对于可能会修改旧行的表不起作用,但对于像您所描述的日志表一样会很好。

在这种情况下,您将要忽略填充新表之后创建索引的通常智慧。当您希望事情尽可能快时(确实对系统其余部分的影响)时,这样做的确确实更有效,但是在这种情况下,您不希望在过程结束时像这样浪费大量的负载。索引是一次性创建的,因为在忙碌的时候您不能暂停索引。


4

这样的事情行吗?

  1. 暂停日志记录(因此$auto_increment日志记录表mytable 上的不变)。
  2. 使用记下该$auto_incrementSHOW TABLE STATUS LIKE 'mytable'
  3. CREATE TABLE mytable_new LIKE mytable
  4. ALTER TABLE mytable_new AUTO_INCREMENT=$auto_increment ENGINE=Innodb
  5. RENAME TABLE mytable TO mytable_old, mytable_new TO mytable
  6. 再次启用日志记录。现在将开始填充Innodb表。
  7. INSERT INTO mytable SELECT * FROM mytable_old

您可以分批或在一条语句中执行第7步,因为它不会阻塞正常的日志记录。


由于innodb处理auto_increment的方式,它仍然会阻塞。默认情况下,innodb在插入到auto_increment列时会获得表级锁,并在插入完成后立即释放该锁。
ovais.tariq
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.