Change to innodb_file_per_table in MySQL securely


Why would you change

A lot of people experience problems with innodb and MySQL, if they do not set the tablespace to use a file per table. This is due to one single file holding all the data of your tables and databases, and this can cause a lot of problems. The ibdata1 file which holds the data can never get smaller, only bigger, and this is why you suddenly may have to change to innodb_file_per_table to keep your server alive.

There is a lot of other advantages like management, flexibility, storage opportunities, and ability to handle your data much easier in general.

Beside my other articles, this is on a CentOS machine.

Prepare

First of all we stop the services which can make your MySQL database inconsistent, in my case i shut down everything which have something to do with the web server (nginx/php-fpm/varnish):

$ /etc/init.d/php-fpm stop
$ /etc/init.d/nginx stop
$ /etc/init.d/varnish stop

Then i make sure there is no connections active, by checking open processes from php-fpm, and eventually log into your mysql prompt to look up some information (connections from status, and full processlist):

mysql> show status like '%onn%';
+--------------------------+--------+
| Variable_name            | Value  |
+--------------------------+--------+
| Aborted_connects         | 8      |
| Connections              | 124789 |
| Max_used_connections     | 42     |
| Ssl_client_connects      | 0      |
| Ssl_connect_renegotiates | 0      |
| Ssl_finished_connects    | 0      |
| Threads_connected        | 5      |
+--------------------------+--------+

mysql> show full processlist;
...

Dump all databases

When everything is OK, we dump our database with the mysqldump tool. Make sure you have space enough, for a lot of data. I dump to a file i call all.sql, with verbose output, so we know what is going on:

$ mysqldump --verbose -u root -p password --all-databases > all.sql

You could eventually use a & in the end to make the process run int he background.

When done, you should have your file in the directory you were running the mysqldump commando from:

$ ls -hl all.sql
-rw-r--r-- 1 root root 83G Jun 24 01:58 all.sql

Do the configuration

Now we close the MySQL service, so we are ready to do a bit of changes:

$ /etc/init.d/mysql stop

Now we enable innodb_file_per_table in our MySQL configuration by appending the following string into it. I use Vim, $ vi /etc/my.cnf:

innodb_file_per_table = 1

Change the name of your current datadir (/var/lib/mysql), which holds all your MySQL data. When just changing the name of the directory, we can always go back. I call mine mysql_old:

$ mv /var/lib/mysql /var/lib/mysql_old

Now we are ready to turn on MySQL again:

$ /etc/init.d/mysql start

When MySQL is running again, we are ready to create the new datadir, and later restore from the dump. We will run the mysql_secure_installation tool, to make the directory:

$ mysql_secure_installation

To keep your MySQL users from your old data, copy the mysql-database from the old directory to the new:

$ cp -R /var/lib/mysql_old/mysql /var/lib/mysql/

Restore

With all your our old users etc. from the mysql-database, innodb_file_per_table enabled, the dump, and the old datadir for backup, your should be ready to restore your databases from the dump, into your new datadir with innodb_file_per_table.

Be sure nothing external is running when you restore, as i got problems with an external backup system.

To restore from the dump, run the following command:

$ mysql --verbose -u root -p password < all.sql 2> mysqldump_errors.log

What is happening is that we run it with verbose output, so we now what is going on, and then we write 2> output to a error-log. We specify that with 2>, and this is because 1> (or >) is for stdout, and 2> is for stderr. To merge these two, you have maybe seen this before: 2>&1. This will send stderr to stdout.

You could eventually use a & in the end to make the process run int he background.

To keep an eye open for what is going on, as a restore can take a very long time, you could use the following two lines (one to check the processlist with mysqladmin, and one to follow your error-log:

$ mysqladmin -u root -p password -i 1 processlist
$ tail -f mysqldump_errors.log

When the dump is ready, start all your services again:

$ /etc/init.d/mysqld start
$ /etc/init.d/php-fpm start
$ /etc/init.d/nginx start
$ /etc/init.d/varnish start

Alternative MySQL datadir

I needed to create an alternative datadir (nfs/iscsi), to have space for everything. In case you need to this aswell, this was how i did it:

First i stop all services i connection to this:

$ /etc/init.d/mysqld stop
$ /etc/init.d/php-fpm stop
$ /etc/init.d/nginx stop
$ /etc/init.d/varnish stop

Then i copy (recursively) everything from my datadir our to a new datadir:

$ cp -R /var/lib/mysql/* /var/lib/mysql.new/

Now we enable write our new datadir and socket location in our MySQL configuration by changing the following lines to your new destination. I use Vim, $ vi /etc/my.cnf:

datadir                 = /var/lib/mysql.new
socket                  = /var/lib/mysql.new/mysql.sock

If you get problems with your mysql.sock, create a symlink from your old dir to your new dir:

$ ln -s /var/lib/mysql/mysql.sock /var/lib/mysql.new/mysql.sock

That should be it. Start all your services again:

$ /etc/init.d/mysqld start
$ /etc/init.d/php-fpm start
$ /etc/init.d/nginx start
$ /etc/init.d/varnish start