继续浏览精彩内容
慕课网APP
程序员的梦工厂
打开
继续
感谢您的支持,我会继续努力的
赞赏金额会直接到老师账户
将二维码发送给自己后长按识别
微信支付
支付宝支付

Datax3.0中配置解读(5)

small_925_ant
关注TA
已关注
手记 69
粉丝 6395
获赞 157

这里以Mysql插件为例子简单分析一下"splitPk": "db_id"这个配置

首先所有的关系型数据库都走通用的处理Reader,com.alibaba.datax.plugin.rdbms.reader.CommonRdbmsReader

1- 当进行split的时候会进行切分获取channel的个数

进入到com.alibaba.datax.plugin.rdbms.reader.CommonRdbmsReader下面

public List<Configuration> split(Configuration originalConfig,
                                         int adviceNumber) {
            return ReaderSplitUtil.doSplit(originalConfig, adviceNumber);
}


2- 继续进入doSplit方法

com.alibaba.datax.plugin.rdbms.reader.util.ReaderSplitUtil

此处会去判断是否是table配置的模式

if (isTableMode) {
            // adviceNumber这里是channel数量大小, 即datax并发task数量
            // eachTableShouldSplittedNumber是单表应该切分的份数, 向上取整可能和adviceNumber没有比例关系了已经
            eachTableShouldSplittedNumber = calculateEachTableShouldSplittedNumber(
                    adviceNumber, originalSliceConfig.getInt(Constant.TABLE_NUMBER_MARK));
}

如果前面计算的adviceNumber=3  配置了一个table  则每个表需要切分的channel是3/1=3

初始化一个List保存切分后的任务配置结果

List<Configuration> splittedConfigs = new ArrayList<Configuration>();

3- 下面进入核心代码的分析

// 说明是配置的 table 方式
if (isTableMode) {
    // 已在之前进行了扩展和`处理,可以直接使用
    List<String> tables = connConf.getList(Key.TABLE, String.class);

    Validate.isTrue(null != tables && !tables.isEmpty(), "您读取数据库表配置错误.");

    String splitPk = originalSliceConfig.getString(Key.SPLIT_PK, null);

     /**
      * 1-判断是否配置了splitPk,如果没有配置则每个table都会当成一个任务,生成一个配置文件给任务运行使用
      * 2-如果配置了splitPk,如果只配了一个table,则重新计算eachTableShouldSplittedNumber=eachTableShouldSplittedNumber * 5;
      * 3-如果配置了多个table,eachTableShouldSplittedNumber不变,然后循环对每个表进行切分splitSingleTable
     */
    //最终切分份数不一定等于 eachTableShouldSplittedNumber
    boolean needSplitTable = eachTableShouldSplittedNumber > 1
            && StringUtils.isNotBlank(splitPk);
    if (needSplitTable) {
        if (tables.size() == 1) {
            //原来:如果是单表的,主键切分num=num*2+1
            // splitPk is null这类的情况的数据量本身就比真实数据量少很多, 和channel大小比率关系时,不建议考虑
            //eachTableShouldSplittedNumber = eachTableShouldSplittedNumber * 2 + 1;// 不应该加1导致长尾
            
            //考虑其他比率数字?(splitPk is null, 忽略此长尾)
            eachTableShouldSplittedNumber = eachTableShouldSplittedNumber * 5;
        }
        // 尝试对每个表,切分为eachTableShouldSplittedNumber 份
        for (String table : tables) {
            tempSlice = sliceConfig.clone();
            tempSlice.set(Key.TABLE, table);

            List<Configuration> splittedSlices = SingleTableSplitUtil
                    .splitSingleTable(tempSlice, eachTableShouldSplittedNumber);

            splittedConfigs.addAll(splittedSlices);
        }
    } else {
        for (String table : tables) {
            tempSlice = sliceConfig.clone();
            tempSlice.set(Key.TABLE, table);
            String queryColumn = HintUtil.buildQueryColumn(jdbcUrl, table, column);
            tempSlice.set(Key.QUERY_SQL, SingleTableSplitUtil.buildQuerySql(queryColumn, table, where));
            splittedConfigs.add(tempSlice);
        }
    }
} else {
    // 说明是配置的 querySql 方式
    List<String> sqls = connConf.getList(Key.QUERY_SQL, String.class);

    // TODO 是否check 配置为多条语句??
    for (String querySql : sqls) {
        tempSlice = sliceConfig.clone();
        tempSlice.set(Key.QUERY_SQL, querySql);
        splittedConfigs.add(tempSlice);
    }
}

此处主要分为两种,一种是table模式配置,一种是querysql模式配置

querySql模式相对简单,配置了几个sql,就会生成几个任务的配置文件。

我们主要关注table模式

主要的流程:

 /**
 * 1-判断是否配置了splitPk,如果没有配置则每个table都会当成一个任务,生成一个配置文件给任务运行使用
 * 2-如果配置了splitPk,如果只配了一个table,则重新计算eachTableShouldSplittedNumber=eachTableShouldSplittedNumber * 5;
 * 3-如果配置了多个table,eachTableShouldSplittedNumber不变,然后循环对每个表进行切分splitSingleTable
*/


4- 接下来关注splitSingleTable方法

大体流程是:

首先会根据 Configuration configuration, int adviceNum  配置文件信息和需要切分的个数进行切分

会计算出splitPk的最大最小值,

然后按照adviceNum进行分割,然后生成具体的sql

比如:

如果我配置了

 "setting": {
      "speed": {
        "channel":15
  }

则此时通过上面的流程最后可以计算出adviceNum为75

在我的mysql表中有3089条数据

此时他会返回下面的配置总共75个有效配置,主要是看querySql,每个配置的条件都会不同。这只是其中一个

{
"column": "id,username,telephone",
"columnList": ["id", "username", "telephone"],
"fetchSize": -2147483648,
"isTableMode": true,
"jdbcUrl": "jdbc:mysql://localhost:3306/datax?yearIsDateType=false&zeroDateTimeBehavior=convertToNull&tinyInt1isBit=false&rewriteBatchedStatements=true",
"loadBalanceResourceMark": "localhost",
"password": "root123",
"pkType": "pkTypeLong",
"querySql": "select id,username,telephone from user  where  (547 <= id AND id < 588) ",
"splitPk": "id",
"table": "user",
"tableNumber": 1,
"username": "root"
}
第一个sql:
"querySql": "select id,username,telephone from user  where  (1 <= id AND id < 43) "
最后一个sql是:
"querySql": "select id,username,telephone from user  where  id IS NULL"
最后一个有效sql是:
"querySql": "select id,username,telephone from user  where  (3048 <= id AND id <= 3089) "

可以看到和表中的数据一致,这里主要是把数据量过大的表,按照配置计算出的channel进行切分,
然后生成分段的sql,一个sql对应一个任务,进行执行。

5- 综上,此时总共会产生75个task,对应75个channel

后面会根据taskGroup的个数,来对着75个任务进行分组,然后提交到线程池中并发执行任务。

切分后可以防止数量过大,select 抽取慢的效果,如果需要完整的job配置文件,可以留言





打开App,阅读手记
0人推荐
发表评论
随时随地看视频慕课网APP

热门评论

博主想问一下,想做整个数据库的同步,只能做批处理吗?,还是可以配置?

博主好,请问Configuration configuration, int adviceNum  配置文件存放在哪里?

我配置的split参数并没有产生效果,表没有切分。请问具体splitPK该如何配置呢?

感谢!


查看全部评论