{"id":60,"date":"2012-09-20T11:59:49","date_gmt":"2012-09-20T09:59:49","guid":{"rendered":"http:\/\/www.olivierdoucet.info\/blog\/?p=60"},"modified":"2012-11-28T10:44:28","modified_gmt":"2012-11-28T08:44:28","slug":"migrating-mysql-database-server","status":"publish","type":"post","link":"https:\/\/www.olivierdoucet.info\/blog\/2012\/09\/20\/migrating-mysql-database-server\/","title":{"rendered":"Migrating a MySQL database to another server"},"content":{"rendered":"<p>A customer asked me to copy a whole database from one mysql server to another.<br \/>\nA few years ago, I would go with the classic mysqldump + import solution, but it is very slow, especially the import part (because MySQL insert buffer is monothread). One can also use mysqlimport (LOAD DATA INFILE), but it is still quite slow&#8230; When using a standard SQL dump, I measured a speed of 1MB\/sec for reimport &#8230; Quite long if you have gigabytes of data !<\/p>\n<p>So I tested xtrabackup for this. This tool is already in heavy tests internally but to my mind, is not quite ready for production. But let&rsquo;s try this for this specific task of migrating a database.<\/p>\n<p>First, you need to install xtrabackup and all other binaries included (especially xbstream). You&rsquo;ll also need at least version 5.5.25 on remote host (you&rsquo;ll see why). And last but not least, InnoDB must run with<\/p>\n<pre>innodb_file_per_table=1<\/pre>\n<p>In this blog post, I&rsquo;ll name \u00ab\u00a0server A\u00a0\u00bb the source, and \u00ab\u00a0server B\u00a0\u00bb the destination.<br \/>\nMy SQL data is stored in \/srv\/mysql.<br \/>\nOn server B, create a destination folder, for example \/tmp\/test<br \/>\nThis is because you need to import data to a temporary directory, where there is .<br \/>\nOn server A, launch the following command :<\/p>\n<pre>time innobackupex --export --databases 'mydb' --no-lock \\\r\n--stream=xbstream --tmpdir=\/tmp --use-memory=128MB \\\r\n--user=backup --password=XXXXX --parallel=4 \/srv\/mysql \\\r\n| ssh root@10.0.0.224 \"xbstream --directory=\/tmp\/test -x\"<\/pre>\n<p>A few explanations :<\/p>\n<ul>\n<li>&#8211;export : add specific data that would be useful for reimport<\/li>\n<li>&#8211;databases : specify database to copy.<\/li>\n<li>&#8211;no-lock : by default, a FLUSH TABLES WITH READ LOCK is emitted, to ensure the whole backup is consistant. I chose not to use it, as I do not care about binary log position of the backup (used in replication).<\/li>\n<li>&#8211;stream=xbstream : use xbstream as streaming software (more powerful than tar)<\/li>\n<li>&#8211;use-memory : memory that can be used for several tasks<\/li>\n<li>&#8211;parallel=4 : dump 4 tables in parallel<\/li>\n<li>I pipe stream to ssh and execute xbstream on the remote host.<\/li>\n<li>I do not use &#8211;compress as network is not a limit in my case.<\/li>\n<\/ul>\n<p>Dump is complete ! I achieved a rate of 11.3 MB\/sec with this : far better than mysqldump \/ mysqlimport ! I&rsquo;m sure we can be faster by tuning a few things.<br \/>\nTake care of file owner : copy was done with root, but I&rsquo;m sure your mysql data is owned by someone else (or it should be !).<\/p>\n<p>Then, prepare data for export :<\/p>\n<pre>xtrabackup_55 --prepare --export --target-dir=\/tmp\/test<\/pre>\n<p>This will \u00ab\u00a0prepare\u00a0\u00bb data (applying innodb log &#8230;), and especially will create .exp files that contains table metadata. This is mandatory for import in the next step !<\/p>\n<p>We can see xtrabackup working :<\/p>\n<pre>[...]\r\nxtrabackup: export option is specified.\r\nxtrabackup: export metadata of table 'mydb\/performer' to file `.\/mydb\/performer.exp` (1 indexes)\r\nxtrabackup: name=PRIMARY, id.low=443, page=3\r\n[...]<\/pre>\n<p>Ok, now we need to create the new database, and import scheme.<br \/>\nOn server B, do :<\/p>\n<pre>CREATE DATABASE mydb;<\/pre>\n<p>We also need a temporary database (you&rsquo;ll see why later &#8230;)<\/p>\n<pre>CREATE DATABASE test;<\/pre>\n<p>Then, we have two choices :<\/p>\n<ol>\n<li>create all tables, discard all tablespaces, and import all<\/li>\n<li>do the same operation, but for each table<\/li>\n<\/ol>\n<p>I first thought that there were a bug with method 1 (<a title=\"expand_import failure when importing multiple tablespaces\" href=\"https:\/\/bugs.launchpad.net\/percona-server\/+bug\/1052960\" target=\"_blank\">https:\/\/bugs.launchpad.net\/percona-server\/+bug\/1052960<\/a>) but it appears this bug is also hitting if you do method 2. Nevertheless, I&rsquo;ve created a script that handle method 2 all by itself.<\/p>\n<p><strong>NOTE THAT YOU NEED<\/strong> Percona Server 5.5.25 v27.1 if you don&rsquo;t want to hit the bug I was talking about. This bug crashes MySQL, and leaves it in a state where it cannot start again &#8230; You&rsquo;ve been warned.<br \/>\nFirst, let&rsquo;s dump all tables schemas. On server A, do :<\/p>\n<pre>mysqldump --routines --triggers --single-transaction --no-data \\\r\n--host=serverA --user=root --password=xxx \\\r\n--result-file=schemas.sql mydb;<\/pre>\n<p>And import it ON THE TEMPORARY DATABASE :<\/p>\n<pre>cat schemas.sql |mysql -uroot -hSERVER_B -p test<\/pre>\n<p>Execute following statement in MySQL (server B) to tell MySQL that we will import data :<\/p>\n<pre>SET GLOBAL innodb_import_table_from_xtrabackup = 1;\r\n\/**\u00a0If you have a version &lt; 5.5.10, despite what I just\r\n * said about minimum version required, query is : **\/\r\nSET GLOBAL innodb_expand_import = 1;<\/pre>\n<p>Then, let&rsquo;s use following bash script :\u00a0<a href=\"https:\/\/www.olivierdoucet.info\/blog\/wp-content\/uploads\/2012\/09\/expand_import.sh.txt\">https:\/\/www.olivierdoucet.info\/blog\/wp-content\/uploads\/2012\/09\/expand_import.sh.txt<\/a><\/p>\n<p><em>A few explanations :<\/em><\/p>\n<p>For each table, we do the following :<\/p>\n<pre>ALTER TABLE xxx DISCARD TABLESPACE;\r\nmv xxx.ibd xxx.exp \/srv\/mysql\/mydb;\r\nALTER TABLE xxx IMPORT TABLESPACE;<\/pre>\n<p>This bash script is using .my.cnf file in your user dir for credentials (or other default values). Please ensure you have access to the destination database with these credentials.<br \/>\n<strong>ALL STEPS ARE REQUIRED<\/strong>. If you miss something (the set global, chown, chmod &#8230;) you&rsquo;ll probably get an error (like &lsquo;got error -1 from storage engine&rsquo;). At this point, you&rsquo;d better start the process over (and drop the destination database, which is incomplete).<\/p>\n<p>If import works, you can see lines like this in log :<\/p>\n<pre>[...]\r\nInnoDB: Import: The extended import of mydb\/mytable is being started.\r\nInnoDB: Import: 2 indexes have been detected.\r\nInnoDB: Progress in %: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 done.\r\n[...]<\/pre>\n<p>Note that when importing huge InnoDB tables, there is (was) a lock on the dictionary while scanning the ibd file (the data file), so it may lock the whole server &#8230; If operation took too long, server crashed. This is bug <a title=\"innodb crashes when importing big table using Expand Table Import\" href=\"https:\/\/bugs.launchpad.net\/percona-server\/+bug\/684829\" target=\"_blank\">https:\/\/bugs.launchpad.net\/percona-server\/+bug\/684829<\/a><br \/>\nThis bug has been fixed since, and as you need at least version 5.5.25, you should not hit this problem.<\/p>\n<p>&nbsp;<\/p>\n<h2>Conclusion<\/h2>\n<p>This method is 10 times faster than mysqldump \/ mysqlimport. But As you can see, there are huge risks and still bugs remaining. The dump part is really safe, so I would recommend you to first test the import on a dev server before doing this in production.<\/p>\n<p>Xtrabackup is really an amazing tool, but it still suffers somme nasty bugs. I&rsquo;m sure I&rsquo;ll use it in production in a few months, when it will be perfectly stable for all tasks.<\/p>\n<p>&nbsp;<\/p>\n","protected":false},"excerpt":{"rendered":"<p>A customer asked me to copy a whole database from one mysql server to another. A few years ago, I would go with the classic mysqldump + import solution, but it is very slow, especially the import part (because MySQL insert buffer is monothread). One can also use mysqlimport (LOAD DATA INFILE), but it is &hellip; <a href=\"https:\/\/www.olivierdoucet.info\/blog\/2012\/09\/20\/migrating-mysql-database-server\/\" class=\"more-link\">Continuer la lecture de <span class=\"screen-reader-text\">Migrating a MySQL database to another server<\/span> <span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[6],"tags":[36,52,53],"class_list":["post-60","post","type-post","status-publish","format-standard","hentry","category-php-sql","tag-mysql","tag-mysqldump","tag-percona"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v24.7 - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>Migrating a MySQL database to another server - My thoughts<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.olivierdoucet.info\/blog\/2012\/09\/20\/migrating-mysql-database-server\/\" \/>\n<meta property=\"og:locale\" content=\"fr_FR\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Migrating a MySQL database to another server - My thoughts\" \/>\n<meta property=\"og:description\" content=\"A customer asked me to copy a whole database from one mysql server to another. A few years ago, I would go with the classic mysqldump + import solution, but it is very slow, especially the import part (because MySQL insert buffer is monothread). One can also use mysqlimport (LOAD DATA INFILE), but it is &hellip; Continuer la lecture de Migrating a MySQL database to another server &rarr;\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.olivierdoucet.info\/blog\/2012\/09\/20\/migrating-mysql-database-server\/\" \/>\n<meta property=\"og:site_name\" content=\"My thoughts\" \/>\n<meta property=\"article:published_time\" content=\"2012-09-20T09:59:49+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2012-11-28T08:44:28+00:00\" \/>\n<meta name=\"author\" content=\"Olivier\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:creator\" content=\"@odoucet\" \/>\n<meta name=\"twitter:site\" content=\"@odoucet\" \/>\n<meta name=\"twitter:label1\" content=\"\u00c9crit par\" \/>\n\t<meta name=\"twitter:data1\" content=\"Olivier\" \/>\n\t<meta name=\"twitter:label2\" content=\"Dur\u00e9e de lecture estim\u00e9e\" \/>\n\t<meta name=\"twitter:data2\" content=\"5 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"WebPage\",\"@id\":\"https:\/\/www.olivierdoucet.info\/blog\/2012\/09\/20\/migrating-mysql-database-server\/\",\"url\":\"https:\/\/www.olivierdoucet.info\/blog\/2012\/09\/20\/migrating-mysql-database-server\/\",\"name\":\"Migrating a MySQL database to another server - My thoughts\",\"isPartOf\":{\"@id\":\"https:\/\/www.olivierdoucet.info\/blog\/#website\"},\"datePublished\":\"2012-09-20T09:59:49+00:00\",\"dateModified\":\"2012-11-28T08:44:28+00:00\",\"author\":{\"@id\":\"https:\/\/www.olivierdoucet.info\/blog\/#\/schema\/person\/d093cada9eddc4839cbae3de5c823a39\"},\"breadcrumb\":{\"@id\":\"https:\/\/www.olivierdoucet.info\/blog\/2012\/09\/20\/migrating-mysql-database-server\/#breadcrumb\"},\"inLanguage\":\"fr-FR\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/www.olivierdoucet.info\/blog\/2012\/09\/20\/migrating-mysql-database-server\/\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/www.olivierdoucet.info\/blog\/2012\/09\/20\/migrating-mysql-database-server\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/www.olivierdoucet.info\/blog\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Migrating a MySQL database to another server\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/www.olivierdoucet.info\/blog\/#website\",\"url\":\"https:\/\/www.olivierdoucet.info\/blog\/\",\"name\":\"My thoughts\",\"description\":\"En fran\u00e7ais and in english :)\",\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/www.olivierdoucet.info\/blog\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"fr-FR\"},{\"@type\":\"Person\",\"@id\":\"https:\/\/www.olivierdoucet.info\/blog\/#\/schema\/person\/d093cada9eddc4839cbae3de5c823a39\",\"name\":\"Olivier\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"fr-FR\",\"@id\":\"https:\/\/www.olivierdoucet.info\/blog\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/e75f77ace92ccb2dfab09f226ce3d329?s=96&d=mm&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/e75f77ace92ccb2dfab09f226ce3d329?s=96&d=mm&r=g\",\"caption\":\"Olivier\"}}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Migrating a MySQL database to another server - My thoughts","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/www.olivierdoucet.info\/blog\/2012\/09\/20\/migrating-mysql-database-server\/","og_locale":"fr_FR","og_type":"article","og_title":"Migrating a MySQL database to another server - My thoughts","og_description":"A customer asked me to copy a whole database from one mysql server to another. A few years ago, I would go with the classic mysqldump + import solution, but it is very slow, especially the import part (because MySQL insert buffer is monothread). One can also use mysqlimport (LOAD DATA INFILE), but it is &hellip; Continuer la lecture de Migrating a MySQL database to another server &rarr;","og_url":"https:\/\/www.olivierdoucet.info\/blog\/2012\/09\/20\/migrating-mysql-database-server\/","og_site_name":"My thoughts","article_published_time":"2012-09-20T09:59:49+00:00","article_modified_time":"2012-11-28T08:44:28+00:00","author":"Olivier","twitter_card":"summary_large_image","twitter_creator":"@odoucet","twitter_site":"@odoucet","twitter_misc":{"\u00c9crit par":"Olivier","Dur\u00e9e de lecture estim\u00e9e":"5 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"WebPage","@id":"https:\/\/www.olivierdoucet.info\/blog\/2012\/09\/20\/migrating-mysql-database-server\/","url":"https:\/\/www.olivierdoucet.info\/blog\/2012\/09\/20\/migrating-mysql-database-server\/","name":"Migrating a MySQL database to another server - My thoughts","isPartOf":{"@id":"https:\/\/www.olivierdoucet.info\/blog\/#website"},"datePublished":"2012-09-20T09:59:49+00:00","dateModified":"2012-11-28T08:44:28+00:00","author":{"@id":"https:\/\/www.olivierdoucet.info\/blog\/#\/schema\/person\/d093cada9eddc4839cbae3de5c823a39"},"breadcrumb":{"@id":"https:\/\/www.olivierdoucet.info\/blog\/2012\/09\/20\/migrating-mysql-database-server\/#breadcrumb"},"inLanguage":"fr-FR","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.olivierdoucet.info\/blog\/2012\/09\/20\/migrating-mysql-database-server\/"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/www.olivierdoucet.info\/blog\/2012\/09\/20\/migrating-mysql-database-server\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/www.olivierdoucet.info\/blog\/"},{"@type":"ListItem","position":2,"name":"Migrating a MySQL database to another server"}]},{"@type":"WebSite","@id":"https:\/\/www.olivierdoucet.info\/blog\/#website","url":"https:\/\/www.olivierdoucet.info\/blog\/","name":"My thoughts","description":"En fran\u00e7ais and in english :)","potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.olivierdoucet.info\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"fr-FR"},{"@type":"Person","@id":"https:\/\/www.olivierdoucet.info\/blog\/#\/schema\/person\/d093cada9eddc4839cbae3de5c823a39","name":"Olivier","image":{"@type":"ImageObject","inLanguage":"fr-FR","@id":"https:\/\/www.olivierdoucet.info\/blog\/#\/schema\/person\/image\/","url":"https:\/\/secure.gravatar.com\/avatar\/e75f77ace92ccb2dfab09f226ce3d329?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/e75f77ace92ccb2dfab09f226ce3d329?s=96&d=mm&r=g","caption":"Olivier"}}]}},"_links":{"self":[{"href":"https:\/\/www.olivierdoucet.info\/blog\/wp-json\/wp\/v2\/posts\/60","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.olivierdoucet.info\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.olivierdoucet.info\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.olivierdoucet.info\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.olivierdoucet.info\/blog\/wp-json\/wp\/v2\/comments?post=60"}],"version-history":[{"count":5,"href":"https:\/\/www.olivierdoucet.info\/blog\/wp-json\/wp\/v2\/posts\/60\/revisions"}],"predecessor-version":[{"id":62,"href":"https:\/\/www.olivierdoucet.info\/blog\/wp-json\/wp\/v2\/posts\/60\/revisions\/62"}],"wp:attachment":[{"href":"https:\/\/www.olivierdoucet.info\/blog\/wp-json\/wp\/v2\/media?parent=60"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.olivierdoucet.info\/blog\/wp-json\/wp\/v2\/categories?post=60"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.olivierdoucet.info\/blog\/wp-json\/wp\/v2\/tags?post=60"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}