{"id":492,"date":"2019-10-27T17:44:11","date_gmt":"2019-10-27T21:44:11","guid":{"rendered":"http:\/\/bhoey.com\/blog\/?p=492"},"modified":"2020-06-25T15:42:16","modified_gmt":"2020-06-25T19:42:16","slug":"complete-mysql-backups-with-mysqldump","status":"publish","type":"post","link":"https:\/\/bhoey.com\/blog\/complete-mysql-backups-with-mysqldump\/","title":{"rendered":"Complete MySQL Backups With Mysqldump"},"content":{"rendered":"<p>Searching around for instructions on how to properly backup a MySQL database will often lead you down a dark path filled with outdated, incomplete, or just plain incorrect examples. The official mysqldump documentation contains a wide variety of options and switches to tweak the final dump output, but most often you just want a complete backup of one or more databases for point-in-time snapshot. This post covers two example mysqldump usages that will work well for typical MySQL setups or as a baseline for more complex configurations.<\/p>\n<h3>Assumptions<\/h3>\n<ul>\n<li>MySQL 8.0 or MariaDB 10.4<\/li>\n<li>Only InnoDB tables<\/li>\n<li>MySQL\/MariaDB 'root' user access<\/li>\n<li>Database server is not part of a replication set<\/li>\n<\/ul>\n<h3>Backup Specific Database(s)<\/h3>\n<p>The following command performs a backup of a database named 'db1' on a given MySQL\/MariaDB instance.<\/p>\n<p>Beyond the obvious benefit of targeting the specific database(s) to backup, a key aspect of this approach is to avoid dumping the built-in 'mysql' database which includes database users and permissions that you may not want to overwrite in the database instance you restore the backup to.<\/p>\n<pre class=\"brush: plain; notranslate\">mysqldump \\\n    -u root \\\n    -p \\\n    --routines \\\n    --triggers \\\n    --events \\\n    --hex-blob \\\n    --skip-extended-insert \\\n    --complete-insert \\\n    --single-transaction \\\n    --databases db1 \\\n    --result-file=db1-bkup.sql\n<\/pre>\n<p>The following options are used:<\/p>\n<ul>\n<li><b>-u root<\/b> - Use root mysql user<\/li>\n<li><b>-p<\/b> - Prompt for password<\/li>\n<li><b>--routines<\/b> - Include stored procedures and functions<\/li>\n<li><b>--triggers<\/b> - Include triggers<\/li>\n<li><b>--events<\/b> - Include events<\/li>\n<li><b>--hex-blob<\/b> - Dump binary columns as hex. If you don't specify this switch, raw binary is written to the result file which is problematic and error-prone<\/li>\n<li><b>--skip-extended-insert<\/b> - Disable extended insert. This trade-off allows you to cherry-pick specific inserts out of the output file later at the expense of increased dump file size<\/li>\n<li><b>--complete-insert<\/b> - Include column names in insert statement. Similar trade-off to --skip-extended-insert above<\/li>\n<li><b>--single-transaction<\/b> - Perform dump within a transaction to prevent reading stale data that has changed in other concurrent sessions<\/li>\n<li><b>--databases db1<\/b> - Indicates the database(s) to backup<\/li>\n<li><b>--result-file<\/b> - Specifies the output file, it is common to see file redirection used instead (ie '&gt; bkup.sql'), however simple redirection may cause issues depending on the encoding, particularly on Windows systems<\/li>\n<\/ul>\n<h3>Backup All Databases<\/h3>\n<p>Alternatively, you can use a similar mysqldump usage to dump all the databases in a given instance. This can be handy if for example you DO want to restore everything including instance-wide users and permissions on restore (for example a test database instance that is wiped out and re-initialized).<\/p>\n<pre class=\"brush: plain; notranslate\">mysqldump \\\n    -u root \\\n    -p \\\n    --routines \\\n    --triggers \\\n    --events \\\n    --hex-blob \\\n    --skip-extended-insert \\\n    --complete-insert \\\n    --single-transaction \\\n    --all-databases \\\n    --result-file=all-dbs-bkup.sql\n<\/pre>\n<p>As you can see there are only two changes from the specific-database backup:<\/p>\n<ul>\n<li><b>--all-databases<\/b> - Include all databases, notably even system-specific ones like the 'mysql' database<\/li>\n<li><b>--result-file=all-dbs-bkup.sql<\/b> - Changed dump filename to reflect that it contains all databases<\/li>\n<\/ul>\n<h3>Testing<\/h3>\n<p>Spin up docker testbed:<\/p>\n<pre class=\"brush: plain; notranslate\">docker run --name mysqltest -e MYSQL_ROOT_PASSWORD=password -d mysql:8\n\ndocker container exec -it mysqltest \/bin\/bash\n\nmysql -u root -p\n<\/pre>\n<p>Initialize test table:<\/p>\n<pre class=\"brush: bensql; notranslate\">CREATE DATABASE db1;\n\nUSE db1;\n\nCREATE TABLE testtbl (\n        myInt INT PRIMARY KEY,\n        myBit BIT,\n     myDouble DOUBLE,\n       myDate DATE,\n       myText TEXT,\n       myBlob BLOB\n);\n\nINSERT INTO testtbl (myInt, myBit, myDouble,     myDate, myText, myBlob)\n             VALUES (1,         0,     12.3, '20201231',  'hey',  x'FF');\n\nINSERT INTO testtbl (myInt, myBit, myDouble,     myDate, myText, myBlob)\n             VALUES (2,         1,     45.6, '20220227',  'joe',  x'8F');\n\n<\/pre>\n<p>Dump database:<\/p>\n<pre class=\"brush: plain; notranslate\">mysqldump \\\n    -u root \\\n    -p \\\n    --routines \\\n    --triggers \\\n    --events \\\n    --hex-blob \\\n    --skip-extended-insert \\\n    --complete-insert \\\n    --single-transaction \\\n    --databases db1 \\\n    --result-file=db1-bkup.sql\n<\/pre>\n<p>Confirm row format:<\/p>\n<pre class=\"brush: bensql; notranslate\">grep INSERT db1-bkup.sql\nINSERT INTO `testtbl` (`myInt`, `myBit`, `myDouble`, `myDate`, `myText`, `myBlob`) VALUES (1,0x00,12.3,'2020-12-31','hey',0xFF);\nINSERT INTO `testtbl` (`myInt`, `myBit`, `myDouble`, `myDate`, `myText`, `myBlob`) VALUES (2,0x01,45.6,'2022-02-27','joe',0x8F);\n\n<\/pre>\n<p>You'll notice:<\/p>\n<ol>\n<li>The column names are explicitly listed<\/li>\n<li>Each insert statement is independently complete (ie not extended format)<\/li>\n<li>the BIT and BLOB types are represented as hex since they are binary column types<\/li>\n<\/ol>\n<h3>Restrictions<\/h3>\n<p>As indicated on the <a href=\"https:\/\/dev.mysql.com\/doc\/refman\/8.0\/en\/mysqldump.html#mysqldump-restrictions\">official documentation<\/a> mysqldump does not dump the following by default:<\/p>\n<ul>\n<li>The performance_schema or sys schema by default<\/li>\n<li>The INFORMATION_SCHEMA schema<\/li>\n<li>The InnoDB CREATE TABLESPACE statemets<\/li>\n<li>The NDB Cluster ndbinfo information database<\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>Searching around for instructions on how to properly backup a MySQL database will often lead you down a dark path filled with outdated, incomplete, or just plain incorrect examples. The official mysqldump documentation contains a wide variety of options and switches to tweak the final dump output, but most often you just want a complete backup of one or more databases for point-in-time snapshot. This post covers two example mysqldump usages that will work well for typical MySQL setups or as a baseline for more complex configurations.&nbsp;<a href=\"https:\/\/bhoey.com\/blog\/complete-mysql-backups-with-mysqldump\/\">[Continue&nbsp;reading...] <span class=\"screen-reader-text\">Complete MySQL Backups With Mysqldump<\/span><\/a><\/p>\n","protected":false},"author":2,"featured_media":835,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[59],"tags":[61,60,62],"_links":{"self":[{"href":"https:\/\/bhoey.com\/blog\/wp-json\/wp\/v2\/posts\/492"}],"collection":[{"href":"https:\/\/bhoey.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/bhoey.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/bhoey.com\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/bhoey.com\/blog\/wp-json\/wp\/v2\/comments?post=492"}],"version-history":[{"count":52,"href":"https:\/\/bhoey.com\/blog\/wp-json\/wp\/v2\/posts\/492\/revisions"}],"predecessor-version":[{"id":550,"href":"https:\/\/bhoey.com\/blog\/wp-json\/wp\/v2\/posts\/492\/revisions\/550"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/bhoey.com\/blog\/wp-json\/wp\/v2\/media\/835"}],"wp:attachment":[{"href":"https:\/\/bhoey.com\/blog\/wp-json\/wp\/v2\/media?parent=492"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/bhoey.com\/blog\/wp-json\/wp\/v2\/categories?post=492"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/bhoey.com\/blog\/wp-json\/wp\/v2\/tags?post=492"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}