DetermineHashedPartitionsJob
功能:
这个任务使用HyperLogLog基数统计方法,通过计算输入数据基数,从而产生合适的ShardSpecs。
Hadoop Index任务IndexGeneratorJob任务就是通过ShardSpecs判断reducer个数等一系列任务属性。
两个阶段:
一、【MR任务】阶段,根据timestamp和dimension计算每个Interval区间的数据量
二、【Load partitions and intervals】阶段,根据MR任务产生的结果,构建List<HadoopyShardSpec>
一、【MR任务】阶段
目标:MapReducer的目标是基于HLL算法,统计数据量在Intervals时间区间的分布情况。所以DetermineHashedPartitionsJob中MR任务输出是Interval区间以及每个区间的数据量<Interval目录,预估基数值>。
1、在DetermineHashedPartitionsJob流程中的MapReduce任务结束后,会以Intervals生成多个目录,每个目录下有partition.json,保存着当前interal的数据量。
关键步骤,如流程图中上半部,
- 创建Job Name
- 设置Mapper、Reducer、Partitioner等
- 判断是否配置Interval参数
- 设置Reducer Num大小
DetermineCardinalityMapper类:
由名字可以看出这个类主要功能是计算输入数据基数
把DetermineCardinalityMapper分成三部分看
1、形成groupKey,由timestamp和inputRow构成
final List<Object> groupKey = Rows.toGroupKey(
rollupGranularity.bucketStart(inputRow.getTimestamp()).getMillis(),
inputRow
);
2、由hyperLogLogs统计每个interval的数据量
hyperLogLogs
.get(interval)
.add(hashFunction.hashBytes(HadoopDruidIndexerConfig.JSON_MAPPER.writeValueAsBytes(groupKey)).asBytes());
3、Mapper的输出格式<interval开始时间戳,该interval的数据量>
for (Map.Entry<Interval, HyperLogLogCollector> entry : hyperLogLogs.entrySet()) {
context.write(
new LongWritable(entry.getKey().getStartMillis()),
new BytesWritable(entry.getValue().toByteArray())
);
}
DetermineCardinalityReducer类
中间逻辑比较简单,直接看reducer输出,<Interval目录,预估基数值>
HadoopDruidIndexerConfig.JSON_MAPPER
.writerWithType(Long.class)
.writeValue(out, aggregate.estimateCardinalityRound());
log.info("文件名:%s, 数据值:%s", outPath.toString(), aggregate.estimateCardinalityRound());
二、【Load partitions and intervals】阶段:
目标:生成下游使用的GranularitySpec配置
关键步骤,如流程图中下半部分
- 判断是否配置Interval参数
- 未配置Interval参数,通过MR输出的Interval目录,生成interval.json配置文件,基于此创建GranularitySpec
- 基于GranularitySpec配置读取每个Interval目录下partition.json
- 计算numberOfShard
计算numberOfShard的公式,假设数据总行数numRows=100,配置targetPartitionSize=30,则numberOfShards=4。
最终形成的4个Segment,每个Segment有25行数据
final int numberOfShards = (int) Math.ceil((double) numRows / config.getTargetPartitionSize());
- 生成shardSpec
【Load partitions and intervals】阶段,若没有配置GranularitySpec.Interval的情况下,会通过上一步MR任务产生的Intervals目录,形成interval.json。interval.json保存各个Intervals区间,例如["2011-04-10T00:00:00.000Z/2011-04-11T00:00:00.000Z","2011-04-11T00:00:00.000Z/2011-04-12T00:00:00.000Z","2011-04-12T00:00:00.000Z/2011-04-13T00:00:00.000Z"]。
然后通过这些Intervals创建GranularitySpec配置。
根据GranularitySpec,形成List<HadoopyShardSpec>。后续将根据List<HadoopyShardSpec>生成数据处理任务
DetermineHashedPartitionsJob生成的结果文件
Interval区间目录:
[mymac@16:33:57]/var/folders/pv/dkqbk3wj51b7yb4r5wd9qgdw0000gn/T/druid1556516960499050846/test_schema/2021-08-18T082845.340Z_03142bf7483b4a39a53db168f0d71501$ ll
total 0
drwxr-xr-x 3 mymac staff 96 8 18 16:28 groupedData
drwxr-xr-x 4 mymac staff 128 8 18 16:28 20110410T000000.000Z_20110411T000000.000Z
drwxr-xr-x 4 mymac staff 128 8 18 16:28 20110411T000000.000Z_20110412T000000.000Z
drwxr-xr-x 4 mymac staff 128 8 18 16:28 20110412T000000.000Z_20110413T000000.000Z
drwxr-xr-x 4 mymac staff 128 8 18 16:28 20110413T000000.000Z_20110414T000000.000Z
drwxr-xr-x 4 mymac staff 128 8 18 16:28 20110414T000000.000Z_20110415T000000.000Z
drwxr-xr-x 4 mymac staff 128 8 18 16:28 20110415T000000.000Z_20110416T000000.000Z
其中一个Interval下的数据量,保存在partitions.json文件中
[mymac@16:33:23]/var/folders/pv/dkqbk3wj51b7yb4r5wd9qgdw0000gn/T/druid1556516960499050846/test_schema/2021-08-18T082845.340Z_03142bf7483b4a39a53db168f0d71501/20110410T000000.000Z_20110411T000000.000Z$ ll
total 8
-rw-r--r-- 1 mymac staff 2 8 18 16:28 partitions.json
[mymac@19:05:42]/var/folders/pv/dkqbk3wj51b7yb4r5wd9qgdw0000gn/T/druid1556516960499050846/test_schema/2021-08-18T082845.340Z_03142bf7483b4a39a53db168f0d71501/20110410T000000.000Z_20110411T000000.000Z$ cat partitions.json
13
疑问:
interval.json,partition.json什么时候生成的?
partition.json是MR产生的结果文件保存在某个Interval目录下,存储数据是该Interval的数据量
interval.json在配置文件中没有配置Interval的情况下,DetermineHashedPartitionsJob会根据MR产生的Intervals目录,构建interval.json,保存的就是Interval集合
Mapper,Reducer逻辑是什么,Reducer最终输出什么?
Reducer输出partition.json,输出<Interval目录,预估基数值>