让myqldump为不同的表使用不同的条件

        mysqldump支持使用-w,--where参数为要dump的表定义条件,但这个条件是全局的。也就是说,无论你要dump的是单表,还是多表,所有的表都使用这个条件。

        如果我们要dump多个表,并且不同的表要使用不同的条件,或者是仅想为dump中,某些表指定条件的情况下,dump就无法做到了。如果不考虑数据一致性,那么还可以根据条件分别导出各表。如果要保持所有表的数据一致性,那么除了分批,还要考虑追加dump过程中产生的binlog日志,这就比较头大了。

        最近因为业务的需要,也遇到了需要在保证数据一致性dump的情况下,为dump中的某个表定义条件的需求。思来想去,还是把手伸向了mysqldump源码。

        考虑到自己的C语言还停留在几十年前的trubo c时代,通过源码改造实现一个复杂的条件定义不现实,而且使用上似乎也会变得困难,所以还是老套路,复杂的事让数据库去做,计划通过数据库存储过程去实现条件的定义,mysqldump通过调用存储过程去获得每个表的条件。

        具体在mysqldump的源代码改造体现为:为-w,-where的值增加了一个判断,如果以#打头,表示后面跟的是存储过程,myqldump调用这个存储过程去获得表的条件,存储过程包含dbname、tbname两个参数,这样存储过程可以根据这两个表名知道要为那个表提供条件。如果不是以#打头,则与原始的mysqldump保持相同的处理逻辑。

        具体的mysqldump源码修改如下(基于5.7.35版本,文件:client-->mysqldump.c):

1、目前版本中,-w,-where的值对应的变量是:static char *where,目前仅在函数:static void dump_table(char *table, char *db)中使用,所以在这个函数前加了一个函数,用来实现自定义的条件(也就是根据#标记判断是使用原始条件,还是调用存储过程获取条件)

static char *zj_dump_table_where(char *table, char *db){  
  if ( strncmp(where, "#", 1) == 0 ){
    char        query[QUERY_LENGTH];
    MYSQL_ROW   row;
    MYSQL_RES   *res;
    char        *mywhere=0;
    my_snprintf(query, sizeof(query),
      "call %s('%s', '%s')", where+1, db, table);
    if (!mysql_query_with_error_report(mysql, &res, query)){
      if ((row = mysql_fetch_row(res))){
        mywhere = row ? (char*)row[0] : NULL;
      }
      do{
        res = mysql_use_result(mysql);
        mysql_free_result(res);
      }while (!mysql_next_result(mysql));
    }
    return(mywhere);
  }
  return(where);
}

2、修改函数 static void dump_table(char *table, char *db),将其中使用where的地方改成使用步骤1定义的函数,我的做法是先在函数最前方的变量定义中定义了一个mywhere,并调用步骤1的条件取到条件,然后将使用where的地方改为mywhere。

static void dump_table(char *table, char *db)
{
  char *mywhere=0;  // 定义自己的变量,后续使用 where 的地方改为使用 mywhere
  char ignore_flag;
  char buf[200], table_buff[NAME_LEN+3];
...........................................
// 调用自定义获取 where 条件
  if (where){
    mywhere=zj_dump_table_where(table, db);
  }
...........................................

    dynstr_append_checked(&query_string, " FROM ");
    dynstr_append_checked(&query_string, result_table);

    // 改为使用 mywhere
    // if (where)
    // {
    //   dynstr_append_checked(&query_string, " WHERE ");
    //   dynstr_append_checked(&query_string, where);
    // }
    if (mywhere){
      dynstr_append_checked(&query_string, " WHERE ");
      dynstr_append_checked(&query_string, mywhere);
    }
...........................................
  }
  else
  {
    my_bool freemem= FALSE;
    char const* text= fix_identifier_with_newline(result_table, &freemem);
    print_comment(md_result_file, 0, "\n--\n-- Dumping data for table %s\n--\n",
                  text);
    if (freemem)
      my_free((void*)text);

    dynstr_append_checked(&query_string, "SELECT /*!40001 SQL_NO_CACHE */ * FROM ");
    dynstr_append_checked(&query_string, result_table);

    // 改为使用 mywhere
    // if (where)
    // {
    //   freemem= FALSE;
    //   text= fix_identifier_with_newline(where, &freemem);
    //   print_comment(md_result_file, 0, "-- WHERE:  %s\n", text);
    //   if (freemem)
    //     my_free((void*)text);

    //   dynstr_append_checked(&query_string, " WHERE ");
    //   dynstr_append_checked(&query_string, where);
    // }
    if (mywhere){
      freemem= FALSE;
      text= fix_identifier_with_newline(mywhere, &freemem);
      print_comment(md_result_file, 0, "-- WHERE:  %s\n", text);
      if (freemem)
        my_free((void*)text);

      dynstr_append_checked(&query_string, " WHERE ");
      dynstr_append_checked(&query_string, mywhere);
    }
...........................................
} /* dump_table */

        数据库中的存储过程类似下面这样定义,这个存储过程表示 为:test.t1表使用条件:id>20,为test.t2表使用条件:host='%’,其他表返回空结果集,表示没有条件。

CREATE PROCEDURE p_dump_filter(
	dbname text,
	tbname text
)BEGIN
	IF dbname='test' AND tbname='t1' THEN
		SELECT 'id>20';
	ELSEIF dbname='test' AND tbname='t2' THEN
		SELECT 'host=''%''';
	END IF;
	SELECT NULL LIMIT 0;  --- 这个是必须的,存储过程至少需要返回一个结果集
END;

        当我们需要使用这个改造后的mysqldump时,如果不需要为不同的表定义导出条件,我们只需要按照原始的方式调用即可。如果需要为不同的表定义不同的条件,则先在数据库中创建好存储过程,然后使用参数 -w"#db_name.proc_name" 即可(db_name、proc_name需要替换为实际的库名与存储过程名)。

        值得一提的是,在改造源码去实现db过滤(另一个功能改造)的时候,我还发现了一段有意思的代码,在dump_all_databases这个函数中。它把我之前发布的博文《mysqldump的几个坑》中的二号坑给填上了

    if (mysql_db_found || (!my_strcasecmp(charset_info, row[0], "mysql")))
    {
      if (dump_all_tables_in_db(row[0]))
        result=1;
      mysql_db_found = 1;

【本文在个人微信公共号ZJCXC上同步发表】

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值