为InnoDB的ALTER TABLE优化MySQL

时间:2009-03-17 14:56:59

标签: mysql alter-table performance

很快我们就需要对生产数据库进行架构更改。我们需要尽量减少这项工作的停机时间,但是,ALTER TABLE语句将运行很长一段时间。我们最大的表有1.5亿条记录,最大的表格文件是50G。 所有表都是InnoDB,它被设置为一个大数据文件(而不是每个表的文件)。 我们在8核机器,16G内存和RAID10配置上运行MySQL 5.0.46。

我有一些MySQL调优的经验,但这通常集中在来自多个客户端的读取或写入。有关此主题的互联网上有很多信息,但是,关于(暂时)调整MySQL服务器以加速InnoDB表上的ALTER TABLE或INSERT INTO的最佳实践似乎很少有可用的信息。 .SET FROM(我们可能会使用它来代替ALTER TABLE,以便有更多机会加快速度)。

我们计划进行的架构更改是向所有表添加一个整数列,并使其成为主键,而不是当前主键。我们还需要保留“旧”列,因此不能覆盖现有值。

尽快完成此任务的理想设置是什么?

6 个答案:

答案 0 :(得分:15)

您需要更仔细地考虑您的要求。

在最简单的层面上,改变表格的“最快”方法是在尽可能少的ALTER TABLE语句中进行,最好是一个。{这是因为MySQL复制表的数据来改变模式并进行十五次更改,而制作单个副本显然(并且确实)比复制表十五次更快,一次进行一次更改。

但我怀疑你问的是如何以最少的停机时间做这个改变。我这样做的方式,你基本上综合了非块ALTER TABLE的工作方式。但它有一些额外的要求:

  1. 您需要一种方法来跟踪添加和更改的数据,例如后者的“已修改”日期字段或前者的AUTO_INCREMENT字段。
  2. 您需要空间才能在数据库中拥有两个表副本。
  3. 您需要一个时间段,表格的更改不会超出快照
  4. 基本技术如您所建议,即使用INSERT INTO ... SELECT ...。至少你是在前面,因为你是从InnoDB表开始的,所以SELECT不会阻止。我建议在新的空表上执行ALTER TABLE,这将保存MySQL再次复制所有数据,这意味着您需要在INSERT INTO ... SELECT ...语句中正确列出所有字段。然后你可以做一个简单的RENAME语句来交换它。然后,您需要执行另一个INSERT INTO ... SELECT ... WHERE ...UPDATE ... INNER JOIN ... WHERE ...来获取所有已修改的数据。 您需要快速执行INSERTUPDATE ,否则您的代码将开始向您的快照添加新行和更新干扰更新。(如果您可以在RENAME之前的几分钟内将应用置于维护模式,则不会出现此问题。)

    除此之外,还有一些与键和缓冲区相关的设置,您可以只为一个会话更改,这可能有助于主数据的移动。 read_rnd_buffer_sizeread_buffer_size之类的内容对增加非常有用。

答案 1 :(得分:15)

您可能希望查看Percona工具包中的pt-online-schema-change。基本上它的作用是:

  • 复制原始表格结构,运行ALTER。
  • 将旧表格中的行复制到新创建的表格。
  • 使用触发器在复制时跟踪和同步更改。
  • 当一切都完成后,它会通过重命名来交换表格。

对于单实例数据库非常有效,但是如果使用复制可能会非常棘手,并且您无法负担停止从属并在以后重建它们。

还有关于此here的精彩网络研讨会。

PS:我知道这是一个老问题,只是回答有人通过搜索引擎点击这个问题。

答案 2 :(得分:12)

  1. 设置奴隶
  2. 停止复制。
  3. 在奴隶上制作ALTER
  4. 让奴隶赶上主人
  5. 交换主站和从站,因此从站成为生产服务器,具有更改的结构和最短的停机时间

答案 3 :(得分:11)

不幸的是,这并不像staticsan在他的回答中那样简单。在线创建新表并移动数据很容易,在维护模式下进行清理也足够了,但是,Mysql RENAME操作会自动操作对旧表的任何外键引用。这意味着对原始表的任何外键引用仍将指向您重命名表的任何内容。

所以,如果你有任何对表的外键引用,你试图改变你就是要么改变那些表来替换你对新表的引用,要么更糟糕的是如果那个表很大你必须重复大表二号的过程。

过去对我们起作用的另一种方法是处理一组处理alter的Mysql副本。我不是说这个过程的最佳人选,但它基本上包括打破一个从属服务器的复制,在该实例上运行补丁,一旦alter table完成就重新启动复制,以便赶上复制。复制赶上后,您将站点置于维护模式(如有必要),从主站切换到新的修补从站作为新的主数据库。

我唯一记不住的是,当你将其他奴隶指向新的主人时,他们也会得到改变。对此过程的一个警告,我们通常使用它来在代码需要更改之前滚动更改补丁,或者在代码更改为不再引用列/键之后滚动更改补丁。

答案 4 :(得分:5)

我测试了各种策略来加速一个alter table。最终我在特定情况下的速度增加了10倍。结果可能适用于您的情况,也可能不适用。但是,基于此,我建议尝试使用InnoDB日志文件/缓冲区大小参数。

简而言之,只增加innodb_log_file_size和innodb_log_buffer_size具有可衡量的效果(小心!更改 innodb_log_file_size有风险。请查看以下内容以获取更多信息。)

基于粗略的写入数据速率(iostat)和cpu活动,瓶颈是基于io而不是数据吞吐量。在速度较快的500s运行中,写入吞吐量至少与硬盘所期望的相同。

尝试过的效果优化:

更改innodb_log_file_size可能很危险。请参阅http://www.mysqlperformanceblog.com/2011/07/09/how-to-change-innodb_log_file_size-safely/链接中解释的技术(文件移动)在我的案例中运行良好。

有关innodb和调整日志大小的信息,另请参阅http://www.mysqlperformanceblog.com/2007/11/03/choosing-innodb_buffer_pool_size/http://www.mysqlperformanceblog.com/2008/11/21/how-to-calculate-a-good-innodb-log-file-size/。更大的日志文件的一个缺点是崩溃后恢复时间更长。

测试运行和粗略计时:

  • 新建createad表的简单加载数据:6500s
  • 加载数据w。 innodb_log_file_size = 200M,innodb_log_buffer_size = 8M,innodb_buffer_pool_size = 2200M,autocommit = 0; unique_checks = 0,foreign_key_checks = 0:500s
  • 加载数据w。 innodb_log_file_size = 200M,innodb_log_buffer_size = 8M:500s
  • 等效直接改变表w。 datainnodb_log_file_size = 200M,innodb_log_buffer_size = 8M:500s

测试细节:表:InnoDB,6M行,2.8G磁盘,单个文件(innodb_file_per_table选项),主键是1个整数,+ 2个unque约束/索引,8列,平均。行长218个字节。服务器:Ubuntu 12.04,x86_64,虚拟机,8核,16GB,sata消费级磁盘,没有raid,没有数据库活动,微不足道的其他进程活动,其他和更小的虚拟机中的微不足道的活动。 Mysql 5.1.53。除了增加1400M的innodb_buffer_pool_size之外,初始服务器配置是非常默认的。 alter table添加了2个小列。我没有对原始的alter table进行计时,而是尝试使用等效的load data infile语句,最后我做了直接的alter table并得到了可比较的结果。

此问题至少与以下问题有关:

答案 5 :(得分:-4)

我真的不知道如何优化它,但在进行此类更新之前将网站置于离线模式通常是一种很好的做法。

然后,你可以在凌晨3点运行你的数据库脚本,所以如果停机时间比理想时间长,那就不重要了。