TL Tech Logo
We Solve Your Problems. Seriously, we actually do.
Home
About Us
Services
Stories
Contact Us
Tools
Info Center
  • Sections

    • Code
    • Howto
    • Tips
  • Recent Entries

    • Recovering from a Broken Partition Table
    • RewriteRule in htaccess vs httpd.conf
    • Running PHP through mod_fcgid
    • How They Got Your Password
    • Installing mod_reqtimeout on cPanel
    • Following the Hacker — passwords
    • Using Nginx as a reverse-proxy
    • Dead-simple templates in PHP
    • Tell-a-friend SPAM
    • PHP mail via SMTP

Transferring a MySQL database via SSH

Normally the preferred way to transfer a MySQL database is to use mysqldump to create a database backup, possibly compress it with gzip, transfer that backup file to the new server, and then load it using the mysql command-line tool.

But what if you wanted to do it all in one step? Certainly it’s better to have the backup file around in case you need to repeat the process or in case something goes wrong. But as part of a recent job, I needed to do a transfer late at night that I knew based on previous experience would take about 6 hours to complete, and staying awake to babysit the process wasn’t a reasonable option.

So can you do it all with a single command? Certainly! Again, our old friend bash to the rescue. For reference, here’s the command to create the database backup:

mysqldump sourcedb > backup.sql

And here’s the command (on the destination server) to restore the backup:

mysql destdb < backup.sql

Obviously you can chain the two like this:

mysqldump sourcedb | mysql destdb

But that requires that they both be on the same server. Assuming one of the servers is listening on the Internet, you can connect to it remotely using the --host= parameter. But in this case we have to transfer over SSH. So lets take advantage of SSH’s stdin/stdout piping ability to make this easy. On the destination server, just do this:

mysql destdb < <(ssh sourceserver.example.com "mysqldump sourcedb")

A little confusing? Let’s break it down. First we run mysql on the destination server and select the database named “destdb”. Then we pipe to that program some input (the < operator). But instead of sending a file, we run a command and send its output. That’s what the notion <( command ) means. It’s a bit like the pipe (|) operator but slightly more flexible. The command we run is the ssh command, and we pass to it a string that we want to execute on the remote server; in this case mysqldump sourcedb.

The end result is that the destination server opens up an SSH connection to the source server, runs mysqldump sourcedb on it, and sends the result to the command mysql destdb on the destination server. Pretty slick, right? Could I have typed it instead like this?

ssh sourceserver.example.com "mysqldump sourcedb" | mysql destdb

Yes, probably. However, I with complex commands I have better luck with the < <( cmd ) syntax instead of using the | operator. The parsing and execution mechanics are slightly different between the two, and I find this preferred method more often works as expected.

Oh, but what about compression? We could certainly pipe the mysqldump output to gzip, and the restore input through gunzip, but that’s a little over-complicated. Instead I’m just going to ask SSH to handle compression transparently. The -C flag requests that compression be used (if it isn’t being used already). Also, I wanted to know how long it actually took, so I added the time command to the very beginning. Here’s the the final revised result, and the command I actually used for the job I mentioned:

time mysql destdb < <(ssh -C sourceserver.example.com "mysqldump sourcedb")

Note that MySQL authentication details are left out. You can easily make authentication implicit (and therefore leave them out completely from commands like this) by putting the details in a .my.cnf file in your home directory.

© 2006-2014 TL Tech Services LLC. All rights reserved. Contact us to inquire about republishing rights.