Hadoop işlem kayıtları blok sınırlarına nasıl bölünür?


119

Göre Hadoop - The Definitive Guide

FileInputFormats tarafından tanımlanan mantıksal kayıtlar, genellikle HDFS bloklarına düzgün bir şekilde sığmaz. Örneğin, TextInputFormat'ın mantıksal kayıtları, HDFS sınırlarını hiç olmadığı kadar sıklıkla aşan çizgilerdir. Bunun programınızın işleyişiyle bir ilgisi yoktur - örneğin, satırlar gözden kaçmaz ya da kopmaz - ancak veri-yerel haritalar (yani, kendi bilgisayarlarıyla aynı ana bilgisayarda çalışan haritalar) anlamına geldiği için bilmeye değer. giriş verileri) bazı uzaktan okumalar gerçekleştirecektir. Bunun neden olduğu hafif ek yük normalde önemli değildir.

Bir kayıt çizgisinin iki bloğa (b1 ve b2) bölündüğünü varsayalım. İlk bloğu (b1) işleyen eşleyici, son satırın bir EOL ayırıcısı olmadığını fark edecek ve satırın kalanını bir sonraki veri bloğundan (b2) alacaktır.

İkinci bloğu (b2) işleyen eşleyici, ilk kaydın eksik olduğunu ve (b2) bloğundaki ikinci kayıttan başlayarak işlem yapması gerektiğini nasıl belirler?

Yanıtlar:


160

İlginç soru, detaylar için koda bakarak biraz zaman geçirdim ve işte düşüncelerim. InputFormat.getSplitsBölmeler istemci tarafından ele alınır , bu nedenle FileInputFormat'a bir bakış aşağıdaki bilgileri verir:

  • Her giriş dosya için dosya uzunluğu, blok boyutu elde gibi bölünmüş boyutunu hesaplamak max(minSize, min(maxSize, blockSize))burada maxSizekarşılık için mapred.max.split.sizeve minSizebir mapred.min.split.size.
  • FileSplitYukarıda hesaplanan bölme boyutuna göre dosyayı farklı 'lere bölün. Burada önemli olan, her birinin girdi dosyasındaki ofsete karşılık gelen FileSplitbir startparametre ile başlatılmasıdır . Bu noktada hala çizgilerle ilgili bir işlem yok. Kodun ilgili kısmı şuna benzer:

    while (((double) bytesRemaining)/splitSize > SPLIT_SLOP) {
      int blkIndex = getBlockIndex(blkLocations, length-bytesRemaining);
      splits.add(new FileSplit(path, length-bytesRemaining, splitSize, 
                               blkLocations[blkIndex].getHosts()));
      bytesRemaining -= splitSize;
    }
    

Bundan sonra, LineRecordReaderhangisinin tanımladığına bakarsanız TextInputFormat, satırların işlendiği yer burasıdır:

  • Sizin başlattığınızda, satırları okuyabilmek için bir soyutlama olan LineRecordReadera'yı somutlaştırmaya çalışır . 2 durum var:LineReaderFSDataInputStream
  • CompressionCodecTanımlanmış bir kod varsa, bu codec bileşeni sınırları ele almaktan sorumludur. Muhtemelen sorunuzla alakalı değildir.
  • Şeyler ilginç nerede ancak hiçbir codec yoksa: eğer startsenin içinde InputSplit0'dan farklıdır, o zaman 1 karakterini sarfınazar ve o sırada Belirlediğiniz karşılaşabileceğiniz ilk satırı atlamak \ ya \ r \ n (Windows) n ! Geri izleme önemlidir çünkü satır sınırlarınızın bölünmüş sınırlarla aynı olması durumunda, bu geçerli satırı atlamamanızı sağlar. İşte ilgili kod:

    if (codec != null) {
       in = new LineReader(codec.createInputStream(fileIn), job);
       end = Long.MAX_VALUE;
    } else {
       if (start != 0) {
         skipFirstLine = true;
         --start;
         fileIn.seek(start);
       }
       in = new LineReader(fileIn, job);
    }
    if (skipFirstLine) {  // skip first line and re-establish "start".
      start += in.readLine(new Text(), 0,
                        (int)Math.min((long)Integer.MAX_VALUE, end - start));
    }
    this.pos = start;
    

Böylelikle bölmeler istemcide hesaplandığından, eşleyicilerin sırayla çalışması gerekmez, her eşleyici ilk satırı atıp atmayacağını zaten bilir.

Yani temelde aynı dosyada her 100Mb'den 2 satırınız varsa ve basitleştirmek için bölünmüş boyutun 64Mb olduğunu varsayalım. Daha sonra girdi bölünmeleri hesaplandığında, aşağıdaki senaryoya sahip olacağız:

  • Bu bloğun yolunu ve ana bilgisayarları içeren Bölünmüş 1. Başlangıç ​​200-200 = 0Mb, uzunluk 64Mb.
  • Bölünme 2, 200-200 + 64 = 64Mb, uzunluk 64Mb'de başlatıldı.
  • Bölünmüş 3, başlangıç ​​200-200 + 128 = 128Mb, uzunluk 64Mb'de başlatıldı.
  • Bölünmüş 4, başlangıç ​​200-200 + 192 = 192Mb, uzunluk 8Mb'de başlatıldı.
  • Eşleştirici A, bölme 1'i işleyecektir, başlangıç ​​0'dır, bu nedenle ilk satırı atlamayın ve 64Mb sınırını aşan tam bir satırı okuyarak uzaktan okuma gerektirir.
  • Eşleyici B, bölme 2'yi işleyecektir, başlangıç! = 0'dır, bu nedenle 64 Mb-1 bayttan sonraki ilk satırı atlayın, bu da hala bölünmüş 2'de olan 100Mb'de satır 1'in sonuna karşılık gelir, bölme 2'de satırın 28 Mb'ı var, yani uzaktan kalan 72Mb'yi okuyun.
  • Eşleştirici C, bölme 3'ü işleyecektir, başlangıç! = 0'dır, bu nedenle 128Mb-1byte'dan sonraki ilk satırı atlayın, bu da dosyanın sonu olan 200Mb'de 2. satırın sonuna karşılık gelir, bu yüzden hiçbir şey yapmayın.
  • Eşleştirici D, 192Mb-1 bayttan sonra yeni bir satır araması dışında eşleştirici C ile aynıdır.

Ayrıca @PraveenSripati, bir sınırın dönüşte \ r \ nde olacağı uç durumların işlevde ele alındığını belirtmekte fayda var, sorunuzla LineReader.readLineilgili olduğunu düşünmüyorum, ancak gerekirse daha fazla ayrıntı ekleyebilir.
Charles Menguy

Girişte tam 64MB olan iki satır olduğunu ve bu nedenle Giriş Bölmelerinin tam olarak satır sınırlarında gerçekleştiğini varsayalım. Öyleyse, eşleştirici ikinci bloktaki satırı her zaman yok sayacak mı çünkü start! = 0.
Praveen Sripati

6
@PraveenSripati Bu durumda, ikinci eşleyici start! = 0 değerini görecektir, bu nedenle 1 karakteri geriye doğru takip edin, bu sizi ilk satırın \ n hemen öncesine götürür ve ardından sonraki \ n'ye atlar. Böylece ilk satırı atlayacak, ancak ikinci satırı beklendiği gibi işleyecektir.
Charles Menguy

@CharlesMenguy, dosyanın ilk satırının bir şekilde atlanması mümkün mü? Somut olarak, anahtar = 1 ve değer a olan ilk satırım var, sonra dosyanın herhangi bir yerinde aynı anahtara sahip iki satır daha var, anahtar = 1, val = b ve anahtar = 1, val = c. Mesele şu ki, redüktörüm {1, [a, b, c]} yerine {1, [b, c]} ve {1, [a]} alıyor. Dosyamın başına yeni satır eklersem bu olmaz. Nedeni ne olabilir efendim?
Kobe-Wan Kenobi

@CharlesMenguy Ya HDFS'deki dosya ikili bir dosyaysa (kayıt kesmeyi \r\n, \ntemsil eden metin dosyasının aksine )?
CᴴᴀZ

17

Harita Azaltma algoritması dosyanın fiziksel blokları üzerinde çalışmaz. Mantıksal girdi bölmeleri üzerinde çalışır. Giriş bölmesi, kaydın nereye yazıldığına bağlıdır. Bir kayıt, iki Haritacıya yayılabilir.

Yolu HDF'ler kurulduktan bu (128MB ölçme, örneğin) büyük bloklar halinde çok büyük dosyalar ayırır ve saklar kümedeki farklı düğümlerde bu blokların üç nüsha.

HDFS, bu dosyaların içeriğiyle ilgili hiçbir bilgiye sahip değildir. Blok-a'da bir kayıt başlatılmış olabilir, ancak bu kaydın sonu Blok-b'de bulunabilir .

Bu sorunu çözmek için Hadoop, giriş bölmeleri olarak bilinen dosya bloklarında depolanan verilerin mantıksal bir temsilini kullanır. Bir MapReduce iş istemci hesaplarken giriş bölmelerini , bir blok içinde ilk tam kayıt başlar ve nerede nereye rakamlar blok uçlarına son rekor .

Kilit nokta:

Bir bloktaki son kaydın eksik olduğu durumlarda, giriş bölümü, bir sonraki blok için konum bilgilerini ve kaydı tamamlamak için gereken verilerin bayt ofsetini içerir.

Aşağıdaki şemaya bir göz atın.

görüntü açıklamasını buraya girin

Bu makaleye ve ilgili SE sorusuna bir göz atın : Hadoop / HDFS dosya bölme hakkında

Belgelerden daha fazla ayrıntı okunabilir

Map-Reduce çerçevesi, aşağıdakileri yapmak için işin InputFormat'ına dayanır:

  1. İşin girdi özelliğini doğrulayın.
  2. Giriş dosyalarını, her biri ayrı bir Eşleştiriciye atanan mantıksal InputSplits'e bölün.
  3. Her bir InputSplit daha sonra işlenmek üzere ayrı bir Eşleştiriciye atanır. Bölünmüş olabilir . InputSplit[] getSplits(JobConf job,int numSplits) bu şeylerle ilgilenen API'dir.

InputFormatUygulanan getSplits() yöntemini genişleten FileInputFormat . Grepcode'da bu yöntemin iç kısımlarına bir göz atın


7

Bunu şu şekilde görüyorum: InputFormat, verinin doğasını hesaba katarak verileri mantıksal bölmelere bölmekten sorumludur.
İşe önemli bir gecikme ekleyebilse de, hiçbir şey bunu engellemiyor - tüm mantık ve istenen bölünmüş boyut sınırları etrafındaki okuma, iş izleyicide gerçekleşecek.
En basit kayıta duyarlı girdi biçimi TextInputFormat'tır. Şu şekilde çalışıyor (koddan anladığım kadarıyla) - girdi formatı, satırlardan bağımsız olarak boyuta göre bölmeler oluşturur, ancak LineRecordReader her zaman:
a) Bölmedeki ilk satırı (veya bir kısmını) atla, değilse ilk bölme
b) Sonunda bölmenin sınırından sonra bir satır okuyun (veri mevcutsa, bu nedenle son bölme değildir).


Skip first line in the split (or part of it), if it is not the first split- birinci olmayan bloktaki ilk kayıt tamamlanmışsa, bu mantığın nasıl çalışacağından emin değilsiniz.
Praveen Sripati

Kodu gördüğüm kadarıyla - her bölüm, + sonraki satırda ne olduğunu okudu. Yani satır sonu blok sınırında değilse - sorun değil. Satır sonu tam olarak blok sınırındayken tam olarak nasıl ele alınacak - anlaşılmalıdır - kodu biraz daha
okuyacağım

3

Anladığım kadarıyla FileSplit, ilk blok için başlatıldığında, varsayılan kurucu çağrılır. Bu nedenle başlangıç ​​ve uzunluk değerleri başlangıçta sıfırdır. İlk bloğun işlenmesinin sonunda, son satır eksikse, o zaman uzunluk değeri bölünmenin uzunluğundan daha büyük olacak ve bir sonraki bloğun ilk satırını da okuyacaktır. Bundan dolayı, ilk blok için başlangıç ​​değeri sıfırdan büyük olacaktır ve bu koşul altında LineRecordReader, ikinci bloğun ilk satırını atlayacaktır. ( Kaynağa bakın )

İlk bloğun son satırının tamamlanması durumunda, uzunluk değeri ilk bloğun uzunluğuna eşit olacak ve ikinci bloğun başlangıç ​​değeri sıfır olacaktır. Bu durumda, LineRecordReaderilk satırı atlamayacak ve ikinci bloğu baştan okuyacaktır.

Mantıklı?


2
Bu senaryoda, haritacıların birbirleriyle iletişim kurması ve belirli bir bloktaki son satır tamamlanmadığında blokları sırayla işlemesi gerekir. Bu şekilde çalışıp çalışmadığından emin değilim.
Praveen Sripati

1

LineRecordReader.java'nın hadoop kaynak kodundan kurucu: Bazı yorumlar buluyorum:

// If this is not the first split, we always throw away first record
// because we always (except the last split) read one extra line in
// next() method.
if (start != 0) {
  start += in.readLine(new Text(), 0, maxBytesToConsume(start));
}
this.pos = start;

Bundan, hadoop'un her bölme için fazladan bir satır okuyacağına inanıyorum (mevcut bölmenin sonunda, bir sonraki bölmede sonraki satırı okuyacak) ve ilk bölme değilse, ilk satır atılacak. böylece hiçbir satır kaydı kaybolmaz ve eksik kalmaz


0

Haritacıların iletişim kurması gerekmez. Dosya blokları HDFS içindedir ve mevcut eşleyici (RecordReader), satırın kalan kısmına sahip bloğu okuyabilir. Bu perde arkasında olur.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.