11
use File::Temp qw(tempfile);
15
mysqlhotcopy - fast on-line hot-backup utility for local MySQL databases and tables
21
mysqlhotcopy --suffix=_copy db_name_1 ... db_name_n
23
mysqlhotcopy db_name_1 ... db_name_n /path/to/new_directory
25
mysqlhotcopy db_name./regex/
27
mysqlhotcopy db_name./^\(foo\|bar\)/
29
mysqlhotcopy db_name./~regex/
31
mysqlhotcopy db_name_1./regex_1/ db_name_1./regex_2/ ... db_name_n./regex_n/ /path/to/new_directory
33
mysqlhotcopy --method='scp -Bq -i /usr/home/foo/.ssh/identity' --user=root --password=secretpassword \
34
db_1./^nice_table/ user@some.system.dom:~/path/to/new_directory
36
WARNING: THIS PROGRAM IS STILL IN BETA. Comments/patches welcome.
40
# Documentation continued at end of file
44
my $opt_tmpdir = $ENV{TMPDIR} || "/tmp";
46
my $OPTIONS = <<"_OPTIONS";
50
Usage: $0 db_name[./table_regex/] [new_db_name | directory]
52
-?, --help display this helpscreen and exit
53
-u, --user=# user for database login if not current user
54
-p, --password=# password to use when connecting to server (if not set
55
in my.cnf, which is recommended)
56
-h, --host=# Hostname for local server when connecting over TCP/IP
57
-P, --port=# port to use when connecting to local server with TCP/IP
58
-S, --socket=# socket to use when connecting to local server
60
--allowold don\'t abort if target dir already exists (rename it _old)
61
--addtodest don\'t rename target dir if it exists, just add files to it
62
--keepold don\'t delete previous (now renamed) target when done
63
--noindices don\'t include full index files in copy
64
--method=# method for copy (only "cp" currently supported)
66
-q, --quiet be silent except for errors
68
-n, --dryrun report actions without doing them
70
--regexp=# copy all databases with names matching regexp
71
--suffix=# suffix for names of copied databases
72
--checkpoint=# insert checkpoint entry into specified db.table
73
--flushlog flush logs once all tables are locked
74
--resetmaster reset the binlog once all tables are locked
75
--resetslave reset the master.info once all tables are locked
76
--tmpdir=# temporary directory (instead of $opt_tmpdir)
77
--record_log_pos=# record slave and master status in specified db.table
78
--chroot=# base directory of chroot jail in which mysqld operates
80
Try \'perldoc $0\' for more complete documentation
87
# Do not initialize user or password options; that way, any user/password
88
# options specified in option files will be used. If no values are specified
89
# all, the defaults will be used (login name, no password).
93
allowold => 0, # for safety
98
Getopt::Long::Configure(qw(no_ignore_case)); # disambuguate -p and -P
124
) or usage("Invalid option");
128
# a list of hash-refs containing:
130
# 'src' - name of the db to copy
131
# 't_regex' - regex describing tables in src
132
# 'target' - destination directory of the copy
133
# 'tables' - array-ref to list of tables in the db
134
# 'files' - array-ref to list of files to be copied
135
# 'index' - array-ref to list of indexes to be copied
139
my $tgt_name = undef;
141
usage("") if ($opt{help});
143
if ( $opt{regexp} || $opt{suffix} || @ARGV > 2 ) {
144
$tgt_name = pop @ARGV unless ( exists $opt{suffix} );
145
@db_desc = map { s{^([^\.]+)\./(.+)/$}{$1}; { 'src' => $_, 't_regex' => ( $2 ? $2 : '.*' ) } } @ARGV;
148
usage("Database name to hotcopy not specified") unless ( @ARGV );
150
$ARGV[0] =~ s{^([^\.]+)\./(.+)/$}{$1};
151
@db_desc = ( { 'src' => $ARGV[0], 't_regex' => ( $2 ? $2 : '.*' ) } );
154
$tgt_name = $ARGV[1];
157
$opt{suffix} = "_copy";
162
my $start_time = time;
163
$opt_tmpdir= $opt{tmpdir} if $opt{tmpdir};
164
$0 = $1 if $0 =~ m:/([^/]+)$:;
165
$opt{quiet} = 0 if $opt{debug};
166
$opt{allowold} = 1 if $opt{keepold};
168
# --- connect to the database ---
170
$dsn = ";host=" . (defined($opt{host}) ? $opt{host} : "localhost");
171
$dsn .= ";port=$opt{port}" if $opt{port};
172
$dsn .= ";mysql_socket=$opt{socket}" if $opt{socket};
174
# use mysql_read_default_group=mysqlhotcopy so that [client] and
175
# [mysqlhotcopy] groups will be read from standard options files.
177
my $dbh = DBI->connect("dbi:mysql:$dsn;mysql_read_default_group=mysqlhotcopy",
178
$opt{user}, $opt{password},
185
# --- check that checkpoint table exists if specified ---
186
if ( $opt{checkpoint} ) {
187
$opt{checkpoint} = quote_names( $opt{checkpoint} );
188
eval { $dbh->do( qq{ select time_stamp, src, dest, msg
189
from $opt{checkpoint} where 1 != 1} );
192
die "Error accessing Checkpoint table ($opt{checkpoint}): $@"
196
# --- check that log_pos table exists if specified ---
197
if ( $opt{record_log_pos} ) {
198
$opt{record_log_pos} = quote_names( $opt{record_log_pos} );
200
eval { $dbh->do( qq{ select host, time_stamp, log_file, log_pos, master_host, master_log_file, master_log_pos
201
from $opt{record_log_pos} where 1 != 1} );
204
die "Error accessing log_pos table ($opt{record_log_pos}): $@"
208
# --- get variables from database ---
209
my $sth_vars = $dbh->prepare("show variables like 'datadir'");
211
while ( my ($var,$value) = $sth_vars->fetchrow_array ) {
212
$mysqld_vars{ $var } = $value;
214
my $datadir = $mysqld_vars{'datadir'}
215
|| die "datadir not in mysqld variables";
216
$datadir= $opt{chroot}.$datadir if ($opt{chroot});
220
# --- get target path ---
221
my ($tgt_dirname, $to_other_database);
222
$to_other_database=0;
223
if (defined($tgt_name) && $tgt_name =~ m:^\w+$: && @db_desc <= 1)
225
$tgt_dirname = "$datadir/$tgt_name";
226
$to_other_database=1;
228
elsif (defined($tgt_name) && ($tgt_name =~ m:/: || $tgt_name eq '.')) {
229
$tgt_dirname = $tgt_name;
231
elsif ( $opt{suffix} ) {
232
print "Using copy suffix '$opt{suffix}'\n" unless $opt{quiet};
234
elsif ( ($^O =~ m/^(NetWare)$/) && defined($tgt_name) && ($tgt_name =~ m:\\: || $tgt_name eq '.'))
236
$tgt_dirname = $tgt_name;
240
$tgt_name="" if (!defined($tgt_name));
241
die "Target '$tgt_name' doesn't look like a database name or directory path.\n";
244
# --- resolve database names from regexp ---
245
if ( defined $opt{regexp} ) {
247
if ( $opt{regexp} =~ s{^/(.+)/\./(.+)/$}{$1} ) {
251
my $sth_dbs = $dbh->prepare("show databases");
253
while ( my ($db_name) = $sth_dbs->fetchrow_array ) {
254
next if $db_name =~ m/^information_schema$/i;
255
push @db_desc, { 'src' => $db_name, 't_regex' => $t_regex } if ( $db_name =~ m/$opt{regexp}/o );
259
# --- get list of tables to hotcopy ---
266
foreach my $rdb ( @db_desc ) {
267
my $db = $rdb->{src};
268
my @dbh_tables = get_list_of_tables( $db );
270
## generate regex for tables/files
273
if ($rdb->{t_regex}) {
274
$t_regex = $rdb->{t_regex}; ## assign temporary regex
275
$negated = $t_regex =~ s/^~//; ## note and remove negation operator
277
$t_regex = qr/$t_regex/; ## make regex string from
280
## filter (out) tables specified in t_regex
281
print "Filtering tables with '$t_regex'\n" if $opt{debug};
282
@dbh_tables = ( $negated
283
? grep { $_ !~ $t_regex } @dbh_tables
284
: grep { $_ =~ $t_regex } @dbh_tables );
287
## get list of files to copy
288
my $db_dir = "$datadir/$db";
289
opendir(DBDIR, $db_dir )
290
or die "Cannot open dir '$db_dir': $!";
294
while ( defined( my $name = readdir DBDIR ) ) {
295
$db_files{$name} = $1 if ( $name =~ /(.+)\.\w+$/ );
299
unless( keys %db_files ) {
300
warn "'$db' is an empty database\n";
303
## filter (out) files specified in t_regex
305
if ($rdb->{t_regex}) {
306
@db_files = ($negated
307
? grep { $db_files{$_} !~ $t_regex } keys %db_files
308
: grep { $db_files{$_} =~ $t_regex } keys %db_files );
311
@db_files = keys %db_files;
314
@db_files = sort @db_files;
318
## remove indices unless we're told to keep them
319
if ($opt{noindices}) {
320
@index_files= grep { /\.(ISM|MYI)$/ } @db_files;
321
@db_files = grep { not /\.(ISM|MYI)$/ } @db_files;
324
$rdb->{files} = [ @db_files ];
325
$rdb->{index} = [ @index_files ];
326
my @hc_tables = map { quote_names("$db.$_") } @dbh_tables;
327
$rdb->{tables} = [ @hc_tables ];
329
$hc_locks .= ", " if ( length $hc_locks && @hc_tables );
330
$hc_locks .= join ", ", map { "$_ READ" } @hc_tables;
331
$hc_tables .= ", " if ( length $hc_tables && @hc_tables );
332
$hc_tables .= join ", ", @hc_tables;
334
$num_tables += scalar @hc_tables;
335
$num_files += scalar @{$rdb->{files}};
338
# --- resolve targets for copies ---
340
if (defined($tgt_name) && length $tgt_name ) {
341
# explicit destination directory specified
343
# GNU `cp -r` error message
344
die "copying multiple databases, but last argument ($tgt_dirname) is not a directory\n"
345
if ( @db_desc > 1 && !(-e $tgt_dirname && -d $tgt_dirname ) );
347
if ($to_other_database)
349
foreach my $rdb ( @db_desc ) {
350
$rdb->{target} = "$tgt_dirname";
353
elsif ($opt{method} =~ /^scp\b/)
354
{ # we have to trust scp to hit the target
355
foreach my $rdb ( @db_desc ) {
356
$rdb->{target} = "$tgt_dirname/$rdb->{src}";
361
die "Last argument ($tgt_dirname) is not a directory\n"
362
if (!(-e $tgt_dirname && -d $tgt_dirname ) );
363
foreach my $rdb ( @db_desc ) {
364
$rdb->{target} = "$tgt_dirname/$rdb->{src}";
369
die "Error: expected \$opt{suffix} to exist" unless ( exists $opt{suffix} );
371
foreach my $rdb ( @db_desc ) {
372
$rdb->{target} = "$datadir/$rdb->{src}$opt{suffix}";
376
print Dumper( \@db_desc ) if ( $opt{debug} );
378
# --- bail out if all specified databases are empty ---
380
die "No tables to hot-copy" unless ( length $hc_locks );
382
# --- create target directories if we are using 'cp' ---
386
if ($opt{method} =~ /^cp\b/)
388
foreach my $rdb ( @db_desc ) {
389
push @existing, $rdb->{target} if ( -d $rdb->{target} );
392
if ( @existing && !($opt{allowold} || $opt{addtodest}) )
395
die "Can't hotcopy to '", join( "','", @existing ), "' because directory\nalready exist and the --allowold or --addtodest options were not given.\n"
399
retire_directory( @existing ) if @existing && !$opt{addtodest};
401
foreach my $rdb ( @db_desc ) {
402
my $tgt_dirpath = "$rdb->{target}";
403
# Remove trailing slashes (needed for Mac OS X)
404
substr($tgt_dirpath, 1) =~ s|/+$||;
405
if ( $opt{dryrun} ) {
406
print "mkdir $tgt_dirpath, 0750\n";
408
elsif ($opt{method} =~ /^scp\b/) {
409
## assume it's there?
413
mkdir($tgt_dirpath, 0750) or die "Can't create '$tgt_dirpath': $!\n"
414
unless -d $tgt_dirpath;
415
if ($^O !~ m/^(NetWare)$/)
417
my @f_info= stat "$datadir/$rdb->{src}";
418
chown $f_info[4], $f_info[5], $tgt_dirpath;
423
##############################
424
# --- PERFORM THE HOT-COPY ---
426
# Note that we try to keep the time between the LOCK and the UNLOCK
427
# as short as possible, and only start when we know that we should
428
# be able to complete without error.
430
# read lock all the tables we'll be copying
431
# in order to get a consistent snapshot of the database
433
if ( $opt{checkpoint} || $opt{record_log_pos} ) {
434
# convert existing READ lock on checkpoint and/or log_pos table into WRITE lock
435
foreach my $table ( grep { defined } ( $opt{checkpoint}, $opt{record_log_pos} ) ) {
436
$hc_locks .= ", $table WRITE"
437
unless ( $hc_locks =~ s/$table\s+READ/$table WRITE/ );
441
my $hc_started = time; # count from time lock is granted
443
if ( $opt{dryrun} ) {
444
print "LOCK TABLES $hc_locks\n";
445
print "FLUSH TABLES /*!32323 $hc_tables */\n";
446
print "FLUSH LOGS\n" if ( $opt{flushlog} );
447
print "RESET MASTER\n" if ( $opt{resetmaster} );
448
print "RESET SLAVE\n" if ( $opt{resetslave} );
452
$dbh->do("LOCK TABLES $hc_locks");
453
printf "Locked $num_tables tables in %d seconds.\n", time-$start unless $opt{quiet};
454
$hc_started = time; # count from time lock is granted
456
# flush tables to make on-disk copy uptodate
458
$dbh->do("FLUSH TABLES /*!32323 $hc_tables */");
459
printf "Flushed tables ($hc_tables) in %d seconds.\n", time-$start unless $opt{quiet};
460
$dbh->do( "FLUSH LOGS" ) if ( $opt{flushlog} );
461
$dbh->do( "RESET MASTER" ) if ( $opt{resetmaster} );
462
$dbh->do( "RESET SLAVE" ) if ( $opt{resetslave} );
464
if ( $opt{record_log_pos} ) {
465
record_log_pos( $dbh, $opt{record_log_pos} );
466
$dbh->do("FLUSH TABLES /*!32323 $hc_tables */");
472
foreach my $rdb ( @db_desc )
474
my @files = map { "$datadir/$rdb->{src}/$_" } @{$rdb->{files}};
477
eval { copy_files($opt{method}, \@files, $rdb->{target}); };
478
push @failed, "$rdb->{src} -> $rdb->{target} failed: $@"
481
@files = @{$rdb->{index}};
484
copy_index($opt{method}, \@files,
485
"$datadir/$rdb->{src}", $rdb->{target} );
488
if ( $opt{checkpoint} ) {
489
my $msg = ( $@ ) ? "Failed: $@" : "Succeeded";
492
$dbh->do( qq{ insert into $opt{checkpoint} (src, dest, msg)
493
VALUES ( '$rdb->{src}', '$rdb->{target}', '$msg' )
498
warn "Failed to update checkpoint table: $@\n";
503
if ( $opt{dryrun} ) {
504
print "UNLOCK TABLES\n";
505
if ( @existing && !$opt{keepold} ) {
506
my @oldies = map { $_ . '_old' } @existing;
507
print "rm -rf @oldies\n"
513
$dbh->do("UNLOCK TABLES");
516
my $hc_dur = time - $hc_started;
517
printf "Unlocked tables.\n" unless $opt{quiet};
520
# --- HOT-COPY COMPLETE ---
521
###########################
526
# hotcopy failed - cleanup
527
# delete any @targets
528
# rename _old copy back to original
531
foreach my $rdb ( @db_desc ) {
532
push @targets, $rdb->{target} if ( -d $rdb->{target} );
534
print "Deleting @targets \n" if $opt{debug};
536
print "Deleting @targets \n" if $opt{debug};
539
print "Restoring @existing from back-up\n" if $opt{debug};
540
foreach my $dir ( @existing ) {
541
rename("${dir}_old", $dir )
542
or warn "Can't rename ${dir}_old to $dir: $!\n";
546
die join( "\n", @failed );
550
# delete _old unless $opt{keepold}
552
if ( @existing && !$opt{keepold} ) {
553
my @oldies = map { $_ . '_old' } @existing;
554
print "Deleting previous copy in @oldies\n" if $opt{debug};
558
printf "$0 copied %d tables (%d files) in %d second%s (%d seconds overall).\n",
559
$num_tables, $num_files,
560
$hc_dur, ($hc_dur==1)?"":"s", time - $start_time
570
my ($method, $files, $target) = @_;
572
print "Copying ".@$files." files...\n" unless $opt{quiet};
574
if ($^O =~ m/^(NetWare)$/) # on NetWare call PERL copy (slower)
576
foreach my $file ( @$files )
578
copy($file, $target."/".basename($file));
581
elsif ($method =~ /^s?cp\b/) # cp or scp with optional flags
584
# add option to preserve mod time etc of copied files
585
# not critical, but nice to have
586
$cp.= " -p" if $^O =~ m/^(solaris|linux|freebsd|darwin)$/;
588
# add recursive option for scp
589
$cp.= " -r" if $^O =~ /m^(solaris|linux|freebsd|darwin)$/ && $method =~ /^scp\b/;
591
# perform the actual copy
592
safe_system( $cp, (map { "'$_'" } @$files), "'$target'" );
596
die "Can't use unsupported method '$method'\n";
601
# Copy only the header of the index file
606
my ($method, $files, $source, $target) = @_;
608
print "Copying indices for ".@$files." files...\n" unless $opt{quiet};
609
foreach my $file (@$files)
611
my $from="$source/$file";
612
my $to="$target/$file";
614
open(INPUT, "<$from") || die "Can't open file $from: $!\n";
615
binmode(INPUT, ":raw");
616
my $length=read INPUT, $buff, 2048;
617
die "Can't read index header from $from\n" if ($length < 1024);
622
print "$opt{method}-header $from $to\n";
624
elsif ($opt{method} eq 'cp')
626
open(OUTPUT,">$to") || die "Can\'t create file $to: $!\n";
627
if (syswrite(OUTPUT,$buff) != length($buff))
629
die "Error when writing data to $to: $!\n";
631
close OUTPUT || die "Error on close of $to: $!\n";
633
elsif ($opt{method} =~ /^scp\b/)
635
my ($fh, $tmp)= tempfile('mysqlhotcopy-XXXXXX', DIR => $opt_tmpdir) or
636
die "Can\'t create/open file in $opt_tmpdir\n";
637
if (syswrite($fh,$buff) != length($buff))
639
die "Error when writing data to $tmp: $!\n";
641
close $fh || die "Error on close of $tmp: $!\n";
642
safe_system("$opt{method} $tmp $to");
647
die "Can't use unsupported method '$opt{method}'\n";
655
my $method= shift @sources;
656
my $target= pop @sources;
657
## @sources = list of source file names
659
## We have to deal with very long command lines, otherwise they may generate
660
## "Argument list too long".
661
## With 10000 tables the command line can be around 1MB, much more than 128kB
662
## which is the common limit on Linux (can be read from
663
## /usr/src/linux/include/linux/binfmts.h
664
## see http://www.linuxjournal.com/article.php?sid=6060).
666
my $chunk_limit= 100 * 1024; # 100 kB
671
$chunk_length+= length($_);
672
if ($chunk_length > $chunk_limit) {
673
safe_simple_system($method, @chunk, $target);
678
if ($chunk_length > 0) { # do not forget last small chunk
679
safe_simple_system($method, @chunk, $target);
683
sub safe_simple_system {
686
if ( $opt{dryrun} ) {
690
## for some reason system fails but backticks works ok for scp...
691
print "Executing '@cmd'\n" if $opt{debug};
692
my $cp_status = system "@cmd > /dev/null";
693
if ($cp_status != 0) {
694
warn "Executing command failed ($cp_status). Trying backtick execution...\n";
695
## try something else
696
`@cmd` || die "Error: @cmd failed ($?) while copying files.\n";
701
sub retire_directory {
704
foreach my $dir ( @dir ) {
705
my $tgt_oldpath = $dir . '_old';
706
if ( $opt{dryrun} ) {
707
print "rmtree $tgt_oldpath\n" if ( -d $tgt_oldpath );
708
print "rename $dir, $tgt_oldpath\n";
712
if ( -d $tgt_oldpath ) {
713
print "Deleting previous 'old' hotcopy directory ('$tgt_oldpath')\n" unless $opt{quiet};
714
rmtree([$tgt_oldpath],0,1);
716
rename($dir, $tgt_oldpath)
717
or die "Can't rename $dir=>$tgt_oldpath: $!\n";
718
print "Existing hotcopy directory renamed to '$tgt_oldpath'\n" unless $opt{quiet};
723
my ( $dbh, $table_name ) = @_;
726
my ($file,$position) = get_row( $dbh, "show master status" );
727
die "master status is undefined" if !defined $file || !defined $position;
729
my $row_hash = get_row_hash( $dbh, "show slave status" );
730
my ($master_host, $log_file, $log_pos );
731
if ( $dbh->{mysql_serverinfo} =~ /^3\.23/ ) {
732
($master_host, $log_file, $log_pos )
733
= @{$row_hash}{ qw / Master_Host Log_File Pos / };
735
($master_host, $log_file, $log_pos )
736
= @{$row_hash}{ qw / Master_Host Relay_Master_Log_File Exec_Master_Log_Pos / };
738
my $hostname = hostname();
740
$dbh->do( qq{ replace into $table_name
741
set host=?, log_file=?, log_pos=?,
742
master_host=?, master_log_file=?, master_log_pos=? },
744
$hostname, $file, $position,
745
$master_host, $log_file, $log_pos );
750
warn "Failed to store master position: $@\n";
755
my ( $dbh, $sql ) = @_;
757
my $sth = $dbh->prepare($sql);
759
return $sth->fetchrow_array();
763
my ( $dbh, $sql ) = @_;
765
my $sth = $dbh->prepare($sql);
767
return $sth->fetchrow_hashref();
770
sub get_list_of_tables {
775
$dbh->selectall_arrayref('SHOW TABLES FROM ' .
776
$dbh->quote_identifier($db))
778
warn "Unable to retrieve list of tables in $db: $@" if $@;
780
return (map { $_->[0] } @$tables);
785
# given a db.table name, add quotes
787
my ($db, $table, @cruft) = split( /\./, $name );
788
die "Invalid db.table name '$name'" if (@cruft || !defined $db || !defined $table );
790
# Earlier versions of DBD return table name non-quoted,
791
# such as DBD-2.1012 and the newer ones, such as DBD-2.9002
792
# returns it quoted. Let's have a support for both.
794
return "`$db`.`$table`";
801
mysqlhotcopy is designed to make stable copies of live MySQL databases.
803
Here "live" means that the database server is running and the database
804
may be in active use. And "stable" means that the copy will not have
805
any corruptions that could occur if the table files were simply copied
806
without first being locked and flushed from within the server.
812
=item --checkpoint checkpoint-table
814
As each database is copied, an entry is written to the specified
815
checkpoint-table. This has the happy side-effect of updating the
816
MySQL update-log (if it is switched on) giving a good indication of
817
where roll-forward should begin for backup+rollforward schemes.
819
The name of the checkpoint table should be supplied in database.table format.
820
The checkpoint-table must contain at least the following fields:
824
time_stamp timestamp not null
831
=item --record_log_pos log-pos-table
833
Just before the database files are copied, update the record in the
834
log-pos-table from the values returned from "show master status" and
835
"show slave status". The master status values are stored in the
836
log_file and log_pos columns, and establish the position in the binary
837
logs that any slaves of this host should adopt if initialised from
838
this dump. The slave status values are stored in master_host,
839
master_log_file, and master_log_pos, corresponding to the coordinates
840
of the next to the last event the slave has executed. The slave or its
841
siblings can connect to the master next time and request replication
842
starting from the recorded values.
844
The name of the log-pos table should be supplied in database.table format.
845
A sample log-pos table definition:
849
CREATE TABLE log_pos (
850
host varchar(60) NOT null,
851
time_stamp timestamp(14) NOT NULL,
852
log_file varchar(32) default NULL,
853
log_pos int(11) default NULL,
854
master_host varchar(60) NULL,
855
master_log_file varchar(32) NULL,
856
master_log_pos int NULL,
864
=item --suffix suffix
866
Each database is copied back into the originating datadir under
867
a new name. The new name is the original name with the suffix
870
If only a single db_name is supplied and the --suffix flag is not
871
supplied, then "--suffix=_copy" is assumed.
875
Move any existing version of the destination to a backup directory for
876
the duration of the copy. If the copy successfully completes, the backup
877
directory is deleted - unless the --keepold flag is set. If the copy fails,
878
the backup directory is restored.
880
The backup directory name is the original name with "_old" appended.
881
Any existing versions of the backup directory are deleted.
885
Behaves as for the --allowold, with the additional feature
886
of keeping the backup directory after the copy successfully completes.
890
Don't rename target directory if it already exists, just add the
891
copied files into it.
893
This is most useful when backing up a database with many large
894
tables and you don't want to have all the tables locked for the
897
In this situation, I<if> you are happy for groups of tables to be
898
backed up separately (and thus possibly not be logically consistant
899
with one another) then you can run mysqlhotcopy several times on
900
the same database each with different db_name./table_regex/.
901
All but the first should use the --addtodest option so the tables
902
all end up in the same directory.
906
Rotate the log files by executing "FLUSH LOGS" after all tables are
907
locked, and before they are copied.
911
Reset the bin-log by executing "RESET MASTER" after all tables are
912
locked, and before they are copied. Useful if you are recovering a
913
slave in a replication setup.
917
Reset the master.info by executing "RESET SLAVE" after all tables are
918
locked, and before they are copied. Useful if you are recovering a
919
server in a mutual replication setup.
921
=item --regexp pattern
923
Copy all databases with names matching the pattern
925
=item --regexp /pattern1/./pattern2/
927
Copy all tables with names matching pattern2 from all databases with
928
names matching pattern1. For example, to select all tables which
929
names begin with 'bar' from all databases which names end with 'foo':
931
mysqlhotcopy --indices --method=cp --regexp /foo$/./^bar/
933
=item db_name./pattern/
935
Copy only tables matching pattern. Shell metacharacters ( (, ), |, !,
936
etc.) have to be escaped (e.g. \). For example, to select all tables
937
in database db1 whose names begin with 'foo' or 'bar':
939
mysqlhotcopy --indices --method=cp db1./^\(foo\|bar\)/
941
=item db_name./~pattern/
943
Copy only tables not matching pattern. For example, to copy tables
944
that do not begin with foo nor bar:
946
mysqlhotcopy --indices --method=cp db1./~^\(foo\|bar\)/
950
Display helpscreen and exit
954
user for database login if not current user
956
=item -p, --password=#
958
password to use when connecting to the server. Note that you are strongly
959
encouraged *not* to use this option as every user would be able to see the
960
password in the process list. Instead use the '[mysqlhotcopy]' section in
961
one of the config files, normally /etc/my.cnf or your personal ~/.my.cnf.
962
(See the chapter 'my.cnf Option Files' in the manual)
964
=item -h, -h, --host=#
966
Hostname for local server when connecting over TCP/IP. By specifying this
967
different from 'localhost' will trigger mysqlhotcopy to use TCP/IP connection.
971
port to use when connecting to MySQL server with TCP/IP. This is only used
972
when using the --host option.
976
UNIX domain socket to use when connecting to local server
980
Don\'t include index files in copy. Only up to the first 2048 bytes
981
are copied; You can restore the indexes with isamchk -r or myisamchk -r
986
method for copy (only "cp" currently supported). Alpha support for
987
"scp" was added in November 2000. Your experience with the scp method
988
will vary with your ability to understand how scp works. 'man scp'
989
and 'man ssh' are your friends.
991
The destination directory _must exist_ on the target machine using the
992
scp method. --keepold and --allowold are meaningless with scp.
993
Liberal use of the --debug option will help you figure out what\'s
994
really going on when you do an scp.
996
Note that using scp will lock your tables for a _long_ time unless
997
your network connection is _fast_. If this is unacceptable to you,
998
use the 'cp' method to copy the tables to some temporary area and then
999
scp or rsync the files at your leisure.
1003
be silent except for errors
1007
Debug messages are displayed
1011
Display commands without actually doing them
1017
This software is free and comes without warranty of any kind. You
1018
should never trust backup software without studying the code yourself.
1019
Study the code inside this script and only rely on it if I<you> believe
1020
that it does the right thing for you.
1022
Patches adding bug fixes, documentation and new features are welcome.
1023
Please send these to internals@lists.mysql.com.
1027
Extend the individual table copy to allow multiple subsets of tables
1028
to be specified on the command line:
1030
mysqlhotcopy db newdb t1 t2 /^foo_/ : t3 /^bar_/ : +
1032
where ":" delimits the subsets, the /^foo_/ indicates all tables
1033
with names begining with "foo_" and the "+" indicates all tables
1034
not copied by the previous subsets.
1036
newdb is either another not existing database or a full path to a directory
1037
where we can create a directory 'db'
1039
Add option to lock each table in turn for people who don\'t need
1040
cross-table integrity.
1042
Add option to FLUSH STATUS just before UNLOCK TABLES.
1044
Add support for other copy methods (eg tar to single file?).
1046
Add support for forthcoming MySQL ``RAID'' table subdirectory layouts.
1052
Martin Waite - added checkpoint, flushlog, regexp and dryrun options
1053
Fixed cleanup of targets when hotcopy fails.
1054
Added --record_log_pos.
1055
RAID tables are now copied (don't know if this works over scp).
1057
Ralph Corderoy - added synonyms for commands
1059
Scott Wiersdorf - added table regex and scp support
1061
Monty - working --noindex (copy only first 2048 bytes of index file)
1062
Fixes for --method=scp
1064
Ask Bjoern Hansen - Cleanup code to fix a few bugs and enable -w again.
1066
Emil S. Hansen - Added resetslave and resetmaster.
1068
Jeremy D. Zawodny - Removed depricated DBI calls. Fixed bug which
1069
resulted in nothing being copied when a regexp was specified but no
1072
Martin Waite - Fix to handle database name that contains space.
1074
Paul DuBois - Remove end '/' from directory names