MySQL在ROW模式下通過binlog提取SQL語句

haoge0205發表於2016-01-05
Linux
基於row模式的binlog,生成DML(insert/update/delete)的rollback語句
通過mysqlbinlog -v 解析binlog生成可讀的sql檔案
提取需要處理的有效sql
  "### "開頭的行.如果輸入的start-position位於某個event group中間,則會導致"無法識別event"錯誤


將INSERT/UPDATE/DELETE 的sql反轉,並且1個完整sql只能佔1行
  INSERT: INSERT INTO => DELETE FROM, SET => WHERE
  UPDATE: WHERE => SET, SET => WHERE
  DELETE: DELETE FROM => INSERT INTO, WHERE => SET
用列名替換位置@{1,2,3}
  通過desc table獲得列順序及對應的列名
  特殊列型別value做特別處理
逆序


注意:
  表結構與現在的表結構必須相同[謹記]
  由於row模式是冪等的,並且恢復是一次性,所以只提取sql,不提取BEGIN/COMMIT
  只能對INSERT/UPDATE/DELETE進行處理

mysql> select * from yoon;
+----------+------------+-----------+---------------------+
| actor_id | first_name | last_name | last_update         |
+----------+------------+-----------+---------------------+
|        1 | HANK       | YOON      | 2006-02-15 04:34:33 |
|        2 | HANK       | YOON      | 2006-02-15 04:34:33 |
|        3 | HANK       | YOON      | 2006-02-15 04:34:33 |
|        4 | HANK       | YOON      | 2006-02-15 04:34:33 |
|        5 | HANK       | YOON      | 2006-02-15 04:34:33 |
|        6 | HANK       | YOON      | 2006-02-15 04:34:33 |
|        7 | HANK       | YOON      | 2006-02-15 04:34:33 |
|        8 | HANK       | YOON      | 2006-02-15 04:34:33 |
|        9 | HANK       | YOON      | 2006-02-15 04:34:33 |
|       10 | HANK       | YOON      | 2006-02-15 04:34:33 |
|       11 | HANK       | YOON      | 2006-02-15 04:34:33 |
+----------+------------+-----------+---------------------+
11 rows in set (0.00 sec)


mysql> delete from yoon;
Query OK, 11 rows affected (1.03 sec)


mysql> select * from yoon;
Empty set (0.00 sec)

命令之間的空格一定要注意,否則就會無法提取SQL語句:
[root@hank-yoon data]# perl binlog-rollback.pl -f 'mysql-bin.000001' -o '/export/data/mysql/data/yoon.sql' -u 'root' -p 'yoon'
Warning: Using a password on the command line interface can be insecure.
[root@hank-yoon data]# ls
auto.cnf            hank     ibdata2      ib_logfile1  modify.pl  mysql-bin.000001  performance_schema  test  yoon.sql
binlog-rollback.pl  ibdata1  ib_logfile0  ib_logfile2  mysql      mysql-bin.index   sakila              yoon
[root@hank-yoon data]# cat yoon.sql 
INSERT INTO `yoon`.`yoon` SET `actor_id`=11, `first_name`='HANK', `last_name`='YOON', `last_update`=from_unixtime(1139949273);
INSERT INTO `yoon`.`yoon` SET `actor_id`=10, `first_name`='HANK', `last_name`='YOON', `last_update`=from_unixtime(1139949273);
INSERT INTO `yoon`.`yoon` SET `actor_id`=9, `first_name`='HANK', `last_name`='YOON', `last_update`=from_unixtime(1139949273);
INSERT INTO `yoon`.`yoon` SET `actor_id`=8, `first_name`='HANK', `last_name`='YOON', `last_update`=from_unixtime(1139949273);
INSERT INTO `yoon`.`yoon` SET `actor_id`=7, `first_name`='HANK', `last_name`='YOON', `last_update`=from_unixtime(1139949273);
INSERT INTO `yoon`.`yoon` SET `actor_id`=6, `first_name`='HANK', `last_name`='YOON', `last_update`=from_unixtime(1139949273);
INSERT INTO `yoon`.`yoon` SET `actor_id`=5, `first_name`='HANK', `last_name`='YOON', `last_update`=from_unixtime(1139949273);
INSERT INTO `yoon`.`yoon` SET `actor_id`=4, `first_name`='HANK', `last_name`='YOON', `last_update`=from_unixtime(1139949273);
INSERT INTO `yoon`.`yoon` SET `actor_id`=3, `first_name`='HANK', `last_name`='YOON', `last_update`=from_unixtime(1139949273);
INSERT INTO `yoon`.`yoon` SET `actor_id`=2, `first_name`='HANK', `last_name`='YOON', `last_update`=from_unixtime(1139949273);
INSERT INTO `yoon`.`yoon` SET `actor_id`=1, `first_name`='HANK', `last_name`='YOON', `last_update`=from_unixtime(1139949273);

mysql> INSERT INTO `yoon`.`yoon` SET `actor_id`=11, `first_name`='HANK', `last_name`='YOON', `last_update`=from_unixtime(1139949273);
Query OK, 1 row affected (0.01 sec)


mysql> select * from yoon;
+----------+------------+-----------+---------------------+
| actor_id | first_name | last_name | last_update         |
+----------+------------+-----------+---------------------+
|       11 | HANK       | YOON      | 2006-02-15 04:34:33 |
+----------+------------+-----------+---------------------+



點選(此處)摺疊或開啟

  1. #!/usr/lib/perl -w

  2. use strict;
  3. use warnings;

  4. use Class::Struct;
  5. use Getopt::Long qw(:config no_ignore_case);                    # GetOption
  6. # register handler system signals
  7. use sigtrap 'handler', \&sig_int, 'normal-signals';

  8. # catch signal
  9. sub sig_int(){
  10.     my ($signals) = @_;
  11.     print STDERR "# Caught SIG$signals.\n";
  12.     exit 1;
  13. }

  14. my %opt;
  15. my $srcfile;
  16. my $host = '127.0.0.1';
  17. my $port = 3306;
  18. my ($user,$pwd);
  19. my ($MYSQL, $MYSQLBINLOG, $ROLLBACK_DML);
  20. my $outfile = '/dev/null';
  21. my (%do_dbs,%do_tbs);

  22. # tbname=>tbcol, tbcol: @n=>colname,type
  23. my %tbcol_pos;

  24. my $SPLITER_COL = ',';
  25. my $SQLTYPE_IST = 'INSERT';
  26. my $SQLTYPE_UPD = 'UPDATE';
  27. my $SQLTYPE_DEL = 'DELETE';
  28. my $SQLAREA_WHERE = 'WHERE';
  29. my $SQLAREA_SET = 'SET';

  30. my $PRE_FUNCT = '========================== ';

  31. # =========================================================
  32. # 基於row模式的binlog,生成DML(insert/update/delete)的rollback語句
  33. # 通過mysqlbinlog -v 解析binlog生成可讀的sql檔案
  34. # 提取需要處理的有效sql
  35. #     "### "開頭的行.如果輸入的start-position位於某個event group中間,則會導致"無法識別event"錯誤
  36. #
  37. # 將INSERT/UPDATE/DELETE 的sql反轉,並且1個完整sql只能佔1行
  38. #     INSERT: INSERT INTO => DELETE FROM, SET => WHERE
  39. #     UPDATE: WHERE => SET, SET => WHERE
  40. #     DELETE: DELETE FROM => INSERT INTO, WHERE => SET
  41. # 用列名替換位置@{1,2,3}
  42. #     通過desc table獲得列順序及對應的列名
  43. #     特殊列型別value做特別處理
  44. # 逆序
  45. #
  46. # 注意:
  47. #     表結構與現在的表結構必須相同[謹記]
  48. #     由於row模式是冪等的,並且恢復是一次性,所以只提取sql,不提取BEGIN/COMMIT
  49. #     只能對INSERT/UPDATE/DELETE進行處理
  50. # ========================================================
  51. sub main{

  52.     # get input option
  53.     &get_options();

  54.     #
  55.     &init_tbcol();

  56.     #
  57.     &do_binlog_rollback();
  58. }

  59. &main();


  60. # ----------------------------------------------------------------------------------------
  61. # Func : get options and set option flag
  62. # ----------------------------------------------------------------------------------------
  63. sub get_options{
  64.     #Get options info
  65.     GetOptions(\%opt,
  66.         'help',                    # OUT : print help info
  67.         'f|srcfile=s',            # IN : binlog file
  68.         'o|outfile=s',            # out : output sql file
  69.         'h|host=s',                # IN : host
  70.         'u|user=s', # IN : user
  71.         'p|password=s', # IN : password
  72.         'P|port=i',                # IN : port
  73.         'start-datetime=s',        # IN : start datetime
  74.         'stop-datetime=s',        # IN : stop datetime
  75.         'start-position=i',        # IN : start position
  76.         'stop-position=i',        # IN : stop position
  77.         'd|database=s',            # IN : database, split comma
  78.         'T|table=s',            # IN : table, split comma
  79.         'i|ignore',                # IN : ignore binlog check ddl and so on
  80.         'debug',                # IN : print debug information
  81.      ) or print_usage();

  82.     if (!scalar(%opt)) {
  83.         &print_usage();
  84.     }

  85.     # Handle for options
  86.     if ($opt{'f'}){
  87.         $srcfile = $opt{'f'};
  88.     }else{
  89.         &merror("please input binlog file");
  90.     }

  91.     $opt{'h'} and $host = $opt{'h'};
  92.     $opt{'u'} and $user = $opt{'u'};
  93.     $opt{'p'} and $pwd = $opt{'p'};
  94.     $opt{'P'} and $port = $opt{'P'};
  95.     if ($opt{'o'}) {
  96.         $outfile = $opt{'o'};
  97.         # 清空 outfile
  98.         `echo '' > $outfile`;
  99.     }

  100.     #
  101.     $MYSQL = qq{mysql -h$host -u$user -p'$pwd' -P$port};
  102.     &mdebug("get_options::MYSQL\n\t$MYSQL");

  103.     # 提取binlog,不需要顯示列定義資訊,用-v,而不用-vv
  104.     $MYSQLBINLOG = qq{mysqlbinlog -v};
  105.     $MYSQLBINLOG .= " --start-position=".$opt{'start-position'} if $opt{'start-position'};
  106.     $MYSQLBINLOG .= " --stop-position=".$opt{'stop-position'} if $opt{'stop-postion'};
  107.     $MYSQLBINLOG .= " --start-datetime='".$opt{'start-datetime'}."'" if $opt{'start-datetime'};
  108.     $MYSQLBINLOG .= " --stop-datetime='$opt{'stop-datetime'}'" if $opt{'stop-datetime'};
  109.     $MYSQLBINLOG .= " $srcfile";
  110.     &mdebug("get_options::MYSQLBINLOG\n\t$MYSQLBINLOG");

  111.     # 檢查binlog中是否含有 ddl sql: CREATE|ALTER|DROP|RENAME
  112.     &check_binlog() unless ($opt{'i'});

  113.     # 不使用mysqlbinlog過濾,USE dbname;方式可能會漏掉某些sql,所以不在mysqlbinlog過濾
  114.     # 指定資料庫
  115.     if ($opt{'d'}){
  116.         my @dbs = split(/,/,$opt{'d'});
  117.         foreach my $db (@dbs){
  118.             $do_dbs{$db}=1;
  119.         }
  120.     }

  121.     # 指定表
  122.     if ($opt{'T'}){
  123.         my @tbs = split(/,/,$opt{'T'});
  124.         foreach my $tb (@tbs){
  125.             $do_tbs{$tb}=1;
  126.         }
  127.     }

  128.     # 提取有效DML SQL
  129.     $ROLLBACK_DML = $MYSQLBINLOG." | grep '^### '";
  130.     # 去掉註釋: '### ' -> ''
  131.     # 刪除首尾空格
  132.     $ROLLBACK_DML .= " | sed 's/###\\s*//g;s/\\s*\$//g'";
  133.     &mdebug("rollback dml\n\t$ROLLBACK_DML");
  134.     
  135.     # 檢查內容是否為空
  136.     my $cmd = "$ROLLBACK_DML | wc -l";
  137.     &mdebug("check contain dml sql\n\t$cmd");
  138.     my $size = `$cmd`;
  139.     chomp($size);
  140.     unless ($size >0){
  141.         &merror("binlog DML is empty:$ROLLBACK_DML");
  142.     };

  143. }    


  144. # ----------------------------------------------------------------------------------------
  145. # Func : check binlog contain DDL
  146. # ----------------------------------------------------------------------------------------
  147. sub check_binlog{
  148.     &mdebug("$PRE_FUNCT check_binlog");
  149.     my $cmd = "$MYSQLBINLOG ";
  150.     $cmd .= " | grep -E -i '^(CREATE|ALTER|DROP|RENAME)' ";
  151.     &mdebug("check binlog has DDL cmd\n\t$cmd");
  152.     my $ddlcnt = `$cmd`;
  153.     chomp($ddlcnt);

  154.     my $ddlnum = `$cmd | wc -l`;
  155.     chomp($ddlnum);
  156.     my $res = 0;
  157.     if ($ddlnum>0){
  158.         # 在ddl sql前面加上字首<DDL>
  159.         $ddlcnt = `echo '$ddlcnt' | sed 's/^//g'`;
  160.         &merror("binlog contain $ddlnum DDL:$MYSQLBINLOG. ddl sql:\n$ddlcnt");
  161.     }

  162.     return $res;
  163. }


  164. # ----------------------------------------------------------------------------------------
  165. # Func : init all table column order
  166. #        if input --database --table params, only get set table column order
  167. # ----------------------------------------------------------------------------------------
  168. sub init_tbcol{
  169.     &mdebug("$PRE_FUNCT init_tbcol");
  170.     # 提取DML語句
  171.     my $cmd .= "$ROLLBACK_DML | grep -E '^(INSERT|UPDATE|DELETE)'";
  172.     # 提取表名,並去重
  173.     #$cmd .= " | awk '{if (\$1 ~ \"^UPDATE\") {print \$2}else {print \$3}}' | uniq ";
  174.     $cmd .= " | awk '{if (\$1 ~ \"^UPDATE\") {print \$2}else {print \$3}}' | sort | uniq ";
  175.     &mdebug("get table name cmd\n\t$cmd");
  176.     open ALLTABLE, "$cmd | " or die "can't open file:$cmd\n";

  177.     while (my $tbname = <ALLTABLE>){
  178.         chomp($tbname);
  179.         #if (exists $tbcol_pos{$tbname}){
  180.         #    next;
  181.         #}
  182.         &init_one_tbcol($tbname) unless (&ignore_tb($tbname));
  183.         
  184.     }
  185.     close ALLTABLE or die "can't close file:$cmd\n";

  186.     # init tb col
  187.     foreach my $tb (keys %tbcol_pos){
  188.         &mdebug("tbname->$tb");
  189.         my %colpos = %{$tbcol_pos{$tb}};
  190.         foreach my $pos (keys %colpos){
  191.             my $col = $colpos{$pos};
  192.             my ($cname,$ctype) = split(/$SPLITER_COL/, $col);
  193.             &mdebug("\tpos->$pos,cname->$cname,ctype->$ctype");
  194.         }
  195.     }
  196. };


  197. # ----------------------------------------------------------------------------------------
  198. # Func : init one table column order
  199. # ----------------------------------------------------------------------------------------
  200. sub init_one_tbcol{
  201.     my $tbname = shift;
  202.     &mdebug("$PRE_FUNCT init_one_tbcol");
  203.     # 獲取表結構及列順序
  204.     my $cmd = $MYSQL." --skip-column-names --silent -e 'desc $tbname'";
  205.     # 提取列名,並拼接
  206.     $cmd .= " | awk -F\'\\t\' \'{print NR\"$SPLITER_COL`\"\$1\"`$SPLITER_COL\"\$2}'";
  207.     &mdebug("get table column infor cmd\n\t$cmd");
  208.     open TBCOL,"$cmd | " or die "can't open desc $tbname;";

  209.     my %colpos;
  210.     while (my $line = <TBCOL>){
  211.         chomp($line);
  212.         my ($pos,$col,$coltype) = split(/$SPLITER_COL/,$line);
  213.         &mdebug("linesss=$line\n\t\tpos=$pos\n\t\tcol=$col\n\t\ttype=$coltype");
  214.         $colpos{$pos} = $col.$SPLITER_COL.$coltype;
  215.     }
  216.     close TBCOL or die "can't colse desc $tbname";

  217.     $tbcol_pos{$tbname} = \%colpos;
  218. }


  219. # ----------------------------------------------------------------------------------------
  220. # Func : rollback sql:    INSERT/UPDATE/DELETE
  221. # ----------------------------------------------------------------------------------------
  222. sub do_binlog_rollback{
  223.     my $binlogfile = "$ROLLBACK_DML ";
  224.     &mdebug("$PRE_FUNCT do_binlog_rollback");

  225.     # INSERT|UPDATE|DELETE
  226.     my $sqltype;
  227.     # WHERE|SET
  228.     my $sqlarea;
  229.     
  230.     my ($tbname, $sqlstr) = ('', '');
  231.     my ($notignore, $isareabegin) = (0,0);

  232.     # output sql file
  233.     open SQLFILE, ">> $outfile" or die "Can't open sql file:$outfile";

  234.     # binlog file
  235.     open BINLOG, "$binlogfile |" or die "Can't open file: $binlogfile";
  236.     while (my $line = <BINLOG>){
  237.         chomp($line);
  238.         if ($line =~ /^(INSERT|UPDATE|DELETE)/){
  239.             # export sql
  240.             if ($sqlstr ne ''){
  241.                 $sqlstr .= ";\n";
  242.                 print SQLFILE $sqlstr;
  243.                 &mdebug("export sql\n\t".$sqlstr);
  244.                 $sqlstr = '';
  245.             }

  246.             if ($line =~ /^INSERT/){
  247.                 $sqltype = $SQLTYPE_IST;
  248.                 $tbname = `echo '$line' | awk '{print \$3}'`;
  249.                 chomp($tbname);
  250.                 $sqlstr = qq{DELETE FROM $tbname};
  251.             }elsif ($line =~ /^UPDATE/){
  252.                 $sqltype = $SQLTYPE_UPD;
  253.                 $tbname = `echo '$line' | awk '{print \$2}'`;
  254.                 chomp($tbname);
  255.                 $sqlstr = qq{UPDATE $tbname};
  256.             }elsif ($line =~ /^DELETE/){
  257.                 $sqltype = $SQLTYPE_DEL;    
  258.                 $tbname = `echo '$line' | awk '{print \$3}'`;
  259.                 chomp($tbname);
  260.                 $sqlstr = qq{INSERT INTO $tbname};
  261.             }

  262.             # check ignore table
  263.             if(&ignore_tb($tbname)){
  264.                 $notignore = 0;
  265.                 &mdebug("#IGNORE#:line:".$line);
  266.                 $sqlstr = '';
  267.             }else{
  268.                 $notignore = 1;
  269.                 &mdebug("#DO#:line:".$line);
  270.             }
  271.         }else {
  272.             if($notignore){
  273.                 &merror("can't get tbname") unless (defined($tbname));
  274.                 if ($line =~ /^WHERE/){
  275.                     $sqlarea = $SQLAREA_WHERE;
  276.                     $sqlstr .= qq{ SET};
  277.                     $isareabegin = 1;
  278.                 }elsif ($line =~ /^SET/){
  279.                     $sqlarea = $SQLAREA_SET;
  280.                     $sqlstr .= qq{ WHERE};
  281.                     $isareabegin = 1;
  282.                 }elsif ($line =~ /^\@/){
  283.                     $sqlstr .= &deal_col_value($tbname, $sqltype, $sqlarea, $isareabegin, $line);
  284.                     $isareabegin = 0;
  285.                 }else{
  286.                     &mdebug("::unknown sql:".$line);
  287.                 }
  288.             }
  289.         }
  290.     }
  291.     # export last sql
  292.     if ($sqlstr ne ''){
  293.         $sqlstr .= ";\n";
  294.         print SQLFILE $sqlstr;
  295.         &mdebug("export sql\n\t".$sqlstr);
  296.     }
  297.     
  298.     close BINLOG or die "Can't close binlog file: $binlogfile";

  299.     close SQLFILE or die "Can't close out sql file: $outfile";

  300.     # 逆序
  301.     # 1!G: 只有第一行不執行G, 將hold space中的內容append回到pattern space
  302.     # h: 將pattern space 拷貝到hold space
  303.     # $!d: 除最後一行都刪除
  304.     my $invert = "sed -i '1!G;h;\$!d' $outfile";
  305.     my $res = `$invert`;
  306.     &mdebug("inverter order sqlfile :$invert");
  307. }

  308. # ----------------------------------------------------------------------------------------
  309. # Func : transfer column pos to name
  310. #    deal column value
  311. #
  312. # &deal_col_value($tbname, $sqltype, $sqlarea, $isareabegin, $line);
  313. # ----------------------------------------------------------------------------------------
  314. sub deal_col_value($$$$$){
  315.     my ($tbname, $sqltype, $sqlarea, $isareabegin, $line) = @_;
  316.     &mdebug("$PRE_FUNCT deal_col_value");
  317.     &mdebug("input:tbname->$tbname,type->$sqltype,area->$sqlarea,areabegin->$isareabegin,line->$line");
  318.     my @vals = split(/=/, $line);
  319.     my $pos = substr($vals[0],1);
  320.     my $valstartpos = length($pos)+2;
  321.     my $val = substr($line,$valstartpos);
  322.     my %tbcol = %{$tbcol_pos{$tbname}};
  323.     my ($cname,$ctype) = split(/$SPLITER_COL/,$tbcol{$pos});
  324.     &merror("can't get $tbname column $cname type") unless (defined($cname) || defined($ctype));
  325.     &mdebug("column infor:cname->$cname,type->$ctype");

  326.     # join str
  327.     my $joinstr;
  328.     if ($isareabegin){
  329.         $joinstr = ' ';
  330.     }else{
  331.         # WHERE 被替換為 SET, 使用 , 連線
  332.         if ($sqlarea eq $SQLAREA_WHERE){
  333.             $joinstr = ', ';
  334.         # SET 被替換為 WHERE 使用 AND 連線
  335.         }elsif ($sqlarea eq $SQLAREA_SET){
  336.             $joinstr = ' AND ';
  337.         }else{
  338.             &merror("!!!!!!The scripts error");
  339.         }
  340.     }
  341.     
  342.     #
  343.     my $newline = $joinstr;

  344.     # NULL value
  345.     if (($val eq 'NULL') && ($sqlarea eq $SQLAREA_SET)){
  346.         $newline .= qq{ $cname IS NULL};
  347.     }else{
  348.         # timestamp: record seconds
  349.         if ($ctype eq 'timestamp'){
  350.             $newline .= qq{$cname=from_unixtime($val)};
  351.         # datetime: @n=yyyy-mm-dd hh::ii::ss
  352.         }elsif ($ctype eq 'datetime'){
  353.             $newline .= qq{$cname='$val'};
  354.         }else{
  355.             $newline .= qq{$cname=$val};
  356.         }
  357.     }
  358.     &mdebug("\told>$line\n\tnew>$newline");
  359.     
  360.     return $newline;
  361. }

  362. # ----------------------------------------------------------------------------------------
  363. # Func : check is ignore table
  364. # params: IN table full name # format:`dbname`.`tbname`
  365. # RETURN:
  366. #        0 not ignore
  367. #        1 ignore
  368. # ----------------------------------------------------------------------------------------
  369. sub ignore_tb($){
  370.     my $fullname = shift;
  371.     # 刪除`
  372.     $fullname =~ s/`//g;
  373.     my ($dbname,$tbname) = split(/\./,$fullname);
  374.     my $res = 0;
  375.     
  376.     # 指定了資料庫
  377.     if ($opt{'d'}){
  378.         # 與指定庫相同
  379.         if ($do_dbs{$dbname}){
  380.             # 指定表
  381.             if ($opt{'T'}){
  382.                 # 與指定表不同
  383.                 unless ($do_tbs{$tbname}){
  384.                     $res = 1;
  385.                 }
  386.             }
  387.         # 與指定庫不同
  388.         }else{
  389.             $res = 1;
  390.         }
  391.     }
  392.     #&mdebug("Table check ignore:$fullname->$res");
  393.     return $res;
  394. }


  395. # ----------------------------------------------------------------------------------------
  396. # Func : print debug msg
  397. # ----------------------------------------------------------------------------------------
  398. sub mdebug{
  399.     my (@msg) = @_;
  400.     print "@msg\n" if ($opt{'debug'});
  401. }


  402. # ----------------------------------------------------------------------------------------
  403. # Func : print error msg and exit
  404. # ----------------------------------------------------------------------------------------
  405. sub merror{
  406.     my (@msg) = @_;
  407.     print ":@msg\n";
  408.     &print_usage();
  409.     exit(1);
  410. }

  411. # ----------------------------------------------------------------------------------------
  412. # Func : print usage
  413. # ----------------------------------------------------------------------------------------
  414. sub print_usage{
  415.     print <<EOF;
  416. ==========================================================================================
  417. Command line options :
  418.     --help                # OUT : print help info
  419.     -f, --srcfile            # IN : binlog file. [required]
  420.     -o, --outfile            # OUT : output sql file. [required]
  421.     -h, --host            # IN : host. default '127.0.0.1'
  422.     -u, --user            # IN : user. [required]
  423.     -p, --password            # IN : password. [required]
  424.     -P, --port            # IN : port. default '3306'
  425.     --start-datetime        # IN : start datetime
  426.     --stop-datetime            # IN : stop datetime
  427.     --start-position        # IN : start position
  428.     --stop-position            # IN : stop position
  429.     -d, --database            # IN : database, split comma
  430.     -T, --table            # IN : table, split comma. [required] set -d
  431.     -i, --ignore            # IN : ignore binlog check contain DDL(CREATE|ALTER|DROP|RENAME)
  432.     --debug                # IN : print debug information

  433. Sample :
  434.    shell> perl binlog-rollback.pl -f 'mysql-bin.000001' -o '/tmp/t.sql' -u 'user' -p 'pwd'
  435.    shell> perl binlog-rollback.pl -f 'mysql-bin.000001' -o '/tmp/t.sql' -u 'user' -p 'pwd' -i
  436.    shell> perl binlog-rollback.pl -f 'mysql-bin.000001' -o '/tmp/t.sql' -u 'user' -p 'pwd' --debug
  437.    shell> perl binlog-rollback.pl -f 'mysql-bin.000001' -o '/tmp/t.sql' -h '192.168.1.2' -u 'user' -p 'pwd' -P 3307
  438.    shell> perl binlog-rollback.pl -f 'mysql-bin.000001' -o '/tmp/t.sql' -u 'user' -p 'pwd' --start-position=107
  439.    shell> perl binlog-rollback.pl -f 'mysql-bin.000001' -o '/tmp/t.sql' -u 'user' -p 'pwd' --start-position=107 --stop-position=10000
  440.    shell> perl binlog-rollback.pl -f 'mysql-bin.000001' -o '/tmp/t.sql' -u 'user' -p 'pwd' -d 'db1,db2'
  441.    shell> perl binlog-rollback.pl -f 'mysql-bin.0000*' -o '/tmp/t.sql' -u 'user' -p 'pwd' -d 'db1,db2' -T 'tb1,tb2'
  442. ==========================================================================================
  443. EOF
  444.     exit;
  445. }


  446. 1;

參考:http://www.oschina.net/code/snippet_617180_34028

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/28939273/viewspace-1971679/,如需轉載,請註明出處,否則將追究法律責任。

相關文章