Copiar / duplicar la base de datos sin usar mysqldump

427

Sin acceso local al servidor, ¿hay alguna forma de duplicar / clonar una base de datos MySQL (con contenido y sin contenido) en otra sin usar mysqldump?

Actualmente estoy usando MySQL 4.0.

jhornnes
fuente
12
¿Qué tiene de malo mysqldump?
Michael Mior
48
Asegúrese de no hacer esto: CREATE TABLE t2 SELECT * FROM t1;ya que perderá su información de índice, cualquier cosa especial como auto_increment, etc. muchos de los google para este tipo de tabla de copia lo llevarán a hacer esto y tendrá resultados no deseados .
John Hunt
59
Una pregunta fuera de tema obtiene 92 votos a favor y 37 favoritos. Pulgares arriba para tal pregunta fuera de tema. Pautas desactualizadas.
pal4life
25
El 100% está de acuerdo en que el "tema cerrado como incorrecto" está mal y que las líneas de culpabilidad deben actualizarse, se necesita más indulgencia, por lo que SO se dirige en la dirección equivocada. Es obvio que @will está completamente fuera de lugar, y debería tener sus privilegios de moderador eliminados; esta sola pregunta es prueba suficiente.
stolsvik
10
Cerrado como fuera de tema es 100% incorrecto. Esta es la pregunta exacta que tengo, y tiene una respuesta técnica bien definida que no se relaciona con la mera opinión. Creo que el moderador debe haber estado buscando palabras como "mejor" para encontrar preguntas para cerrar.
Sam Goldberg

Respuestas:

686

Puedo ver que dijiste que no querías usar mysqldump, pero llegué a esta página mientras buscaba una solución similar y otros podrían encontrarla también. Con eso en mente, aquí hay una manera simple de duplicar una base de datos desde la línea de comandos de un servidor de Windows:

  1. Cree la base de datos de destino utilizando MySQLAdmin o su método preferido. En este ejemplo, db2es la base de datos de destino, donde db1se copiará la base de datos de origen .
  2. Ejecute la siguiente instrucción en una línea de comando:

mysqldump -h [server] -u [user] -p[password] db1 | mysql -h [server] -u [user] -p[password] db2

Nota: NO hay espacio entre -py[password]

Rafe
fuente
108
El caso en contra de mysqldump es que tiene que haber una forma más rápida de serializar los datos en consultas, transmitir las consultas fuera del proceso y a través del tty nuevamente en el mismo proceso , volver a analizar las consultas y ejecutarlas como declaraciones. Eso suena terriblemente ineficiente e innecesario . No estamos hablando de cruzar entre maestros MySQL o cambiar motores de almacenamiento. Es sorprendente que no haya una transferencia binaria eficiente dentro del proceso.
Toddius Zho el
42
Si no desea guardar el texto sin formato de la contraseña en el historial de sus terminales, debe dividir el comando: de lo contrario mysqldump -h [server] -u [user] -p db1 > db1, mysql -h [server] -u [user] -p db2 < db1el mensaje de contraseña lo estropea, al menos para mí cuando usa masilla.
kapex
55
usar mysqldump y mysql de bash se vuelve mucho más simple si configura su archivo .my.cnf para almacenar sus archivos de usuario / host / contraseña
ErichBSchulz
44
mysqldump -u root -p -v db1 | mysql -u root -p db2y dos veces entrará pase
hlcs
66
Dios, ¿podría alguien explicarme por qué una pregunta que dice "sin mysqldump" tiene como primera respuesta una que usa mysqldump? con como, 6 veces más votos que el correcto ? vamos, SO ...
igorsantos07
135

Puede duplicar una tabla sin datos ejecutando:

CREATE TABLE x LIKE y;

(Consulte los documentos de MySQL CREATE TABLE )

Podría escribir una secuencia de comandos que tome la salida SHOW TABLESde una base de datos y copie el esquema en otra. Debería poder hacer referencia a nombres de esquema + tabla como:

CREATE TABLE x LIKE other_db.y;

En lo que respecta a los datos, también puede hacerlo en MySQL, pero no es necesariamente rápido. Después de crear las referencias, puede ejecutar lo siguiente para copiar los datos:

INSERT INTO x SELECT * FROM other_db.y;

Si está utilizando MyISAM, es mejor que copie los archivos de la tabla; Será mucho más rápido. Debería poder hacer lo mismo si está utilizando INNODB con espacios de tabla por tabla .

Si terminas haciendo un INSERT INTO SELECT, ¡asegúrate de desactivar temporalmente los índices con ALTER TABLE x DISABLE KEYS!

EDITAR Maatkit también tiene algunos scripts que pueden ser útiles para sincronizar datos. Puede que no sea más rápido, pero probablemente podría ejecutar sus scripts de sincronización en datos en vivo sin mucho bloqueo.

Gary Richardson
fuente
1
¿Es este trabajo para la tabla duplicada? desde que veo el comando es CREATE TABLE
GusDeCooL
44
Puedes hacer CREATE TABLE ... SELECT.
eggyal
3
Intenté copiar los archivos de la tabla de una base de datos MyISAM una vez, pero eso solo dañó la nueva base de datos. Probablemente sea malo, pero definitivamente no es una operación tan trivial como algunos dicen que es.
Johan Fredrik Varen
2
Este es un buen truco y soy un fanático, pero una nota importante: esto no transfiere ninguna restricción de clave externa (incluso las que son externas al esquema que se está copiando) según los documentos de MySQL
abigperson
59

Si está utilizando Linux, puede usar este script bash: (tal vez necesite una limpieza adicional del código pero funciona ... y es mucho más rápido que mysqldump | mysql)

#!/bin/bash

DBUSER=user
DBPASSWORD=pwd
DBSNAME=sourceDb
DBNAME=destinationDb
DBSERVER=db.example.com

fCreateTable=""
fInsertData=""
echo "Copying database ... (may take a while ...)"
DBCONN="-h ${DBSERVER} -u ${DBUSER} --password=${DBPASSWORD}"
echo "DROP DATABASE IF EXISTS ${DBNAME}" | mysql ${DBCONN}
echo "CREATE DATABASE ${DBNAME}" | mysql ${DBCONN}
for TABLE in `echo "SHOW TABLES" | mysql $DBCONN $DBSNAME | tail -n +2`; do
        createTable=`echo "SHOW CREATE TABLE ${TABLE}"|mysql -B -r $DBCONN $DBSNAME|tail -n +2|cut -f 2-`
        fCreateTable="${fCreateTable} ; ${createTable}"
        insertData="INSERT INTO ${DBNAME}.${TABLE} SELECT * FROM ${DBSNAME}.${TABLE}"
        fInsertData="${fInsertData} ; ${insertData}"
done;
echo "$fCreateTable ; $fInsertData" | mysql $DBCONN $DBNAME
jozjan
fuente
77
Si está utilizando el script anterior con tablas InnoDB y tiene claves foráneas, cambie la última línea a la siguiente:echo "set foreign_key_checks = 0; $fCreateTable ; $fInsertData ; set foreign_key_checks = 1;" | mysql $DBCONN $DBNAME
pegli
¿Esto también copia datos de restricción y otras propiedades de tablas?
Lucas Moeskops
1
Parece que sí, porque usa una instrucción "SHOW CREATE TABLE" que genera una CREATE TABLE con todas las propiedades del original.
Danita
1
Si tiene el problema que @zirael describió, probablemente se deba a que el script no puede copiar las vistas. Puede ignorar las vistas de la copia cambiando la SHOW TABLESlínea SHOW FULL TABLES WHERE Table_Type = 'BASE TABLE'y agregando | cut -f 1. La línea completa debería verse más o menos así, pero reemplace las líneas de retroceso dobles con líneas de fondo simples: for TABLE in ``echo "SHOW FULL TABLES WHERE Table_Type = 'BASE TABLE'" | mysql $DBCONN $DBSNAME | tail -n +2 | cut -f 1``; do
Code Commander
1
Limpié este script de @jozjan y apliqué algunos de los comentarios sobre claves externas y de otro tipo para crear esta versión en GIST gist.github.com/christopher-hopper/8431737
Christopher
11

En PHP:

function cloneDatabase($dbName, $newDbName){
    global $admin;
    $db_check = @mysql_select_db ( $dbName );
    $getTables  =   $admin->query("SHOW TABLES");   
    $tables =   array();
    while($row = mysql_fetch_row($getTables)){
        $tables[]   =   $row[0];
    }
    $createTable    =   mysql_query("CREATE DATABASE `$newDbName` DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;") or die(mysql_error());
    foreach($tables as $cTable){
        $db_check   =   @mysql_select_db ( $newDbName );
        $create     =   $admin->query("CREATE TABLE $cTable LIKE ".$dbName.".".$cTable);
        if(!$create) {
            $error  =   true;
        }
        $insert     =   $admin->query("INSERT INTO $cTable SELECT * FROM ".$dbName.".".$cTable);
    }
    return !isset($error);
}


// usage
$clone  = cloneDatabase('dbname','newdbname');  // first: toCopy, second: new database
mr_app
fuente
Si está trabajando en la máquina de Windows. Luego, amablemente, use esto en lugar de encontrar formas largas de ejecutar el comando.
Parixit
este script no tiene vistas en cuenta
sd1sd1
4

Tenga en cuenta que hay un comando mysqldbcopy como parte del complemento de las utilidades mysql ... https://dev.mysql.com/doc/mysql-utilities/1.5/en/utils-task-clone-db.html

furículo
fuente
Pero requiere la instalación de un paquete adicional:apt install mysql-utilities
Joel G Mathew
2
Pero no había ninguna restricción que dijera que eso no era posible ... y es algo que se instala comúnmente (pero opcional como usted dice) Si no está instalado, a muchos les resultaría más fácil instalar ese paquete que configurar y ejecutar un script Bash de 60 líneas , etc ....
furicle
Su publicación probablemente fue rechazada porque no incluyó ninguna otra información que no sea un enlace. Se supone que las respuestas son más completas.
Joel G Mathew
1

Realmente no sé qué quieres decir con "acceso local". Pero para esa solución, debe poder acceder a través del servidor ssh para copiar los archivos donde se almacena la base de datos .

No puedo usar mysqldump, porque mi base de datos es grande (7Go, mysqldump falla) Si la versión de la base de datos 2 mysql es demasiado diferente, puede que no funcione, puede verificar su versión de mysql usando mysql -V.

1) Copie los datos de su servidor remoto a su computadora local (vps es el alias de su servidor remoto, puede ser reemplazado por [email protected])

ssh vps:/etc/init.d/mysql stop
scp -rC vps:/var/lib/mysql/ /tmp/var_lib_mysql
ssh vps:/etc/init.d/apache2 start

2) Importe los datos copiados en su computadora local

/etc/init.d/mysql stop
sudo chown -R mysql:mysql /tmp/var_lib_mysql
sudo nano /etc/mysql/my.cnf
-> [mysqld]
-> datadir=/tmp/var_lib_mysql
/etc/init.d/mysql start

Si tiene una versión diferente, es posible que deba ejecutar

/etc/init.d/mysql stop
mysql_upgrade -u root -pPASSWORD --force #that step took almost 1hrs
/etc/init.d/mysql start
Remy Mellet
fuente
Esta es la forma más eficiente de hacerlo, pero creo que "sin acceso local al servidor" significa que no podemos acceder al sistema. Probablemente un alojamiento compartido? Entonces esta no es la respuesta.
Valerio Bozz
1

Todas las soluciones anteriores llegan al punto un poco, sin embargo, simplemente no copian todo. Creé una función PHP (aunque algo larga) que copia todo, incluyendo tablas, claves foráneas, datos, vistas, procedimientos, funciones, disparadores y eventos. Aquí está el código:

/* This function takes the database connection, an existing database, and the new database and duplicates everything in the new database. */
function copyDatabase($c, $oldDB, $newDB) {

    // creates the schema if it does not exist
    $schema = "CREATE SCHEMA IF NOT EXISTS {$newDB};";
    mysqli_query($c, $schema);

    // selects the new schema
    mysqli_select_db($c, $newDB);

    // gets all tables in the old schema
    $tables = "SELECT table_name
               FROM information_schema.tables
               WHERE table_schema = '{$oldDB}'
               AND table_type = 'BASE TABLE'";
    $results = mysqli_query($c, $tables);

    // checks if any tables were returned and recreates them in the new schema, adds the foreign keys, and inserts the associated data
    if (mysqli_num_rows($results) > 0) {

        // recreates all tables first
        while ($row = mysqli_fetch_array($results)) {
            $table = "CREATE TABLE {$newDB}.{$row[0]} LIKE {$oldDB}.{$row[0]}";
            mysqli_query($c, $table);
        }

        // resets the results to loop through again
        mysqli_data_seek($results, 0);

        // loops through each table to add foreign key and insert data
        while ($row = mysqli_fetch_array($results)) {

            // inserts the data into each table
            $data = "INSERT IGNORE INTO {$newDB}.{$row[0]} SELECT * FROM {$oldDB}.{$row[0]}";
            mysqli_query($c, $data);

            // gets all foreign keys for a particular table in the old schema
            $fks = "SELECT constraint_name, column_name, table_name, referenced_table_name, referenced_column_name
                    FROM information_schema.key_column_usage
                    WHERE referenced_table_name IS NOT NULL
                    AND table_schema = '{$oldDB}'
                    AND table_name = '{$row[0]}'";
            $fkResults = mysqli_query($c, $fks);

            // checks if any foreign keys were returned and recreates them in the new schema
            // Note: ON UPDATE and ON DELETE are not pulled from the original so you would have to change this to your liking
            if (mysqli_num_rows($fkResults) > 0) {
                while ($fkRow = mysqli_fetch_array($fkResults)) {
                    $fkQuery = "ALTER TABLE {$newDB}.{$row[0]}                              
                                ADD CONSTRAINT {$fkRow[0]}
                                FOREIGN KEY ({$fkRow[1]}) REFERENCES {$newDB}.{$fkRow[3]}({$fkRow[1]})
                                ON UPDATE CASCADE
                                ON DELETE CASCADE;";
                    mysqli_query($c, $fkQuery);
                }
            }
        }   
    }

    // gets all views in the old schema
    $views = "SHOW FULL TABLES IN {$oldDB} WHERE table_type LIKE 'VIEW'";                
    $results = mysqli_query($c, $views);

    // checks if any views were returned and recreates them in the new schema
    if (mysqli_num_rows($results) > 0) {
        while ($row = mysqli_fetch_array($results)) {
            $view = "SHOW CREATE VIEW {$oldDB}.{$row[0]}";
            $viewResults = mysqli_query($c, $view);
            $viewRow = mysqli_fetch_array($viewResults);
            mysqli_query($c, preg_replace("/CREATE(.*?)VIEW/", "CREATE VIEW", str_replace($oldDB, $newDB, $viewRow[1])));
        }
    }

    // gets all triggers in the old schema
    $triggers = "SELECT trigger_name, action_timing, event_manipulation, event_object_table, created
                 FROM information_schema.triggers
                 WHERE trigger_schema = '{$oldDB}'";                 
    $results = mysqli_query($c, $triggers);

    // checks if any triggers were returned and recreates them in the new schema
    if (mysqli_num_rows($results) > 0) {
        while ($row = mysqli_fetch_array($results)) {
            $trigger = "SHOW CREATE TRIGGER {$oldDB}.{$row[0]}";
            $triggerResults = mysqli_query($c, $trigger);
            $triggerRow = mysqli_fetch_array($triggerResults);
            mysqli_query($c, str_replace($oldDB, $newDB, $triggerRow[2]));
        }
    }

    // gets all procedures in the old schema
    $procedures = "SHOW PROCEDURE STATUS WHERE db = '{$oldDB}'";
    $results = mysqli_query($c, $procedures);

    // checks if any procedures were returned and recreates them in the new schema
    if (mysqli_num_rows($results) > 0) {
        while ($row = mysqli_fetch_array($results)) {
            $procedure = "SHOW CREATE PROCEDURE {$oldDB}.{$row[1]}";
            $procedureResults = mysqli_query($c, $procedure);
            $procedureRow = mysqli_fetch_array($procedureResults);
            mysqli_query($c, str_replace($oldDB, $newDB, $procedureRow[2]));
        }
    }

    // gets all functions in the old schema
    $functions = "SHOW FUNCTION STATUS WHERE db = '{$oldDB}'";
    $results = mysqli_query($c, $functions);

    // checks if any functions were returned and recreates them in the new schema
    if (mysqli_num_rows($results) > 0) {
        while ($row = mysqli_fetch_array($results)) {
            $function = "SHOW CREATE FUNCTION {$oldDB}.{$row[1]}";
            $functionResults = mysqli_query($c, $function);
            $functionRow = mysqli_fetch_array($functionResults);
            mysqli_query($c, str_replace($oldDB, $newDB, $functionRow[2]));
        }
    }

    // selects the old schema (a must for copying events)
    mysqli_select_db($c, $oldDB);

    // gets all events in the old schema
    $query = "SHOW EVENTS
              WHERE db = '{$oldDB}';";
    $results = mysqli_query($c, $query);

    // selects the new schema again
    mysqli_select_db($c, $newDB);

    // checks if any events were returned and recreates them in the new schema
    if (mysqli_num_rows($results) > 0) {
        while ($row = mysqli_fetch_array($results)) {
            $event = "SHOW CREATE EVENT {$oldDB}.{$row[1]}";
            $eventResults = mysqli_query($c, $event);
            $eventRow = mysqli_fetch_array($eventResults);
            mysqli_query($c, str_replace($oldDB, $newDB, $eventRow[3]));
        }
    }
}
Dustin
fuente
Votado negativamente porque la pregunta no es "no use mysqldump" sino "use un enfoque mejor que mysqldump". Esto es aún peor mysqldumpen términos de eficiencia.
Valerio Bozz
1

En realidad, quería lograr exactamente eso en PHP, pero ninguna de las respuestas aquí fue muy útil, así que aquí está mi solución, bastante sencilla, usando MySQLi:

// Database variables

$DB_HOST = 'localhost';
$DB_USER = 'root';
$DB_PASS = '1234';

$DB_SRC = 'existing_db';
$DB_DST = 'newly_created_db';



// MYSQL Connect

$mysqli = new mysqli( $DB_HOST, $DB_USER, $DB_PASS ) or die( $mysqli->error );



// Create destination database

$mysqli->query( "CREATE DATABASE $DB_DST" ) or die( $mysqli->error );



// Iterate through tables of source database

$tables = $mysqli->query( "SHOW TABLES FROM $DB_SRC" ) or die( $mysqli->error );

while( $table = $tables->fetch_array() ): $TABLE = $table[0];


    // Copy table and contents in destination database

    $mysqli->query( "CREATE TABLE $DB_DST.$TABLE LIKE $DB_SRC.$TABLE" ) or die( $mysqli->error );
    $mysqli->query( "INSERT INTO $DB_DST.$TABLE SELECT * FROM $DB_SRC.$TABLE" ) or die( $mysqli->error );


endwhile;
GDY
fuente
No estoy seguro de que haga un clon 1: 1, pero parece que para bases de datos simples podría ser suficiente.
beppe9000
Lo estoy usando para crear instalaciones rápidas de WordPress en mi servidor de desarrollo. Esta parte combinada con algunas otras rutinas duplica y ajusta una instalación de origen en un nuevo proyecto. Para eso funciona bien ... pero una base de datos en blanco de WordPress no es muy compleja, así que no puedo hacer una declaración para casos de uso más extendido
GDY
0

La mejor manera de clonar tablas de bases de datos sin mysqldump:

  1. Crea una nueva base de datos.
  2. Crear consultas de clonación con consulta:

    SET @NewSchema = 'your_new_db';
    SET @OldSchema = 'your_exists_db';
    SELECT CONCAT('CREATE TABLE ',@NewSchema,'.',table_name, ' LIKE ', TABLE_SCHEMA ,'.',table_name,';INSERT INTO ',@NewSchema,'.',table_name,' SELECT * FROM ', TABLE_SCHEMA ,'.',table_name,';') 
    FROM information_schema.TABLES where TABLE_SCHEMA = @OldSchema AND TABLE_TYPE != 'VIEW';
  3. ¡Ejecuta esa salida!

Pero tenga en cuenta que la secuencia de comandos anterior solo clona tablas rápidas , no vistas, disparadores y funciones de usuario: puede obtener una estructura rápida mysqldump --no-data --triggers -uroot -ppasswordy luego usar para clonar solo insertar instrucciones.

¿Por qué es una pregunta real? Debido a que la carga de mysqldumps es muy lenta si la DB supera los 2 Gb. Y no puede clonar tablas de InnoDB simplemente copiando archivos DB (como la copia de seguridad de instantáneas).

Alexander Goncharov
fuente
0

un SQL que muestra comandos SQL, debe ejecutarse para duplicar una base de datos de una base de datos a otra. para cada tabla se crea una declaración de tabla y una declaración de inserción. se supone que ambas bases de datos están en el mismo servidor:

select @fromdb:="crm";
select @todb:="crmen";

SET group_concat_max_len=100000000;


SELECT  GROUP_CONCAT( concat("CREATE TABLE `",@todb,"`.`",table_name,"` LIKE `",@fromdb,"`.`",table_name,"`;\n",
"INSERT INTO `",@todb,"`.`",table_name,"` SELECT * FROM `",@fromdb,"`.`",table_name,"`;") 

SEPARATOR '\n\n')

as sqlstatement
 FROM information_schema.tables where table_schema=@fromdb and TABLE_TYPE='BASE TABLE';
Shimon Doodkin
fuente
-1

Mysqldump no es una mala solución. La forma más simple de duplicar la base de datos:

mysqldump -uusername -ppass dbname1 | mysql -uusername -ppass dbname2

Además, puede cambiar el motor de almacenamiento de esta manera:

mysqldump -uusername -ppass dbname1 | sed 's/InnoDB/RocksDB/' | mysql -uusername -ppass dbname2

Andy Al
fuente