如何在hadoop集群上处理id生成



我正在hadoop集群上构建一个字典,需要为每个令牌生成一个数字id。我该怎么做?

您有两个问题。首先,您需要确保为每个令牌分配一个id。要做到这一点,您应该按记号对记录进行排序和分组,并在reducer中进行赋值。一旦您确保每个令牌只调用一次reducer方法,您就可以使用上下文中的分区编号和reducer维护的唯一数字id(每个分区一个实例)-只需在setup方法中使用初始化为1的实例变量,并在reduce方法中递增即可。

为了避免不属于业务逻辑的同步、排序和分组,您可以使用一些技巧,这样会更快。

最简单的方法是在Reducer中生成UUID,每个键生成一个UUID UUID.randomUUID(),但这些不是数字。

如果你想要连续的数字id序列,并且你的输出足够小,可以用一个reducer来处理,那么通过org.apache.hadop.mapreduce.job.setNumReduceTasks(int tasks)为作业强制使用一个redener,这样所有的键都将指向一个reductor。

如果Mapper的输出对于单个Reducer来说仍然太大,并且您不关心id的序列连续性,或者您的字典可以进行分区,那么您可以使用Partitioner使用一些技巧(请参阅)。其想法是,您可以在逻辑上将密钥划分为已知长度的N个范围(例如,范围1可以有以1开头的1密耳密钥,范围4可以有以3500000开头的500个id,等等)。逻辑:

  1. 将Reducer的数量设置为N(JobConf.setNumReduceTasks(N))
  2. 实现了将键与其范围相匹配的Partitioner,它将把同一范围内的所有键指向同一个Reducer
  3. 在Reducer中有一个以范围的第一个id开始的计数器(在第一次调用reduce时,使用与partitioner中相同的逻辑来识别哪个范围是Reducer)

如果你对范围没有任何商业知识,你可以花一些时间对键进行区分,并计算范围及其长度。有了这个,您可以在结果集中获得连续的id序列。

如果你不想花时间做不同的关键点,那么目标是用不同的数字为每个Reducer启动id(Reducer 1生成仅以1开头的id(1、10、124523、1341243),Reducer 2生成以2开头的ID(2、23、234234532),等等)。要做到这一点,请计算密钥第一个字节的mod 10,并强制10个Reducer,将0直接指向与1相同的分区(主要原因是没有以0开头的2位整数,这可能会导致与其他分区的id发生冲突),因此分区0的输出为空。然后在reducer端将计数器追加(连接2个字符串!!!)到(键mod 10的第一个字节),其中0变为1。每个reducer都有一个从1到无穷大的计数器,可以从包含该分区使用的最后一个id的文件中初始化。

Ex。

key = "abc", ascii of 'a' is 97, 97 % 10 = 7 and id for that key is '7' + '1' = '71', 
for "asd" it will be '7' + '244' = '7244', 
for "bbv" is '8' + '1' = '81', 
for "bgh" is '8' + '2' = '82', 
for "ddv", 'd' is ascii 100, 100 % 100 = 0 , convert to 1, '1' + '1' = '11', 
for "edv" is 101 % 100 = 1, '1' + '2234' = '12234'. 

因为所有键都以不同的数字开头,所以它们从不重叠,因此不需要在多个Reducer之间同步。它由mod结果和计数器的字符串连接来保证,所以不会溢出到下一个前缀/前导数字。另一个优点是您不需要进行任何预分类,这不是您业务逻辑的一部分。当Reducer关闭时,它可以将id生成中使用的最后一个计数器写入文件,该计数器可以在下一次运行中使用,并在字典的分区内提供id的连续性。

要将分区数量从10增加到100,请使用mod 100并将个位数的结果与2位数的结果合并(我们再次浪费10个减少器的输出)。例如,10%100=10,1%100=1转换为10,102%100=2转换为20,将"0"作为字符串相加或乘以10。目标是使所有前缀都具有相同数量的数字,在本例中为2。

通过巧妙的逻辑,我们可以避免浪费跳过的分区(在mod 100的情况下为0,或1,2,9)。

警告:此逻辑易受数据倾斜的影响。

我希望它能有所帮助,干杯

我们使用的另一个选项是为任务分配ID块,为此您需要一种管理事务的机制。我们使用了AWS的SimpleDB来跟踪已使用或未使用的事务和ID。

在作业开始时,我们将整个ID块锁定在Simple DB上(只有一个属性更改为"locked"),然后每个任务都调用Simple DB来"检出"它使用的ID块。

如果任务失败,它永远不会"解锁"块,也永远不会更新上次使用的ID,因此这些ID不会被失败的任务消耗。最后,当我们解锁整个ID块时,我们会解锁任何被失败任务占用的ID块。

我们将通过从简单数据库导入/导出ID块来改进这一过程,这样我们就不必更新外部资源。相反,我们可以从与数据一起存储的文件中读取可用的ID,将它们写入简单的数据库,使用简单的数据库来协调将块分配给各个任务(映射器、还原器),然后将它们写出来并取得成功。

一旦我们做出了改进,我们可能会公开代码,所以如果有人对这种方法感兴趣,请随时与我联系,如果我们完成了,我会用代码更新这篇文章。

相关内容

  • 没有找到相关文章

最新更新