很快我们就需要对生产数据库进行架构更改。我们需要尽量减少这项工作的停机时间,但是,ALTER TABLE语句将运行很长一段时间。我们最大的表有1.5亿条记录,最大的表格文件是50G。 所有表都是InnoDB,它被设置为一个大数据文件(而不是每个表的文件)。 我们在8核机器,16G内存和RAID10配置上运行MySQL 5.0.46。
我有一些MySQL调优的经验,但这通常集中在来自多个客户端的读取或写入。有关此主题的互联网上有很多信息,但是,关于(暂时)调整MySQL服务器以加速InnoDB表上的ALTER TABLE或INSERT INTO的最佳实践似乎很少有可用的信息。 .SET FROM(我们可能会使用它来代替ALTER TABLE,以便有更多机会加快速度)。
我们计划进行的架构更改是向所有表添加一个整数列,并使其成为主键,而不是当前主键。我们还需要保留“旧”列,因此不能覆盖现有值。
尽快完成此任务的理想设置是什么?
答案 0 :(得分:15)
您需要更仔细地考虑您的要求。
在最简单的层面上,改变表格的“最快”方法是在尽可能少的ALTER TABLE
语句中进行,最好是一个。{这是因为MySQL复制表的数据来改变模式并进行十五次更改,而制作单个副本显然(并且确实)比复制表十五次更快,一次进行一次更改。
但我怀疑你问的是如何以最少的停机时间做这个改变。我这样做的方式,你基本上综合了非块ALTER TABLE
的工作方式。但它有一些额外的要求:
AUTO_INCREMENT
字段。 基本技术如您所建议,即使用INSERT INTO ... SELECT ...
。至少你是在前面,因为你是从InnoDB表开始的,所以SELECT
不会阻止。我建议在新的空表上执行ALTER TABLE
,这将保存MySQL再次复制所有数据,这意味着您需要在INSERT INTO ... SELECT ...
语句中正确列出所有字段。然后你可以做一个简单的RENAME
语句来交换它。然后,您需要执行另一个INSERT INTO ... SELECT ... WHERE ...
和UPDATE ... INNER JOIN ... WHERE ...
来获取所有已修改的数据。 您需要快速执行INSERT
和UPDATE
,否则您的代码将开始向您的快照添加新行和更新将干扰更新。(如果您可以在RENAME
之前的几分钟内将应用置于维护模式,则不会出现此问题。)
除此之外,还有一些与键和缓冲区相关的设置,您可以只为一个会话更改,这可能有助于主数据的移动。 read_rnd_buffer_size
和read_buffer_size
之类的内容对增加非常有用。
答案 1 :(得分:15)
您可能希望查看Percona工具包中的pt-online-schema-change。基本上它的作用是:
对于单实例数据库非常有效,但是如果使用复制可能会非常棘手,并且您无法负担停止从属并在以后重建它们。
还有关于此here的精彩网络研讨会。
PS:我知道这是一个老问题,只是回答有人通过搜索引擎点击这个问题。
答案 2 :(得分:12)
答案 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/。更大的日志文件的一个缺点是崩溃后恢复时间更长。
测试运行和粗略计时:
测试细节:表: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点运行你的数据库脚本,所以如果停机时间比理想时间长,那就不重要了。