views:

3104

answers:

6

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. Our largest tables have 150 million records, largest table file is 50G. All tables are InnoDB, and it was set up as one big data file (instead of a file-per-table). We're running MySQL 5.0.46 on an 8 core machine, 16G memory and a RAID10 config.

I have some experience with MySQL tuning, but this usually focusses on reads or writes from multiple clients. 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).

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. 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?

A: 

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.

Seb
Yes -- we will bring down the site during night hours to do this task. But with such big tables the ALTER TABLE may run for many hours, and that is probably not going to fit into one night :(
schuilr
Maybe you'd want to run the script in a separate server with a dump of the production DB timing how much it takes. Hopefully, you'll get a nice surprise! :)
Seb
Yes, we're preparing a QA environment for this. Can't optimize without testing your optimizations ;)
schuilr
+2  A: 

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. 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.

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. 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.
  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, i.e. using an INSERT INTO ... SELECT .... At least you're in front because you're starting with an InnoDB table, so the SELECT won't block. 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. Then you can do a simple RENAME statement to swap it over. Then you need to do another INSERT INTO ... SELECT ... WHERE ... and perhaps an UPDATE ... INNER JOIN ... WHERE ... to grab all the modified data. 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. (You won't have this problem if you can put your app into maintenence mode for a few minutes from before the 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. Things like read_rnd_buffer_size and read_buffer_size would be useful to increase.

staticsan
+1  A: 

Unfortunately, this is not always as simple as staticsan leads on in his answer. 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. 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. 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.

RC
I hadn't thought of foreign keys. Where I was a DBA where I used the technique I described, we didn't use foreign keys at all as the application believed it and only it handled all of that.
staticsan
+1  A: 

Hi, drop a comment and let me know how your optomizations are going. I have a similiar problem with a db table 150GB in size, and can only really afford 10 mins of down time max.

So I'll be opting for running the update on the slave, switching over a VIP making it master, and then get the original master working as a slave to then do the alter table there as well.

Any optomizations would be helpfull though :)

+1  A: 
  1. Setup slave
  2. Stop replication.
  3. Make ALTER on slave
  4. Let slave catch up the master
  5. swap master and slave, so slave becomes production server with changed structure and minimum downtime
noonex
A: 

There's a tool called chronicdb that tries to minimize downtime because of the ALTER TABLE problem you describe. On the face of it, they seem to automate the steps you describe.

Gary
i read cronicdb's marketing blurb on their website. They only support PostgresQL.
Kevin