บันทึกกระบวนการ Hadoop แบ่งตามขอบเขตบล็อกอย่างไร


119

ให้เป็นไปตาม Hadoop - The Definitive Guide

บันทึกตรรกะที่ FileInputFormats กำหนดมักจะไม่พอดีกับบล็อก HDFS อย่างเรียบร้อย ตัวอย่างเช่นระเบียนตรรกะของ TextInputFormat คือเส้นซึ่งจะข้ามขอบเขต HDFS บ่อยกว่าไม่ สิ่งนี้ไม่มีผลต่อการทำงานของโปรแกรมของคุณเช่นเส้นไม่พลาดหรือขาด แต่ก็คุ้มค่าที่จะรู้เพราะมันหมายความว่า data-local maps (นั่นคือแผนที่ที่ทำงานบนโฮสต์เดียวกันกับ ข้อมูลอินพุต) จะทำการอ่านระยะไกลบางส่วน ค่าใช้จ่ายเล็กน้อยสาเหตุนี้ไม่สำคัญตามปกติ

สมมติว่าเส้นบันทึกแบ่งเป็นสองช่วงตึก (b1 และ b2) ผู้ทำแผนที่ประมวลผลบล็อกแรก (b1) จะสังเกตว่าบรรทัดสุดท้ายไม่มีตัวคั่น EOL และดึงข้อมูลที่เหลือของบรรทัดจากบล็อกข้อมูลถัดไป (b2)

ผู้ทำแผนที่ประมวลผลบล็อกที่สอง (b2) ได้อย่างไรว่าเร็กคอร์ดแรกไม่สมบูรณ์และควรดำเนินการโดยเริ่มจากเร็กคอร์ดที่สองในบล็อก (b2)

คำตอบ:


160

คำถามที่น่าสนใจฉันใช้เวลาดูรหัสเพื่อดูรายละเอียดและนี่คือความคิดของฉัน ไคลเอนต์จัดการการแยกโดยInputFormat.getSplitsดังนั้นการดู FileInputFormat จะให้ข้อมูลต่อไปนี้:

  • สำหรับแฟ้มใส่แต่ละได้รับความยาวไฟล์ขนาดบล็อกและคำนวณขนาดแยกเป็นmax(minSize, min(maxSize, blockSize))ที่maxSizeสอดคล้องกับการmapred.max.split.sizeและเป็นminSizemapred.min.split.size
  • แบ่งไฟล์ออกเป็นFileSplits ต่างๆตามขนาดแยกที่คำนวณด้านบน สิ่งที่สำคัญที่นี่เป็นที่แต่ละคนFileSplitจะเริ่มต้นด้วยstartพารามิเตอร์ที่สอดคล้องกับการชดเชยในแฟ้มใส่ ยังไม่มีการจัดการเส้นตรงจุดนั้น ส่วนที่เกี่ยวข้องของโค้ดมีลักษณะดังนี้:

    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;
    }
    

หลังจากนั้นหากคุณดูสิ่งLineRecordReaderที่กำหนดไว้TextInputFormatนั่นคือจุดที่จัดการกับเส้น:

  • เมื่อคุณเริ่มต้นระบบLineRecordReaderจะพยายามสร้างอินสแตนซ์LineReaderซึ่งเป็นนามธรรมเพื่อให้สามารถอ่านบรรทัดFSDataInputStreamได้ มี 2 ​​กรณี:
  • หากมีการCompressionCodecกำหนดตัวแปลงสัญญาณนี้จะรับผิดชอบในการจัดการขอบเขต อาจไม่เกี่ยวข้องกับคำถามของคุณ
  • อย่างไรก็ตามหากไม่มีตัวแปลงสัญญาณนั่นคือสิ่งที่น่าสนใจ: ถ้าstartของคุณInputSplitแตกต่างจาก 0 คุณจะย้อนรอย 1 อักขระแล้วข้ามบรรทัดแรกที่คุณพบซึ่งระบุโดย \ n หรือ \ r \ n (Windows) ! แบ็กแทร็กมีความสำคัญเนื่องจากในกรณีที่ขอบเขตเส้นของคุณเหมือนกับขอบเขตที่แยกสิ่งนี้จะทำให้แน่ใจว่าคุณจะไม่ข้ามบรรทัดที่ถูกต้อง นี่คือรหัสที่เกี่ยวข้อง:

    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;
    

ดังนั้นเนื่องจากมีการคำนวณการแยกในไคลเอนต์ผู้ทำแผนที่จึงไม่จำเป็นต้องทำงานตามลำดับผู้ทำแผนที่ทุกคนรู้อยู่แล้วว่าจำเป็นต้องทิ้งบรรทัดแรกหรือไม่

โดยพื้นฐานแล้วถ้าคุณมี 2 บรรทัดของแต่ละ 100Mb ในไฟล์เดียวกันและเพื่อให้ง่ายขึ้นสมมติว่าขนาดแยกคือ 64Mb จากนั้นเมื่อคำนวณการแยกอินพุตเราจะมีสถานการณ์ต่อไปนี้:

  • แยก 1 ที่มีเส้นทางและโฮสต์ไปยังบล็อกนี้ เริ่มต้นเมื่อเริ่มต้น 200-200 = 0Mb ความยาว 64Mb
  • แยก 2 เริ่มต้นเมื่อเริ่มต้น 200-200 + 64 = 64Mb ความยาว 64Mb
  • แยก 3 เริ่มต้นเมื่อเริ่มต้น 200-200 + 128 = 128Mb ความยาว 64Mb
  • แยก 4 เริ่มต้นเมื่อเริ่มต้น 200-200 + 192 = 192Mb ความยาว 8Mb
  • Mapper A จะประมวลผลการแบ่ง 1 เริ่มต้นเป็น 0 ดังนั้นอย่าข้ามบรรทัดแรกและอ่านบรรทัดเต็มซึ่งเกินขีด จำกัด 64Mb ดังนั้นจึงต้องอ่านระยะไกล
  • Mapper B จะประมวลผลแยก 2 เริ่มต้นคือ! = 0 ดังนั้นให้ข้ามบรรทัดแรกหลัง 64Mb-1byte ซึ่งตรงกับจุดสิ้นสุดของบรรทัดที่ 1 ที่ 100Mb ซึ่งยังอยู่ในการแยก 2 เรามี 28Mb ของบรรทัดในการแยก 2 ดังนั้น อ่านระยะไกล 72Mb ที่เหลือ
  • Mapper C จะประมวลผลแยก 3 เริ่มต้นคือ! = 0 ดังนั้นให้ข้ามบรรทัดแรกหลัง 128Mb-1byte ซึ่งตรงกับจุดสิ้นสุดของบรรทัดที่ 2 ที่ 200Mb ซึ่งเป็นจุดสิ้นสุดของไฟล์ดังนั้นอย่าทำอะไรเลย
  • Mapper D เหมือนกับ mapper C ยกเว้นว่าจะค้นหาบรรทัดใหม่หลังจาก 192Mb-1byte

นอกจากนี้ @PraveenSripati ควรค่าแก่การกล่าวถึงว่ากรณีขอบที่ขอบเขตจะอยู่ที่ \ r ใน \ r \ n จะได้รับการจัดการในLineReader.readLineฟังก์ชันนี้ฉันไม่คิดว่ามันเกี่ยวข้องกับคำถามของคุณ แต่สามารถเพิ่มรายละเอียดเพิ่มเติมได้หากจำเป็น
Charles Menguy

สมมติว่ามีสองบรรทัดที่มี 64MB ที่แน่นอนในอินพุตดังนั้น InputSplits จึงเกิดขึ้นที่ขอบเขตของเส้น ผู้ทำแผนที่จะไม่สนใจบรรทัดในบล็อกที่สองเสมอไปหรือไม่เพราะ start! = 0.
Praveen Sripati

6
@PraveenSripati ในกรณีนี้ผู้ทำแผนที่ตัวที่สองจะเห็น start! = 0 ดังนั้นอักขระ backtrack 1 ซึ่งจะนำคุณกลับมาก่อน \ n ของบรรทัดแรกจากนั้นข้ามขึ้นไปที่ \ n ต่อไปนี้ ดังนั้นมันจะข้ามบรรทัดแรก แต่ประมวลผลบรรทัดที่สองตามที่คาดไว้
Charles Menguy

@CharlesMenguy เป็นไปได้ไหมที่บรรทัดแรกของไฟล์จะถูกข้ามไป? ฉันมีบรรทัดแรกด้วย key = 1 และค่า a จากนั้นมีอีกสองบรรทัดที่มีคีย์เดียวกันอยู่ที่ไหนสักแห่งในไฟล์ key = 1, val = b และ key = 1, val = c สิ่งนี้คือตัวลดของฉันได้รับ {1, [b, c]} และ {1, [a]} แทนที่จะเป็น {1, [a, b, c]} สิ่งนี้จะไม่เกิดขึ้นถ้าฉันเพิ่มบรรทัดใหม่ที่จุดเริ่มต้นของไฟล์ จะเป็นเพราะอะไรครับท่าน?
Kobe-Wan Kenobi

@CharlesMenguy จะเกิดอะไรขึ้นถ้าไฟล์บน HDFS เป็นไฟล์ไบนารี (ตรงข้ามกับไฟล์ข้อความซึ่ง\r\n, \nแสดงถึงการตัดทอนบันทึก)
CᴴᴀZ

17

อัลกอริทึมการลดแผนที่ไม่ทำงานบนบล็อกทางกายภาพของไฟล์ ทำงานกับการแยกอินพุตแบบลอจิคัล การแยกอินพุตขึ้นอยู่กับตำแหน่งที่เขียนบันทึก บันทึกอาจครอบคลุมสองผู้ทำแผนที่

วิธีการตั้งค่าHDFSจะแบ่งไฟล์ขนาดใหญ่มากออกเป็นบล็อกขนาดใหญ่ (ตัวอย่างเช่นขนาด 128MB) และจัดเก็บสำเนาสามชุดของบล็อกเหล่านี้บนโหนดต่างๆในคลัสเตอร์

HDFS ไม่ทราบถึงเนื้อหาของไฟล์เหล่านี้ บันทึกอาจจะเริ่มต้นในบล็อก-Aแต่ในตอนท้ายของบันทึกไว้ว่าอาจจะอยู่ในบล็อก-B

เพื่อแก้ปัญหานี้ Hadoop ใช้การแสดงข้อมูลเชิงตรรกะของข้อมูลที่จัดเก็บในบล็อกไฟล์หรือที่เรียกว่าการแยกอินพุต เมื่อลูกค้างาน MapReduce คำนวณแยกอินพุต , มันออกจากร่างที่บันทึกทั้งเป็นครั้งแรกในบล็อกเริ่มต้นและที่ระเบียนสุดท้ายในการป้องกันปลาย

ประเด็นสำคัญ:

ในกรณีที่เร็กคอร์ดสุดท้ายในบล็อกไม่สมบูรณ์การแบ่งอินพุตจะรวมข้อมูลตำแหน่งสำหรับบล็อกถัดไปและการชดเชยไบต์ของข้อมูลที่จำเป็นในการทำให้เร็กคอร์ดสมบูรณ์

ดูแผนภาพด้านล่าง

ใส่คำอธิบายภาพที่นี่

ดูบทความนี้และคำถาม SE ที่เกี่ยวข้อง: เกี่ยวกับการแยกไฟล์ Hadoop / HDFS

รายละเอียดเพิ่มเติมสามารถอ่านได้จากเอกสารประกอบ

กรอบการลดแผนที่อาศัย InputFormat ของงานเพื่อ:

  1. ตรวจสอบข้อกำหนดอินพุตของงาน
  2. แยกไฟล์อินพุตเป็นอินพุตแบบลอจิคัล InputSplits ซึ่งแต่ละไฟล์จะถูกกำหนดให้กับ Mapper แต่ละตัว
  3. จากนั้น InputSplit แต่ละรายการจะถูกกำหนดให้กับ Mapper แต่ละตัวเพื่อประมวลผล Split อาจจะ tuple InputSplit[] getSplits(JobConf job,int numSplits) คือ API สำหรับดูแลสิ่งเหล่านี้

FileInputFormatซึ่งขยายInputFormatวิธีการใช้งานgetSplits() ดูข้อมูลภายในของวิธีนี้ที่grepcode


7

ฉันเห็นดังต่อไปนี้: InputFormat มีหน้าที่แบ่งข้อมูลออกเป็นส่วนแยกทางตรรกะโดยคำนึงถึงลักษณะของข้อมูล
ไม่มีสิ่งใดป้องกันไม่ให้ทำเช่นนั้นแม้ว่าจะสามารถเพิ่มเวลาในการตอบสนองที่สำคัญให้กับงานได้ - ตรรกะทั้งหมดและการอ่านขอบเขตขนาดแยกที่ต้องการจะเกิดขึ้นในเครื่องมือติดตามงาน
รูปแบบการป้อนข้อมูลบันทึกที่ง่ายที่สุดคือ TextInputFormat มันทำงานดังต่อไปนี้ (เท่าที่ฉันเข้าใจจากโค้ด) - รูปแบบการป้อนข้อมูลสร้างการแบ่งตามขนาดโดยไม่คำนึงถึงบรรทัด แต่ LineRecordReader จะเสมอ:
a) ข้ามบรรทัดแรกในการแบ่ง (หรือบางส่วน) หากไม่ใช่ การแยกแรก
b) อ่านหนึ่งบรรทัดหลังขอบเขตของการแบ่งในตอนท้าย (หากมีข้อมูลดังนั้นจึงไม่ใช่การแยกครั้งสุดท้าย)


Skip first line in the split (or part of it), if it is not the first split- หากบันทึกแรกในบล็อกที่ไม่ใช่บล็อกแรกเสร็จสมบูรณ์แล้วไม่แน่ใจว่าตรรกะนี้จะทำงานอย่างไร
Praveen Sripati

เท่าที่ฉันเห็นโค้ด - แต่ละแยกอ่านว่ามีอะไร + บรรทัดถัดไป ดังนั้นหากเส้นแบ่งไม่อยู่ในขอบเขตบล็อกก็ใช้ได้ จะจัดการอย่างไรเมื่อเส้นแบ่งตรงกับบล็อกที่ถูกผูกไว้ - ต้องเข้าใจ - ฉันจะอ่านโค้ดอีกเล็กน้อย
David Gruzman

3

จากสิ่งที่ฉันเข้าใจเมื่อFileSplitเริ่มต้นสำหรับบล็อกแรกตัวสร้างเริ่มต้นจะถูกเรียกใช้ ดังนั้นค่าเริ่มต้นและความยาวจึงเป็นศูนย์ในตอนแรก เมื่อสิ้นสุดการประมวลผลของบล็อกกำปั้นหากบรรทัดสุดท้ายไม่สมบูรณ์ค่าของความยาวจะมากกว่าความยาวของการแบ่งและมันจะอ่านบรรทัดแรกของบล็อกถัดไปด้วย ด้วยเหตุนี้ค่าเริ่มต้นสำหรับบล็อกแรกจะมากกว่าศูนย์และภายใต้เงื่อนไขนี้LineRecordReaderจะข้ามเส้นกำปั้นของบล็อกที่สอง (ดูแหล่งที่มา )

ในกรณีที่บรรทัดสุดท้ายของบล็อกแรกเสร็จสมบูรณ์ค่าของความยาวจะเท่ากับความยาวของบล็อกแรกและค่าของการเริ่มต้นสำหรับบล็อกที่สองจะเป็นศูนย์ ในกรณีนี้LineRecordReaderจะไม่ข้ามบรรทัดแรกและอ่านบล็อกที่สองจากจุดเริ่มต้น

มีเหตุผล?


2
ในสถานการณ์สมมตินี้ผู้ทำแผนที่ต้องสื่อสารกันและประมวลผลบล็อกตามลำดับเมื่อบรรทัดสุดท้ายในบล็อกหนึ่ง ๆ ไม่สมบูรณ์ ไม่แน่ใจว่าเป็นวิธีนี้หรือไม่
Praveen Sripati

1

จากซอร์สโค้ด hadoop ของ LineRecordReader.java ตัวสร้าง: ฉันพบความคิดเห็นบางส่วน:

// 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;

จากสิ่งนี้ฉันเชื่อว่า hadoop จะอ่านบรรทัดพิเศษหนึ่งบรรทัดสำหรับแต่ละการแยก (เมื่อสิ้นสุดการแยกปัจจุบันอ่านบรรทัดถัดไปในการแยกถัดไป) และหากไม่แยกบรรทัดแรกบรรทัดแรกจะถูกทิ้งไป เพื่อไม่ให้บันทึกบรรทัดสูญหายและไม่สมบูรณ์


0

ผู้ทำแผนที่ไม่ต้องสื่อสาร บล็อกไฟล์อยู่ใน HDFS และผู้ทำแผนที่ปัจจุบัน (RecordReader) สามารถอ่านบล็อกที่มีส่วนที่เหลือของบรรทัดได้หรือไม่ สิ่งนี้เกิดขึ้นเบื้องหลัง

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.