One of the routine tasks for a DBA is MySQL renaming database schemas, and as such MySQL added a command to carry out that purpose called “RENAME DATABASE <database_name>”. However, this command just made it through a few minor releases before being discontinued (from MySQL 5.1.7 to 5.1.23). Here’s a link to the reference manual regarding the command http://dev.mysql.com/doc/refman/5.1/en/rename-database.html. Vadim wrote a MySQL Performance Blog post about this a few years ago where he mentions the dangerous nature of this command – that post was appropriately headlined, “Dangerous Command.” Today we will see what are the ways in which a database schema can be renamed and which of them is the quickest.
Method 1: A well-known method for renaming database schema is by dumping the schema using Mysqldump and restoring it in another schema, and then dropping the old schema (if needed).
|
1 |
[root@percona ~]# mysqldump emp > emp.out<br>[root@percona ~]# mysql -e "CREATE DATABASE employees;"<br>[root@percona ~]# mysql employees < emp.out <br>[root@percona ~]# mysql -e "DROP DATABASE emp;" |
Although the above method is easy, it is time and space consuming. What if the schema is more than a 100GB? There are methods where you can pipe the above commands together to save on space, however, it will not save time.
To remedy such situations, there is another quick method to rename schemas, however, some care must be taken while doing it.
Method 2: MySQL has a very good feature for renaming tables that even works across different schemas. This rename operation is atomic and no one else can access the table while it is being renamed. This takes a short time to complete since changing a table’s name or its schema is only a metadata change. Here is the procedural approach at doing the rename:
If there are views, triggers, functions, stored procedures in the schema, those will need to be recreated too. MySQL’s “RENAME TABLE” fails if there are triggers exists on the tables. To remedy this we can do the following things :
1) Dump the triggers, events and stored routines in a separate file. This is done using -E, -R flags (in addition to -t -d which dumps the triggers) to the mysqldump command. Once triggers are dumped, we will need to drop them from the schema, for RENAME TABLE command to work.
|
1 |
$ mysqldump <old_schema_name> -d -t -R -E > stored_routines_triggers_events.out |
2) Generate a list of only “BASE” tables. These can be found using a query on information_schema.TABLES table.
|
1 |
mysql> select TABLE_NAME from information_schema.tables where table_schema='<old_schema_name>' and TABLE_TYPE='BASE TABLE'; |
3) Dump the views in an out file. Views can be found using a query on the same information_schema.TABLES table.
|
1 |
mysql> select TABLE_NAME from information_schema.tables where table_schema='<old_schema_name>' and TABLE_TYPE='VIEW';<br>$ mysqldump <database> <view1> <view2> … > views.out |
4) Drop the triggers on the current tables in the old_schema.
|
1 |
mysql> DROP TRIGGER <trigger_name>;<br>... |
5) Restore the above dump files once all the “Base” tables found in step #2 are renamed.
|
1 |
mysql> RENAME TABLE <old_schema>.table_name TO <new_schema>.table_name;<br>...<br>$ mysql <new_schema> < views.out<br>$ mysql <new_schema> < stored_routines_triggers_events.out |
Intricacies with above methods :
Although “method 2” seems a bit more complicated than the “method 1”, this is totally scriptable. A simple bash script to carry out the above steps in proper sequence can help you save space and time while renaming database schemas next time.
We on the Percona Remote DBA team have written a script called “rename_db” that works in the following way :
|
1 |
[root@percona ~]# /tmp/rename_db<br>rename_db <server> <database> <new_database> |
To demonstrate the use of this script, we used a sample schema “emp”, created test triggers, stored routines on that schema. We will try to rename the database schema using the script, which takes some seconds to complete as opposed to time-consuming dump/restore method.
|
1 |
mysql> show databases;<br>+--------------------+<br>| Database |<br>+--------------------+<br>| information_schema |<br>| emp |<br>| mysql |<br>| performance_schema |<br>| test |<br>+--------------------+ |
|
1 |
[root@percona ~]# time /tmp/rename_db localhost emp emp_test<br>create database emp_test DEFAULT CHARACTER SET latin1<br>drop trigger salary_trigger<br>rename table emp.__emp_new to emp_test.__emp_new<br>rename table emp._emp_new to emp_test._emp_new<br>rename table emp.departments to emp_test.departments<br>rename table emp.dept to emp_test.dept<br>rename table emp.dept_emp to emp_test.dept_emp<br>rename table emp.dept_manager to emp_test.dept_manager<br>rename table emp.emp to emp_test.emp<br>rename table emp.employees to emp_test.employees<br>rename table emp.salaries_temp to emp_test.salaries_temp<br>rename table emp.titles to emp_test.titles<br>loading views<br>loading triggers, routines and events<br>Dropping database emp<br><br>real 0m0.643s<br>user 0m0.053s<br>sys 0m0.131s |
|
1 |
mysql> show databases;<br>+--------------------+<br>| Database |<br>+--------------------+<br>| information_schema |<br>| emp_test |<br>| mysql |<br>| performance_schema |<br>| test |<br>+--------------------+ |
As you can see in the above output the database schema “emp” was renamed to “emp_test” in less than a second.
Lastly, we are happy to share the script we used above for “method 2”.
|
1 |
#!/bin/bash<br># Copyright 2013 Percona LLC and/or its affiliates<br>set -e<br>if [ -z "$3" ]; then<br> echo "rename_db <server> <database> <new_database>"<br> exit 1<br>fi<br>db_exists=`mysql -h $1 -e "show databases like '$3'" -sss`<br>if [ -n "$db_exists" ]; then<br> echo "ERROR: New database already exists $3"<br> exit 1<br>fi<br>TIMESTAMP=`date +%s`<br>character_set=`mysql -h $1 -e "show create database $2G" -sss | grep ^Create | awk -F'CHARACTER SET ' '{print $2}' | awk '{print $1}'`<br>TABLES=`mysql -h $1 -e "select TABLE_NAME from information_schema.tables where table_schema='$2' and TABLE_TYPE='BASE TABLE'" -sss`<br>STATUS=$?<br>if [ "$STATUS" != 0 ] || [ -z "$TABLES" ]; then<br> echo "Error retrieving tables from $2"<br> exit 1<br>fi<br>echo "create database $3 DEFAULT CHARACTER SET $character_set"<br>mysql -h $1 -e "create database $3 DEFAULT CHARACTER SET $character_set"<br>TRIGGERS=`mysql -h $1 $2 -e "show triggersG" | grep Trigger: | awk '{print $2}'`<br>VIEWS=`mysql -h $1 -e "select TABLE_NAME from information_schema.tables where table_schema='$2' and TABLE_TYPE='VIEW'" -sss`<br>if [ -n "$VIEWS" ]; then<br> mysqldump -h $1 $2 $VIEWS > /tmp/${2}_views${TIMESTAMP}.dump<br>fi<br>mysqldump -h $1 $2 -d -t -R -E > /tmp/${2}_triggers${TIMESTAMP}.dump<br>for TRIGGER in $TRIGGERS; do<br> echo "drop trigger $TRIGGER"<br> mysql -h $1 $2 -e "drop trigger $TRIGGER"<br>done<br>for TABLE in $TABLES; do<br> echo "rename table $2.$TABLE to $3.$TABLE"<br> mysql -h $1 $2 -e "SET FOREIGN_KEY_CHECKS=0; rename table $2.$TABLE to $3.$TABLE"<br>done<br>if [ -n "$VIEWS" ]; then<br> echo "loading views"<br> mysql -h $1 $3 < /tmp/${2}_views${TIMESTAMP}.dump<br>fi<br>echo "loading triggers, routines and events"<br>mysql -h $1 $3 < /tmp/${2}_triggers${TIMESTAMP}.dump<br>TABLES=`mysql -h $1 -e "select TABLE_NAME from information_schema.tables where table_schema='$2' and TABLE_TYPE='BASE TABLE'" -sss`<br>if [ -z "$TABLES" ]; then<br> echo "Dropping database $2"<br> mysql -h $1 $2 -e "drop database $2"<br>fi<br>if [ `mysql -h $1 -e "select count(*) from mysql.columns_priv where db='$2'" -sss` -gt 0 ]; then<br> COLUMNS_PRIV=" UPDATE mysql.columns_priv set db='$3' WHERE db='$2';"<br>fi<br>if [ `mysql -h $1 -e "select count(*) from mysql.procs_priv where db='$2'" -sss` -gt 0 ]; then<br> PROCS_PRIV=" UPDATE mysql.procs_priv set db='$3' WHERE db='$2';"<br>fi<br>if [ `mysql -h $1 -e "select count(*) from mysql.tables_priv where db='$2'" -sss` -gt 0 ]; then<br> TABLES_PRIV=" UPDATE mysql.tables_priv set db='$3' WHERE db='$2';"<br>fi<br>if [ `mysql -h $1 -e "select count(*) from mysql.db where db='$2'" -sss` -gt 0 ]; then<br> DB_PRIV=" UPDATE mysql.db set db='$3' WHERE db='$2';"<br>fi<br>if [ -n "$COLUMNS_PRIV" ] || [ -n "$PROCS_PRIV" ] || [ -n "$TABLES_PRIV" ] || [ -n "$DB_PRIV" ]; then<br> echo "IF YOU WANT TO RENAME the GRANTS YOU NEED TO RUN ALL OUTPUT BELOW:"<br> if [ -n "$COLUMNS_PRIV" ]; then echo "$COLUMNS_PRIV"; fi<br> if [ -n "$PROCS_PRIV" ]; then echo "$PROCS_PRIV"; fi<br> if [ -n "$TABLES_PRIV" ]; then echo "$TABLES_PRIV"; fi<br> if [ -n "$DB_PRIV" ]; then echo "$DB_PRIV"; fi<br> echo " flush privileges;"<br>fi |