简体   繁体   English

针对 InnoDB 的 ALTER TABLE 优化 MySQL

[英]Optimizing MySQL for ALTER TABLE of InnoDB

Sometime soon we will need to make schema changes to our production database.不久之后,我们将需要对生产数据库进行架构更改。 We need to minimize downtime for this effort, however, the ALTER TABLE statements are going to run for quite a while.我们需要尽量减少这项工作的停机时间,但是,ALTER TABLE 语句将运行相当长的一段时间。 Our largest tables have 150 million records, largest table file is 50G.我们最大的表有1.5亿条记录,最大的表文件是50G。 All tables are InnoDB, and it was set up as one big data file (instead of a file-per-table).所有表都是 InnoDB,并且它被设置为一个大数据文件(而不是一个文件每个表)。 We're running MySQL 5.0.46 on an 8 core machine, 16G memory and a RAID10 config.我们在 8 核机器、16G memory 和 RAID10 配置上运行 MySQL 5.0.46。

I have some experience with MySQL tuning, but this usually focusses on reads or writes from multiple clients.我在 MySQL 调优方面有一些经验,但这通常侧重于来自多个客户端的读取或写入。 There is lots of info to be found on the Internet on this subject, however, there seems to be very little information available on best practices for (temporarily) tuning your MySQL server to speed up ALTER TABLE on InnoDB tables, or for INSERT INTO.. SELECT FROM (we will probably use this instead of ALTER TABLE to have some more opportunities to speed things up a bit).在 Internet 上有很多关于这个主题的信息,但是,似乎很少有关于(临时)调整 MySQL 服务器以加速 InnoDB 表上的 ALTER TABLE 或 INSERT INTO 的最佳实践的信息。 . SELECT FROM(我们可能会使用它而不是 ALTER TABLE 以获得更多机会来加快速度)。

The schema changes we are planning to do is adding a integer column to all tables and make it the primary key, instead of the current primary key.我们计划进行的架构更改是向所有表添加一个 integer 列,并将其作为主键,而不是当前的主键。 We need to keep the 'old' column as well so overwriting the existing values is not an option.我们还需要保留“旧”列,因此不能选择覆盖现有值。

What would be the ideal settings to get this task done as quick as possible?什么是尽快完成这项任务的理想设置?

You might want to look at pt-online-schema-change from Percona toolkit.您可能想查看 Percona 工具包中的pt-online-schema-change Essentially what it does is:本质上它的作用是:

  • Copies original table structure, runs ALTER.复制原始表结构,运行 ALTER。
  • Copies rows from old table to newly created one.将旧表中的行复制到新创建的表中。
  • Uses triggers to track and sync changes while copying.复制时使用触发器跟踪和同步更改。
  • When everything is finished it swaps tables by renaming both.一切完成后,它通过重命名两个表来交换表。

Works very well for single instance databases, but might be quite tricky if you use replication and you can't afford stopping slaves and rebuilding them later.对于单实例数据库非常有效,但如果您使用复制可能会非常棘手,并且您无法负担停止从属服务器并在以后重建它们。

There's also a nice webinar about this here . 这里还有一个很好的网络研讨会。

PS: I know it's an old question, just answering in case someone hits this via search engine. PS:我知道这是一个老问题,只是回答以防有人通过搜索引擎点击此问题。

You need to think about your requirements a little more carefully.您需要更仔细地考虑您的要求。

At the simplest level, the "fastest" way to get the table changed is to do it in as few ALTER TABLE statements as possible, preferably one.在最简单的级别上,更改表的“最快”方法是在尽可能少的ALTER TABLE语句中进行更改,最好是一个。 This is because MySQL copies a table's data to change the schema and making fifteen changes whilst make a single copy is obviously (and really is) faster than copying the table fifteen times, making one change at a time.这是因为 MySQL 复制表的数据以更改架构并进行十五次更改,同时进行一次复制显然(并且实际上是)比复制表十五次更快,一次进行一次更改。

But I suspect you're asking how to do this change with the least amount of downtime.但是我怀疑您是在问如何以最少的停机时间进行此更改。 The way I would do that, you basically synthesize the way a non-block ALTER TABLE would work.我这样做的方式,你基本上综合了非块ALTER TABLE的工作方式。 But it has some additional requirements:但它有一些额外的要求:

  1. you need a way to track added and changed data, such as with a "modified" date field for the latter, or an AUTO_INCREMENT field for the former.您需要一种方法来跟踪添加和更改的数据,例如为后者使用“修改”日期字段,或为前者使用AUTO_INCREMENT字段。
  2. you need space to have two copies of your table on the database.您需要空间才能在数据库上拥有两个表副本。
  3. you need a time period where alterations to the table won't get too far ahead of a snapshot您需要一个时间段,在该时间段内对表的更改不会比快照提前太多

The basic technique is as you suggested, ie using an INSERT INTO... SELECT... .基本技术如您所建议的那样,即使用INSERT INTO... SELECT... At least you're in front because you're starting with an InnoDB table, so the SELECT won't block.至少你在前面,因为你从 InnoDB 表开始,所以SELECT不会阻塞。 I recommend doing the ALTER TABLE on the new, empty table, which will save MySQL copying all the data again, which will mean you need to list all the fields correctly in the INSERT INTO... SELECT... statement.我建议在新的空表上执行ALTER TABLE ,这将保存 MySQL 再次复制所有数据,这意味着您需要在INSERT INTO... SELECT...语句中正确列出所有字段。 Then you can do a simple RENAME statement to swap it over.然后你可以做一个简单的RENAME语句来交换它。 Then you need to do another INSERT INTO... SELECT... WHERE... and perhaps an UPDATE... INNER JOIN... WHERE... to grab all the modified data.然后你需要再做一次INSERT INTO... SELECT... WHERE...也许还有一个UPDATE... INNER JOIN... WHERE...来获取所有修改过的数据。 You need to do the INSERT and UPDATE quickly or your code will starting adding new rows and updates to your snapshot which will interfere with your update.您需要快速执行INSERTUPDATE ,否则您的代码将开始向快照添加新行和更新,这干扰您的更新。 (You won't have this problem if you can put your app into maintenence mode for a few minutes from before the RENAME .) (如果您可以在RENAME之前将应用程序置于维护模式几分钟,则不会出现此问题。)

Apart from that, there are some key and buffer related settings you can change for just one session that may help the main data move.除此之外,您可以只为一个 session 更改一些键和缓冲区相关设置,这可能有助于主数据移动。 Things like read_rnd_buffer_size and read_buffer_size would be useful to increase.增加read_rnd_buffer_sizeread_buffer_size之类的东西会很有用。

  1. Setup slave设置从站
  2. Stop replication.停止复制。
  3. Make ALTER on slave在奴隶上进行 ALTER
  4. Let slave catch up the master让奴隶赶上主人
  5. swap master and slave, so slave becomes production server with changed structure and minimum downtime交换主机和从机,因此从机成为具有更改结构和最小停机时间的生产服务器

Unfortunately, this is not always as simple as staticsan leads on in his answer.不幸的是,这并不总是像staticsan在他的回答中提到的那样简单。 Creating the new table while online and moving the data over is easy enough, and doing the cleanup while in maintenance mode is also do-able enough, however, the Mysql RENAME operation automatically manipulates any foreign key references to your old table.在线创建新表并移动数据很容易,并且在维护模式下进行清理也很可行,但是,Mysql RENAME 操作会自动操作对旧表的任何外键引用。 What this means is that any foreign key references to the original table will still point to whatever you rename the table to.这意味着对原始表的任何外键引用仍将指向您将表重命名为的任何内容。

So, if you have any foreign key references to the table you're trying to alter you're stuck either altering those tables to replace the reference to your new table, or worse if that table is large you have to repeat the process with large table number two.因此,如果您对要更改的表有任何外键引用,那么您要么更改这些表以替换对新表的引用,要么更糟的是,如果该表很大,您必须重复该过程表二。

Another approach that has worked for us in the past has been to juggle a set of Mysql replicas handling the alter.过去对我们有用的另一种方法是使用一组 Mysql 副本来处理变更。 I'm not the best person to speak to the process, but it basically consists of breaking replication to one slave, running the patch on that instance, turning replication back on once the alter table is completed so that it catches up on replication.我不是谈论这个过程的最佳人选,但它基本上包括中断复制到一个从属,在该实例上运行补丁,一旦完成变更表就重新打开复制,以便它赶上复制。 Once the replication catches up, you put the site into maintenance mode (if necessary) to switch from your master to this new patched slave as the new master database.一旦复制赶上,您将站点置于维护模式(如有必要)以从您的主数据库切换到这个新的修补过的从属数据库作为新的主数据库。

The only thing I can't remember is exactly when you point the other slaves at the new master so that they also get the alter applied.我不记得的唯一一件事是你将其他奴隶指向新主人的确切时间,以便他们也得到应用。 One caveat to this process, we typically use this to roll alter patches before the code needs the change, or after the code has changed to no longer reference the columns/keys.对此过程有一个警告,我们通常在代码需要更改之前或在代码更改为不再引用列/键之后使用它来滚动更改补丁。

I tested various strategies to speed up one alter table.我测试了各种策略来加速一张变更表。 Eventually I got about 10x speed increase in my particular case.最终,在我的特定情况下,我的速度提高了大约 10 倍。 The results may or may not apply to your situation.结果可能适用于您的情况,也可能不适用于您的情况。 However, based on this I would suggest experimenting with InnoDB log file/buffer size parameters.但是,基于此,我建议尝试使用 InnoDB 日志文件/缓冲区大小参数。

In short, only increasing innodb_log_file_size and innodb_log_buffer_size had a measurable effect (Be careful! Changing innodb_log_file_size is risky . Look below for more info).简而言之,只有增加 innodb_log_file_size 和 innodb_log_buffer_size 才会产生可衡量的效果(小心!更改innodb_log_file_size 是有风险的。请参阅下文了解更多信息)。

Based on the rough write data rate (iostat) and cpu activity the bottleneck was io based, but not data throughput.根据粗略的写入数据速率 (iostat) 和 cpu 活动,瓶颈是基于 io,但不是数据吞吐量。 In the faster 500s runs the write throughput is at least in the same ballpark that you would expect from the hard disk.在更快的 500 秒运行中,写入吞吐量至少与您对硬盘的预期相同。

Tried performance optimizations:尝试了性能优化:

Changing innodb_log_file_size can be dangerous.更改 innodb_log_file_size 可能很危险。 See http://www.mysqlperformanceblog.com/2011/07/09/how-to-change-innodb_log_file_size-safely/ The technique (file move) explained in the link worked nicely in my case.请参阅http://www.mysqlperformanceblog.com/2011/07/09/how-to-change-innodb_log_file_size-safely/链接中解释的技术(文件移动)在我的案例中效果很好。

Also see http://www.mysqlperformanceblog.com/2007/11/03/choosing-innodb_buffer_pool_size/ and http://www.mysqlperformanceblog.com/2008/11/21/how-to-calculate-a-good-innodb-log-file-size/ for information about innodb and tuning log sizes.另见http://www.mysqlperformanceblog.com/2007/11/03/choosing-innodb_buffer_pool_size/http://www.mysqlperformanceblog.com/2008/11/good-21/how-to-calculate-innoa -log-file-size/有关 innodb 和调整日志大小的信息。 One drawback of larger log files is longer recovery time after crash.较大日志文件的一个缺点是崩溃后的恢复时间较长。

Test runs and rough timings:试运行和粗略的时间安排:

  • The simple load data to a freshly createad table: 6500s将数据简单加载到新创建的表中:6500s
  • load data w.加载数据 w。 innodb_log_file_size=200M, innodb_log_buffer_size=8M, innodb_buffer_pool_size=2200M, autocommit= 0; innodb_log_file_size=200M,innodb_log_buffer_size=8M,innodb_buffer_pool_size=2200M,自动提交= 0; unique_checks=0, foreign_key_checks=0: 500s unique_checks=0, foreign_key_checks=0: 500s
  • load data w.加载数据 w。 innodb_log_file_size=200M, innodb_log_buffer_size=8M: 500s innodb_log_file_size=200M, innodb_log_buffer_size=8M: 500s
  • Equivalent straight alter table w.等效的直接更改表 w。 datainnodb_log_file_size=200M, innodb_log_buffer_size=8M: 500s datainnodb_log_file_size=200M, innodb_log_buffer_size=8M: 500s

Testing details : Table: InnoDB, 6M rows, 2.8G on disk, single file (innodb_file_per_table option), primary key is 1 integer, +2 unque constraints/indices, 8 columns, avg.测试详细信息:表:InnoDB,6M 行,2.8G 磁盘,单个文件(innodb_file_per_table 选项),主键是 1 integer,+2 unque 约束/索引,8 列,平均。 row length 218 bytes.行长 218 字节。 Server: Ubuntu 12.04, x86_64, virtual machine, 8 cores, 16GB, sata consumer grade disk, no raid, no database activity, minuscule other process activity, minuscule activity in other and much smaller virtual machines.服务器:Ubuntu 12.04,x86_64,虚拟机,8 核,16GB,sata 消费级磁盘,无 raid,无数据库活动,其他进程活动很少,其他和更小的虚拟机中的活动很少。 Mysql 5.1.53. Mysql 5.1.53。 The initial server config is pretty default except for increased innodb_buffer_pool_size of 1400M.初始服务器配置是非常默认的,除了增加了 1400M 的 innodb_buffer_pool_size。 The alter table adds 2 small columns. alter 表添加了 2 个小列。 I didn't clock the raw alter table, but instead experimented with equivalent load data infile statement, finally I did the straight alter table and got comparable result.我没有对原始的 alter table 计时,而是尝试了等效的 load data infile 语句,最后我做了直接的 alter table 并得到了可比较的结果。

This question is related to at least following questions:这个问题至少与以下问题有关:

I really don't know how to optimize that, but it's usually a good practice to put the site in offline mode before doing such updates.我真的不知道如何优化它,但在进行此类更新之前将站点置于离线模式通常是一个好习惯。

Then, you can run your DB scripts at, say, 3 am, so it shouldn't matter much if downtime's a big longer than ideal.然后,您可以在凌晨 3 点运行您的 DB 脚本,因此如果停机时间比理想情况长很多,这无关紧要。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM