mysql使用binlog恢复数据
###############################方法一############################
#!/bin/bash
mpath=“/data1/mysql5022_3306/binlog” for ((a=989;a < 1360;a++)) do if [[ $a -lt 1000 ]];then b=“0$a” else b=$a fi filename=”${mpath}/mysql-log.00${b}” if [[ -f $filename ]];then
echo "$filename"
#mysqlbinlog ${filename} >temp.log
#由于之上语句没有指定临时文件目录,所以大量临时数据会产生在/tmp下,容易引起磁盘爆满。使用下句:
mysqlbinlog —local-load=/data1/mysql5022_3306/tmp ${filename} >temp.log cat temp.log|grep “version_property” >>result.log fi done
########################方法二##########################
#!/usr/bin/perl
#################################################################
#parse_binlog.pl: find proper mysql binlog file and print lines
# match a giving regex.
#Author: Curu Wong
#License: GPL v2
#Date: 2012-10-31
#################################################################
use strict;
use warnings;
use
File::Glob
‘:glob’;
use
File::stat
;
use Getopt::Long;
sub parse_time{
my $time_str = shift;
my $ts = qx(date -d “$time_str” +%s);
return $ts;
}
#return file atime and mtime
sub get_amtime{
my $file = shift;
my $stat = stat($file);
return ($stat->atime,$stat->mtime);
}
sub select_binlog_files{
#$logbin is mysql —log-bin param
my $logbin = shift;
my $time_start = shift;
my $time_end = shift;
my @result;
my @files = bsd_glob(“$logbin.[0-9]”);
for my $f (@files){
my ($atime, $mtime) = get_amtime($f);
push @result, $f if ($mtime >= $time_start
&
&
$atime
<
= $time_end);
}
return @result;
}
sub get_sql_from_binlog{
my $time_start = shift;
my $time_end = shift;
my $db = shift;
my $tbl = shift;
my @binlogfiles = @_;
my $mysqlbinlog_cmd=“mysqlbinlog —start-datetime=‘$time_start’ —stop-datetime=‘$time_end’ @binlogfiles”;
print STDERR “$mysqlbinlog_cmd\n”;
open(my $BINLOG, “$mysqlbinlog_cmd|”)
or die “unable to run mysqlbinlog:$!”;
#separate record by binlog header
my $record_sep = ”# at ”;
local $/ = “\n$record_sep”;
my $cur_db = "";
my $use_db = 0;
print '' x 80,“\n”;
print “db:$db, tbl:$tbl\n”;
print '' x 80,“\n”;
my @tbl_regex = (
qr/^\sCREATE\s+(?:TEMPORARY)?\sTABLE\s(?:IF\s+NOT\s+EXISTS)?\s*?\b$tbl\b'?/i, qr/^\s*ALTER\s+(?:IGNORE)?\s*TABLE\s+?\b$tbl\b?/i, qr/^\s*(?:CREATE|DROP).+?\s+INDEX\s+.*?\s+ON\s+?\b$tbl\b?/i, qr/^\s*DROP\s+(?:TEMPORARY)?\s*TABLE\s+.*??\b$tbl\b?/i, qr/^\s*RENAME\s+TABLE.*?\b$tbl\b/i, qr/^\s*TRUNCATE\s+(?:TABLE)?\s*?\b$tbl\b?/i, qr/^\s*DELETE.*?\s+FROM\s+?\b$tbl\b?/i, qr/^\s*(?:INSERT|REPLACE)\s+(?:LOW_PRIORITY|DELAYED|HIGH_PRIORITY)?(?:\s+IGNORE)?(?:INTO\s+)?\s*?\b$tbl\b?/i, qr/^\s*LOAD\s+DATA.*?INTO\s+TABLE\s+?\b$tbl\b?/i, qr/^\s*UPDATE\s+.*??\b$tbl\b`?/i,
);
while(my $record =
<
$BINLOG>){
#skip next 2 line
$record = substr($record, index($record, "\n") + 1);
$record = substr($record, index($record, "\n") + 1);
my @statements = split /;\n/, $record;
for my $stmt (@statements){
if($stmt =~ /use\s+?(\S+)?/){
$cur_db = $1;
$use_db = 1;
}
next if($db
&
&
($db ne $cur_db));
if($use_db){
$use_db = 0;
print “$stmt;\n”;
next;
}
print “$stmt;\n” if (!$tbl or grep { $stmt =~ m/$_/ } @tbl_regex);
}
}
close($BINLOG);
}
sub usage{
print “Usage: $0 [-i]
<
log-bin>
<
start_time>
<
stop_time>
<
db_name> [tbl_name]\n”;
print “eg:\n”;
print ” $0 /var/lib/mysql/mysql-bin ‘2012-10-20 00:00:00’ ‘2012-10-21 00:00:00’ ‘d_qqpcmgr_crash_raw’\n”;
print “\n”;
print “Note:
<
start_time> or
<
stop_time> must be in valid mysql DATETIME or TIMESTAMP format\n”;
print ” use -i option to ignore case in
<
regex>\n”;
print ”
<
log-bin> is the full file prefix of mysql binlog,not just filename prefix\n”;
exit(1);
}
#main
my $ignore_case = 0;
#parse options
GetOptions(‘help|h’ =>
&
usage,
);
if(@ARGV
<
5){
usage();
}
my ($dir, $time_start, $time_end, $db, $tbl) = @ARGV;
my @binlog = select_binlog_files($dir, parse_time($time_start), parse_time($time_end));
get_sql_from_binlog($time_start, $time_end, $db, $tbl, @binlog);