Copia / duplica il database senza usare mysqldump


427

Senza accesso locale al server, esiste un modo per duplicare / clonare un db MySQL (con contenuto e senza contenuto) in un altro senza utilizzarlo mysqldump?

Attualmente sto usando MySQL 4.0.


12
Cosa c'è che non va mysqldump?
Michael Mior,

48
Assicurati di non farlo: CREATE TABLE t2 SELECT * FROM t1;poiché perderai le informazioni sull'indice, qualsiasi cosa speciale come auto_increment ecc. Molti google per questo tipo di tabella di copia ti condurranno a fare questo e avranno risultati indesiderati .
John Hunt,

59
Una domanda fuori tema ottiene 92 voti e 37 preferiti. Complimenti per tale domanda fuori tema. Linee guida obsolete.
pal4life,

25
Il 100% concorda sul fatto che il "chiuso come fuori tema" sia sbagliato e che le linee guida debbano essere aggiornate - è necessaria una maggiore clemenza - SO sta andando nella direzione sbagliata. È ovvio che @will è completamente fuori dal comune e dovrebbe avere i suoi privilegi di moderatore rimossi - questa singola domanda è abbastanza prova.
stolsvik,

10
Chiuso come off topic è sbagliato al 100%. Questa è la domanda esatta che ho, e ha una risposta tecnica ben definita che non si riferisce alla semplice opinione. Penso che il moderatore debba aver cercato parole come "il migliore" per trovare domande da chiudere.
Sam Goldberg,

Risposte:


686

Vedo che hai detto che non volevi usare mysqldump, ma ho raggiunto questa pagina mentre cercavo una soluzione simile e anche altri potrebbero trovarla. Con questo in mente, ecco un modo semplice per duplicare un database dalla riga di comando di un server Windows:

  1. Crea il database di destinazione utilizzando MySQLAdmin o il tuo metodo preferito. In questo esempio, db2è il database di destinazione, in cui db1verrà copiato il database di origine .
  2. Eseguire la seguente istruzione su una riga di comando:

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

Nota: non c'è spazio tra -pe[password]


108
Il caso contro mysqldump è che ci deve essere un modo più veloce di serializzare i dati in query, trasmettere le query al di fuori del processo e tornare indietro nello stesso identico processo , replicare le query ed eseguirle come istruzioni. Sembra orribilmente inefficiente e inutile . Non stiamo parlando di incrocio tra master MySQL o modifica dei motori di archiviazione. È scioccante che non vi sia un efficiente trasferimento binario intraprocess.
Toddius Zho,

42
Se non si desidera salvare il testo in chiaro la password nella vostra storia terminali, è necessario dividere il comando: mysqldump -h [server] -u [user] -p db1 > db1, mysql -h [server] -u [user] -p db2 < db1 In caso contrario Richiesta password pasticci in su, almeno per me quando si utilizza stucco.
Kapex,

5
l'utilizzo di mysqldump e mysql da bash diventa molto più semplice se si configura il file .my.cnf per archiviare i file utente / host / password
ErichBSchulz

4
mysqldump -u root -p -v db1 | mysql -u root -p db2e due volte inserisci pass
hlcs

6
dio, per favore qualcuno potrebbe spiegare il mio perché una domanda affermando "senza mysqldump" ha come prima risposta quella che usa mysqldump? con like, 6 volte più voti di quello corretto ? dai, SO ...
igorsantos07,

135

Puoi duplicare una tabella senza dati eseguendo:

CREATE TABLE x LIKE y;

(Vedi i documenti MySQL CREATE TABLE )

È possibile scrivere uno script che preleva l'output SHOW TABLESda un database e copia lo schema in un altro. Dovresti essere in grado di fare riferimento a nomi di schema + tabella come:

CREATE TABLE x LIKE other_db.y;

Per quanto riguarda i dati, puoi anche farlo in MySQL, ma non è necessariamente veloce. Dopo aver creato i riferimenti, è possibile eseguire quanto segue per copiare i dati:

INSERT INTO x SELECT * FROM other_db.y;

Se stai usando MyISAM, è meglio copiare i file della tabella; sarà molto più veloce. Dovresti essere in grado di fare lo stesso se utilizzi INNODB con tablespace per tabella .

Se finisci per fare un INSERT INTO SELECT, assicurati di disattivare temporaneamente gli indici con ALTER TABLE x DISABLE KEYS!

EDIT Maatkit ha anche alcuni script che possono essere utili per la sincronizzazione dei dati. Potrebbe non essere più veloce, ma probabilmente potresti eseguire i loro script di sincronizzazione su dati live senza molto blocco.


1
è questo lavoro per la tabella duplicata? da quando vedo il comando è CREATE TABLE
GusDeCooL

4
Si può fare CREATE TABLE ... SELECT.
Eggyal

3
Ho provato a copiare i file della tabella di un database MyISAM una volta, ma questo ha appena danneggiato il nuovo database. Probabilmente il mio male, ma sicuramente non è un'operazione così banale come alcuni dicono.
Johan Fredrik Varen,

2
Questo è un bel trucco e io sono un fan, ma una nota importante: questo non comporta alcun vincolo di chiave esterna (anche quelli esterni allo schema da copiare) per i documenti MySQL
abigperson

59

Se stai usando Linux, puoi usare questo script bash: (forse ha bisogno di una pulizia aggiuntiva del codice ma funziona ... ed è molto più veloce di 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

7
Se stai usando lo script sopra con tabelle InnoDB e hai chiavi esterne, cambia l'ultima riga nel modo seguente:echo "set foreign_key_checks = 0; $fCreateTable ; $fInsertData ; set foreign_key_checks = 1;" | mysql $DBCONN $DBNAME
pegli

Questo copia anche i dati sui vincoli e altre proprietà delle tabelle?
Lucas Moeskops,

1
Sembra così, perché usa un'istruzione "SHOW CREATE TABLE" che genera una CREATE TABLE con tutte le proprietà dell'originale.
Danita,

1
Se ricevi il problema descritto da @zirael, probabilmente è perché lo script non riesce a copiare le viste. È possibile ignorare le viste dalla copia modificando la SHOW TABLESriga SHOW FULL TABLES WHERE Table_Type = 'BASE TABLE'e aggiungendola | cut -f 1. La riga completa dovrebbe assomigliare a questa, ma sostituire i doppi backtick con single backtick: 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
Ho ripulito questo script da @jozjan e applicato alcuni dei commenti relativi a chiavi esterne e di altro tipo per creare questa versione su GIST gist.github.com/christopher-hopper/8431737
Christopher

11

In 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

Se stai lavorando su Windows Machine. Quindi usa gentilmente questo invece di trovare lunghi modi per eseguire il comando.
Parixit,

questo script non prende in considerazione il conteggio
sd1sd1,

4

Nota che esiste un comando mysqldbcopy come parte dell'aggiunta nelle utility mysql .... https://dev.mysql.com/doc/mysql-utilities/1.5/en/utils-task-clone-db.html


Ma richiede l'installazione di un pacchetto aggiuntivo:apt install mysql-utilities
Joel G Mathew,

2
Ma non c'erano restrizioni che dicevano che non era possibile ... ed è una cosa comunemente installata (ma opzionale come dici tu) Se non è installato, molti troverebbero l'installazione di quel pacchetto più semplice della configurazione e dell'esecuzione di uno script Bash a 60 righe , ecc ....
Furicle,

Il tuo post è stato probabilmente annullato perché non hai incluso altre informazioni oltre a un link. Le risposte dovrebbero essere più complete.
Joel G Mathew,

1

Non so davvero cosa intendi per "accesso locale". Ma per quella soluzione è necessario poter accedere su ssh al server per copiare i file in cui è archiviato il database .

Non riesco a usare mysqldump, perché il mio database è grande (7 Go, mysqldump fallito) Se la versione del database 2 mysql è troppo diversa potrebbe non funzionare, puoi controllare la tua versione mysql usando mysql -V.

1) Copia i dati dal tuo server remoto sul tuo computer locale (vps è l'alias sul tuo server remoto, può essere sostituito da root@1.2.3.4)

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) Importa i dati copiati sul tuo computer locale

/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

Se hai una versione diversa, potresti dover eseguire

/etc/init.d/mysql stop
mysql_upgrade -u root -pPASSWORD --force #that step took almost 1hrs
/etc/init.d/mysql start

Questo è il modo più efficiente per farlo, ma penso che "senza accesso locale al server" significhi che non possiamo accedere al sistema. Probabilmente un hosting condiviso? Quindi questa non è la risposta.
Valerio Bozz,

1

Tutte le soluzioni precedenti arrivano al punto un po ', tuttavia, non copiano tutto. Ho creato una funzione PHP (anche se un po 'lunga) che copia tutto incluso tabelle, chiavi esterne, dati, viste, procedure, funzioni, trigger ed eventi. Ecco il codice:

/* 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]));
        }
    }
}

Sottovalutato perché la domanda non è "non usare mysqldump" ma "usa un approccio migliore di mysqldump". Questo è ancora peggio mysqldumpin termini di efficienza.
Valerio Bozz,

1

In realtà volevo ottenere esattamente questo in PHP, ma nessuna delle risposte qui è stata molto utile, quindi ecco la mia soluzione - piuttosto semplice - 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;

Non sono sicuro che questo dia un clone 1: 1 ma sembra che per semplici database potrebbe essere sufficiente.
beppe9000,

Lo sto usando per creare veloci installazioni di WordPress sul mio server di sviluppo. Questa parte accoppiata con altre routine duplicate e adatta un'installazione di origine in un nuovo progetto. Per questo funziona bene ... ma un database di wordpress vuoto non è molto complesso, quindi non posso fare una dichiarazione per casi d'uso più estesi
GDY

0

Il modo migliore per clonare le tabelle del database senza mysqldump:

  1. Crea un nuovo database.
  2. Crea query clone con query:

    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. Esegui quell'output!

Ma nota, lo script sopra solo le tabelle di clonazione veloce - non le viste, i trigger e le funzioni utente: puoi ottenere rapidamente la struttura mysqldump --no-data --triggers -uroot -ppassworde quindi utilizzare per clonare solo l'istruzione insert.

Perché è una vera domanda? Perché il caricamento di mysqldumps è brutto lento se il DB è superiore a 2 GB. E non puoi clonare le tabelle di InnoDB semplicemente copiando i file DB (come il backup di istantanee).


0

un SQL che mostra i comandi SQL, deve essere eseguito per duplicare un database da un database all'altro. per ogni tabella è presente un'istruzione table e un'istruzione insert. presuppone che entrambi i database si trovino sullo stesso server:

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';

-1

Mysqldump non è una cattiva soluzione. Il modo più semplice per duplicare il database:

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

Inoltre, è possibile modificare il motore di archiviazione in questo modo:

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

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.