这里以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配置文件,可以留言
共同学习,写下你的评论
评论加载中...
作者其他优质文章