วิธีอ่านบทความนี้: ฉันขอโทษสำหรับข้อความที่ยาวและวุ่นวายมาก เพื่อช่วยคุณประหยัดเวลา ฉันจะเริ่มแต่ละบทด้วยคำนำ "สิ่งที่ฉันเรียนรู้" ซึ่งจะสรุปสาระสำคัญของบทด้วยหนึ่งหรือสองประโยค
“แสดงวิธีแก้ปัญหาให้ฉันดูสิ!” หากคุณต้องการดูว่าฉันมาจากไหน ให้ข้ามไปที่บท “มีความคิดสร้างสรรค์มากขึ้น” แต่ฉันคิดว่ามันน่าสนใจและมีประโยชน์มากกว่าถ้าอ่านเกี่ยวกับความล้มเหลว
เมื่อเร็วๆ นี้ ฉันได้รับมอบหมายให้ตั้งค่ากระบวนการสำหรับประมวลผลลำดับ DNA ดิบจำนวนมาก (ในทางเทคนิคแล้วคือชิป SNP) ความจำเป็นคือการได้รับข้อมูลเกี่ยวกับตำแหน่งทางพันธุกรรมที่กำหนดอย่างรวดเร็ว (เรียกว่า SNP) สำหรับการสร้างแบบจำลองในภายหลังและงานอื่น ๆ การใช้ R และ AWK ช่วยให้ฉันสามารถล้างและจัดระเบียบข้อมูลได้อย่างเป็นธรรมชาติ ซึ่งช่วยเร่งการประมวลผลคำค้นหาได้อย่างมาก นี่ไม่ใช่เรื่องง่ายสำหรับฉันและต้องทำซ้ำหลายครั้ง บทความนี้จะช่วยคุณหลีกเลี่ยงข้อผิดพลาดบางอย่างของฉันและแสดงให้คุณเห็นว่าฉันลงเอยอย่างไร
ขั้นแรกให้อธิบายเบื้องต้นบางประการ
ข้อมูล
ศูนย์ประมวลผลข้อมูลทางพันธุกรรมของมหาวิทยาลัยของเราให้ข้อมูลในรูปแบบของ TSV ขนาด 25 TB ฉันได้รับมันแบ่งออกเป็น 5 แพ็คเกจ ซึ่งบีบอัดโดย Gzip แต่ละแพ็คเกจมีไฟล์ขนาดสี่กิกะไบต์ประมาณ 240 ไฟล์ แต่ละแถวมีข้อมูลสำหรับหนึ่ง SNP จากบุคคลหนึ่งราย โดยรวมแล้วมีการส่งข้อมูล SNP ประมาณ 2,5 ล้าน SNP และผู้คนประมาณ 60 คน นอกเหนือจากข้อมูล SNP แล้ว ไฟล์ดังกล่าวยังมีคอลัมน์จำนวนมากที่มีตัวเลขที่สะท้อนถึงคุณลักษณะต่างๆ เช่น ความเข้มในการอ่าน ความถี่ของอัลลีลที่แตกต่างกัน เป็นต้น โดยรวมแล้วมีประมาณ 30 คอลัมน์ที่มีค่าไม่ซ้ำกัน
เป้า
เช่นเดียวกับโครงการจัดการข้อมูลอื่นๆ สิ่งที่สำคัญที่สุดคือการกำหนดว่าข้อมูลจะถูกนำไปใช้อย่างไร ในกรณีนี้ ส่วนใหญ่เราจะเลือกโมเดลและเวิร์กโฟลว์สำหรับ SNP ตาม SNP. นั่นคือเราต้องการข้อมูลบน SNP เดียวเท่านั้นในแต่ละครั้ง ฉันต้องเรียนรู้วิธีเรียกค้นบันทึกทั้งหมดที่เกี่ยวข้องกับ SNP หนึ่งใน 2,5 ล้านรายการอย่างง่ายดาย รวดเร็ว และถูกที่สุดเท่าที่จะเป็นไปได้
จะไม่ทำเช่นนี้ได้อย่างไร
หากต้องการอ้างอิงความคิดโบราณที่เหมาะสม:
ฉันไม่ได้ล้มเหลวเป็นพันครั้ง ฉันเพิ่งค้นพบวิธีการนับพันครั้งในการหลีกเลี่ยงการแยกวิเคราะห์ข้อมูลจำนวนมากในรูปแบบที่เป็นมิตรกับการค้นหา
ครั้งแรกลอง
ฉันได้เรียนรู้อะไรบ้าง: ไม่มีวิธีที่ถูกในการแยกวิเคราะห์ครั้งละ 25 TB
หลังจากเรียนหลักสูตร "วิธีการขั้นสูงสำหรับการประมวลผลข้อมูลขนาดใหญ่" ที่มหาวิทยาลัย Vanderbilt ฉันแน่ใจว่าเคล็ดลับอยู่ในกระเป๋า อาจต้องใช้เวลาหนึ่งหรือสองชั่วโมงในการตั้งค่าเซิร์ฟเวอร์ Hive เพื่อให้ทำงานผ่านข้อมูลทั้งหมดและรายงานผลลัพธ์ เนื่องจากข้อมูลของเราถูกจัดเก็บไว้ใน AWS S3 ฉันจึงใช้บริการนี้
หลังจากที่ฉันแสดงข้อมูลและรูปแบบของ Athena ฉันก็ได้ทำการทดสอบบางอย่างกับข้อความค้นหาดังนี้:
select * from intensityData limit 10;
และได้รับผลลัพธ์ที่มีโครงสร้างดีอย่างรวดเร็ว พร้อม.
จนเราลองใช้ข้อมูลในการทำงาน...
ฉันถูกขอให้ดึงข้อมูล SNP ทั้งหมดออกมาเพื่อทดสอบโมเดล ฉันรันแบบสอบถาม:
select * from intensityData
where snp = 'rs123456';
...และเริ่มรอ หลังจากแปดนาทีและข้อมูลที่ร้องขอมากกว่า 4 TB ฉันก็ได้รับผลลัพธ์ Athena คิดค่าบริการตามปริมาณข้อมูลที่พบ 5 ดอลลาร์ต่อเทราไบต์ ดังนั้นคำขอเดียวนี้จึงมีค่าใช้จ่าย 20 ดอลลาร์และใช้เวลารอแปดนาที ในการรันโมเดลกับข้อมูลทั้งหมด เราต้องรอ 38 ปีและจ่ายเงิน 50 ล้านดอลลาร์ แน่นอนว่าสิ่งนี้ไม่เหมาะกับเรา
จำเป็นต้องใช้ไม้ปาร์เก้...
ฉันได้เรียนรู้อะไรบ้าง: ระวังขนาดของไฟล์ Parquet ของคุณและการจัดวาง
ก่อนอื่นฉันพยายามแก้ไขสถานการณ์โดยแปลง TSV ทั้งหมดเป็น
ฉันทำงานง่ายๆ
สิ่งที่น่าสนใจคือประเภทการบีบอัดเริ่มต้น (และที่แนะนำ) ของ Parquet ซึ่งเร็วนั้นไม่สามารถแบ่งแยกได้ ดังนั้นผู้ดำเนินการแต่ละคนจึงติดอยู่กับงานในการคลายแพ็กและดาวน์โหลดชุดข้อมูลขนาด 3,5 GB ทั้งหมด
มาทำความเข้าใจปัญหากัน
ฉันได้เรียนรู้อะไรบ้าง: การเรียงลำดับทำได้ยาก โดยเฉพาะหากมีการกระจายข้อมูล
สำหรับฉันดูเหมือนว่าตอนนี้ฉันเข้าใจสาระสำคัญของปัญหาแล้ว ฉันจำเป็นต้องจัดเรียงข้อมูลตามคอลัมน์ SNP เท่านั้น ไม่ใช่ตามบุคคล จากนั้น SNP หลายรายการจะถูกจัดเก็บไว้ในกลุ่มข้อมูลที่แยกจากกัน จากนั้นฟังก์ชัน “อัจฉริยะ” ของ Parquet “เปิดเฉพาะเมื่อค่าอยู่ในช่วง” จะแสดงตัวเองออกมาอย่างสง่างาม น่าเสียดายที่การจัดเรียงแถวนับพันล้านแถวที่กระจัดกระจายไปทั่วคลัสเตอร์กลายเป็นงานที่ยาก
ฉันเข้าเรียนวิชาอัลกอริธึมในวิทยาลัย: “อืม ไม่มีใครสนใจความซับซ้อนในการคำนวณของอัลกอริธึมการเรียงลำดับเหล่านี้ทั้งหมด”
ฉันพยายามจัดเรียงคอลัมน์ขนาด 20TB
# สปาร์ค ตาราง: “เหตุใดจึงใช้เวลานานมาก”#ดาต้าวิทยาศาสตร์ การต่อสู้— นิค สเตรเยอร์ (@NicholasStrayer)
March 11, 2019
AWS ไม่ต้องการคืนเงินอย่างแน่นอนเนื่องจากเหตุผล "ฉันเป็นนักเรียนที่ฟุ้งซ่าน" หลังจากที่ฉันรันการเรียงลำดับบน Amazon Glue มันก็ทำงานเป็นเวลา 2 วันและเกิดข้อขัดข้อง
แล้วการแบ่งพาร์ติชั่นล่ะ?
ฉันได้เรียนรู้อะไรบ้าง: พาร์ติชันใน Spark จะต้องมีความสมดุล
จากนั้นฉันก็เกิดแนวคิดในการแบ่งพาร์ติชันข้อมูลในโครโมโซม มี 23 รายการ (และอีกหลายแห่งหากคุณคำนึงถึง DNA ของไมโตคอนเดรียและบริเวณที่ไม่ได้แมป)
ซึ่งจะทำให้คุณสามารถแบ่งข้อมูลออกเป็นส่วนเล็กๆ ได้ หากคุณเพิ่มเพียงหนึ่งบรรทัดในฟังก์ชันการส่งออก Spark ในสคริปต์กาว partition_by = "chr"
จากนั้นข้อมูลควรแบ่งออกเป็นที่เก็บข้อมูล
จีโนมประกอบด้วยชิ้นส่วนจำนวนมากที่เรียกว่าโครโมโซม
น่าเสียดายที่มันไม่ได้ผล โครโมโซมมีขนาดแตกต่างกัน ซึ่งหมายถึงปริมาณข้อมูลที่แตกต่างกัน ซึ่งหมายความว่างานที่ Spark ส่งถึงผู้ปฏิบัติงานไม่สมดุลและเสร็จสิ้นช้า เนื่องจากบางโหนดเสร็จเร็วและไม่ได้ใช้งาน อย่างไรก็ตาม งานต่างๆ ก็เสร็จสิ้นแล้ว แต่เมื่อขอ SNP หนึ่งรายการ ความไม่สมดุลกลับทำให้เกิดปัญหาอีกครั้ง ค่าใช้จ่ายในการประมวลผล SNP บนโครโมโซมที่มีขนาดใหญ่กว่า (นั่นคือจุดที่เราต้องการรับข้อมูล) ลดลงเพียงประมาณ 10 เท่าเท่านั้น เยอะแต่ยังไม่พอ
จะเป็นอย่างไรถ้าเราแบ่งมันออกเป็นส่วนย่อยๆ ล่ะ?
ฉันได้เรียนรู้อะไรบ้าง: อย่าพยายามสร้างพาร์ติชั่น 2,5 ล้านพาร์ติชั่นเลย
ฉันตัดสินใจออกไปทั้งหมดและแบ่งพาร์ติชัน SNP แต่ละรายการ เพื่อให้แน่ใจว่าพาร์ติชันมีขนาดเท่ากัน มันเป็นความคิดที่ไม่ดี. ฉันใช้กาวและเพิ่มบรรทัดที่ไร้เดียงสา partition_by = 'snp'
. งานเริ่มต้นและเริ่มดำเนินการ วันต่อมาฉันตรวจสอบและเห็นว่ายังไม่มีสิ่งใดเขียนถึง S3 ดังนั้นฉันจึงปิดงานนั้น ดูเหมือนว่า Glue กำลังเขียนไฟล์ระดับกลางไปยังตำแหน่งที่ซ่อนอยู่ใน S3 ซึ่งเป็นไฟล์จำนวนมากหรืออาจเป็นสองสามล้านไฟล์ เป็นผลให้ความผิดพลาดของฉันมีค่าใช้จ่ายมากกว่าหนึ่งพันดอลลาร์และไม่ได้ทำให้ที่ปรึกษาของฉันพอใจ
การแบ่งพาร์ติชัน + การเรียงลำดับ
ฉันได้เรียนรู้อะไรบ้าง: การเรียงลำดับยังคงทำได้ยาก เช่นเดียวกับการปรับ Spark
ความพยายามครั้งสุดท้ายของฉันในการแบ่งพาร์ติชันเกี่ยวข้องกับการแบ่งพาร์ติชันโครโมโซมแล้วจึงเรียงลำดับแต่ละพาร์ติชัน ตามทฤษฎีแล้ว สิ่งนี้จะช่วยเร่งความเร็วในการสืบค้นแต่ละครั้ง เนื่องจากข้อมูล SNP ที่ต้องการต้องอยู่ภายในชิ้นไม้ปาร์เก้สองสามชิ้นภายในช่วงที่กำหนด น่าเสียดายที่การจัดเรียงข้อมูลที่แบ่งพาร์ติชันกลายเป็นงานที่ยาก ด้วยเหตุนี้ ฉันจึงเปลี่ยนมาใช้ EMR สำหรับคลัสเตอร์แบบกำหนดเอง และใช้อินสแตนซ์ที่ทรงพลังแปดอินสแตนซ์ (C5.4xl) และ Sparklyr เพื่อสร้างเวิร์กโฟลว์ที่ยืดหยุ่นมากขึ้น...
# Sparklyr snippet to partition by chr and sort w/in partition
# Join the raw data with the snp bins
raw_data
group_by(chr) %>%
arrange(Position) %>%
Spark_write_Parquet(
path = DUMP_LOC,
mode = 'overwrite',
partition_by = c('chr')
)
...แต่งานก็ยังไม่เสร็จสิ้น ฉันกำหนดค่าด้วยวิธีต่างๆ: เพิ่มการจัดสรรหน่วยความจำสำหรับตัวดำเนินการแบบสอบถามแต่ละตัว, ใช้โหนดที่มีหน่วยความจำจำนวนมาก, ใช้ตัวแปรการออกอากาศ (ตัวแปรการออกอากาศ) แต่แต่ละครั้งสิ่งเหล่านี้กลายเป็นเพียงครึ่งวัด และค่อยๆ ตัวดำเนินการเริ่มต้นขึ้น ล้มเหลวจนกว่าทุกอย่างจะหยุดลง
อัปเดต: ดังนั้นมันจึงเริ่มต้น
pic.twitter.com/agY4GU2ru5 — นิค สเตรเยอร์ (@NicholasStrayer)
May 15, 2019
ฉันมีความคิดสร้างสรรค์มากขึ้น
ฉันได้เรียนรู้อะไรบ้าง: บางครั้งข้อมูลพิเศษต้องใช้วิธีแก้ไขปัญหาพิเศษ
SNP แต่ละรายการมีค่าตำแหน่ง นี่คือตัวเลขที่สอดคล้องกับจำนวนฐานตามโครโมโซม นี่เป็นวิธีที่ดีและเป็นธรรมชาติในการจัดระเบียบข้อมูลของเรา ตอนแรกฉันต้องการแบ่งส่วนตามบริเวณของแต่ละโครโมโซม เช่น ตำแหน่ง 1 - 2000, 2001 - 4000 เป็นต้น แต่ปัญหาคือ SNP ไม่กระจายทั่วทั้งโครโมโซมเท่าๆ กัน ดังนั้นขนาดของกลุ่มจึงแตกต่างกันมาก
เป็นผลให้ฉันแบ่งตำแหน่งออกเป็นหมวดหมู่ (อันดับ) โดยใช้ข้อมูลที่ดาวน์โหลดไปแล้ว ฉันได้ดำเนินการร้องขอเพื่อรับรายการ SNP ที่ไม่ซ้ำกัน ตำแหน่ง และโครโมโซม จากนั้น ฉันจัดเรียงข้อมูลภายในโครโมโซมแต่ละตัวและรวบรวม SNP ออกเป็นกลุ่ม (bin) ตามขนาดที่กำหนด สมมติว่าแต่ละ 1000 SNP สิ่งนี้ทำให้ฉันมีความสัมพันธ์ SNP-to-group-per-chromosome
ในท้ายที่สุด ฉันได้สร้างกลุ่ม (bin) จำนวน 75 SNP เหตุผลจะอธิบายไว้ด้านล่าง
snp_to_bin <- unique_snps %>%
group_by(chr) %>%
arrange(position) %>%
mutate(
rank = 1:n()
bin = floor(rank/snps_per_bin)
) %>%
ungroup()
ลองใช้ Spark ครั้งแรก
ฉันได้เรียนรู้อะไรบ้าง: การรวม Spark รวดเร็ว แต่การแบ่งพาร์ติชันยังคงมีราคาแพง
ฉันต้องการอ่านเฟรมข้อมูลขนาดเล็ก (2,5 ล้านแถว) นี้ลงใน Spark รวมเข้ากับข้อมูลดิบ จากนั้นแบ่งพาร์ติชันตามคอลัมน์ที่เพิ่มใหม่ bin
.
# Join the raw data with the snp bins
data_w_bin <- raw_data %>%
left_join(sdf_broadcast(snp_to_bin), by ='snp_name') %>%
group_by(chr_bin) %>%
arrange(Position) %>%
Spark_write_Parquet(
path = DUMP_LOC,
mode = 'overwrite',
partition_by = c('chr_bin')
)
ฉันใช้ sdf_broadcast()
ดังนั้น Spark จึงรู้ว่าควรส่ง data frame ไปยังโหนดทั้งหมด สิ่งนี้มีประโยชน์หากข้อมูลมีขนาดเล็กและจำเป็นสำหรับงานทั้งหมด มิฉะนั้น Spark จะพยายามฉลาดและกระจายข้อมูลตามความจำเป็น ซึ่งอาจทำให้เกิดการชะลอตัวได้
และอีกครั้งที่ความคิดของฉันไม่ได้ผล: งานต่างๆ ได้ผลมาระยะหนึ่งแล้ว สหภาพแรงงานเสร็จสมบูรณ์ และจากนั้น เช่นเดียวกับผู้ดำเนินการที่เริ่มโดยการแบ่งพาร์ติชัน พวกเขาก็เริ่มล้มเหลว
กำลังเพิ่ม AWK
ฉันได้เรียนรู้อะไรบ้าง: อย่านอนเมื่อคุณถูกสอนพื้นฐาน แน่นอนว่ามีคนแก้ไขปัญหาของคุณไปแล้วในช่วงทศวรรษ 1980
จนถึงจุดนี้ สาเหตุของความล้มเหลวทั้งหมดของฉันกับ Spark ก็คือข้อมูลในคลัสเตอร์ที่สับสน บางทีสถานการณ์อาจดีขึ้นได้ด้วยการรักษาล่วงหน้า ฉันตัดสินใจลองแยกข้อมูลข้อความดิบออกเป็นคอลัมน์ของโครโมโซม ดังนั้นฉันจึงหวังว่าจะให้ข้อมูล "แบ่งพาร์ติชันล่วงหน้า" แก่ Spark
ฉันค้นหา StackOverflow เพื่อดูวิธีแยกตามค่าคอลัมน์และพบ stdout
.
ฉันเขียนสคริปต์ Bash เพื่อทดลองใช้ ดาวน์โหลด TSV แพ็กเกจหนึ่ง จากนั้นแตกไฟล์โดยใช้ gzip
และส่งไปที่ awk
.
gzip -dc path/to/chunk/file.gz |
awk -F 't'
'{print $1",..."$30">"chunked/"$chr"_chr"$15".csv"}'
มันได้ผล!
เติมแกน
ฉันได้เรียนรู้อะไรบ้าง: gnu parallel
- มันเป็นสิ่งมหัศจรรย์ที่ทุกคนควรใช้มัน
การแยกทางกันค่อนข้างช้าและเมื่อฉันเริ่มต้น htop
เพื่อตรวจสอบการใช้ EC2 instance ที่ทรงพลัง (และมีราคาแพง) ปรากฎว่าฉันใช้เพียงคอร์เดียวและมีหน่วยความจำประมาณ 200 MB เพื่อแก้ปัญหาและไม่เสียเงินมากมาย เราต้องคิดหาวิธีการทำงานแบบขนาน โชคดีที่มีหนังสือที่น่าทึ่งอย่างยิ่ง gnu parallel
ซึ่งเป็นวิธีการที่ยืดหยุ่นมากสำหรับการใช้งานมัลติเธรดใน Unix
เมื่อฉันเริ่มการแบ่งพาร์ติชันโดยใช้กระบวนการใหม่ ทุกอย่างเรียบร้อยดี แต่ยังมีปัญหาคอขวดอยู่ - การดาวน์โหลดออบเจ็กต์ S3 ลงดิสก์นั้นไม่เร็วมากและไม่ได้ขนานอย่างสมบูรณ์ เพื่อแก้ไขปัญหานี้ ฉันทำสิ่งนี้:
- ฉันพบว่าเป็นไปได้ที่จะใช้ขั้นตอนการดาวน์โหลด S3 โดยตรงในไปป์ไลน์ โดยกำจัดที่เก็บข้อมูลระดับกลางบนดิสก์โดยสิ้นเชิง ซึ่งหมายความว่าฉันสามารถหลีกเลี่ยงการเขียนข้อมูลดิบลงดิสก์และใช้พื้นที่จัดเก็บบน AWS ที่มีขนาดเล็กกว่าและราคาถูกกว่าได้
- ทีม
aws configure set default.s3.max_concurrent_requests 50
เพิ่มจำนวนเธรดที่ AWS CLI ใช้อย่างมาก (โดยค่าเริ่มต้นคือ 10) - ฉันเปลี่ยนไปใช้อินสแตนซ์ EC2 ที่ปรับให้เหมาะกับความเร็วเครือข่าย โดยมีตัวอักษร n อยู่ในชื่อ ฉันพบว่าการสูญเสียพลังการประมวลผลเมื่อใช้ n-instance นั้นมากกว่าการชดเชยด้วยการเพิ่มความเร็วในการโหลด สำหรับงานส่วนใหญ่ฉันใช้ c5n.4xl
- เปลี่ยน
gzip
บน นี่เป็นเครื่องมือ gzip ที่สามารถทำสิ่งดีๆ เพื่อทำให้งานคลายการบีบอัดไฟล์ที่ไม่ขนานในตอนแรก (ซึ่งช่วยได้น้อยที่สุด)pigz
# Let S3 use as many threads as it wants
aws configure set default.s3.max_concurrent_requests 50
for chunk_file in $(aws s3 ls $DATA_LOC | awk '{print $4}' | grep 'chr'$DESIRED_CHR'.csv') ; do
aws s3 cp s3://$batch_loc$chunk_file - |
pigz -dc |
parallel --block 100M --pipe
"awk -F 't' '{print $1",..."$30">"chunked/{#}_chr"$15".csv"}'"
# Combine all the parallel process chunks to single files
ls chunked/ |
cut -d '_' -f 2 |
sort -u |
parallel 'cat chunked/*_{} | sort -k5 -n -S 80% -t, | aws s3 cp - '$s3_dest'/batch_'$batch_num'_{}'
# Clean up intermediate data
rm chunked/*
done
ขั้นตอนเหล่านี้จะรวมเข้าด้วยกันเพื่อให้ทุกอย่างทำงานได้อย่างรวดเร็ว ด้วยการเพิ่มความเร็วในการดาวน์โหลดและกำจัดการเขียนดิสก์ ตอนนี้ฉันสามารถประมวลผลแพ็คเกจขนาด 5 เทราไบต์ได้ในเวลาเพียงไม่กี่ชั่วโมง
ไม่มีอะไรจะดีไปกว่าการได้เห็นคอร์ทั้งหมดที่คุณจ่ายเงินซื้อบน AWS ที่ใช้งานอยู่ ต้องขอบคุณ gnu-parallel ที่ทำให้ฉันคลายซิปและแยกไฟล์ 19gig csv ได้เร็วที่สุดเท่าที่จะดาวน์โหลดได้ ฉันไม่สามารถแม้แต่จุดประกายให้ทำสิ่งนี้ได้
#ดาต้าวิทยาศาสตร์ #Linux pic.twitter.com/Nqyba2zqEk — นิค สเตรเยอร์ (@NicholasStrayer)
May 17, 2019
ทวีตนี้น่าจะพูดถึง 'TSV' อนิจจา.
การใช้ข้อมูลที่แยกวิเคราะห์ใหม่
ฉันได้เรียนรู้อะไรบ้าง: Spark ชอบข้อมูลที่ไม่มีการบีบอัดและไม่ชอบการรวมพาร์ติชัน
ตอนนี้ข้อมูลอยู่ใน S3 ในรูปแบบคลายแพ็ก (อ่าน: แชร์) และรูปแบบกึ่งเรียงลำดับ และฉันสามารถกลับสู่ Spark อีกครั้งได้ ความประหลาดใจรอฉันอยู่: ฉันล้มเหลวอีกครั้งในการบรรลุสิ่งที่ต้องการ! เป็นเรื่องยากมากที่จะบอก Spark อย่างชัดเจนว่าข้อมูลถูกแบ่งพาร์ติชันอย่างไร และแม้ว่าฉันจะทำสิ่งนี้ปรากฎว่ามีพาร์ติชั่นมากเกินไป (95) และเมื่อฉันใช้ coalesce
ลดจำนวนลงเหลือขีดจำกัดที่สมเหตุสมผล สิ่งนี้ทำลายการแบ่งพาร์ติชันของฉัน ฉันแน่ใจว่าสิ่งนี้สามารถแก้ไขได้ แต่หลังจากค้นหาสองสามวันฉันก็ไม่พบวิธีแก้ไข ในที่สุดฉันก็ทำงานทั้งหมดใน Spark สำเร็จ แม้ว่าจะต้องใช้เวลาสักพักและไฟล์ Parquet ที่แยกออกมาก็ไม่เล็กมาก (~200 KB) อย่างไรก็ตาม ข้อมูลอยู่ในจุดที่จำเป็น
เล็กเกินไปและไม่สม่ำเสมอ ยอดเยี่ยมมาก!
การทดสอบคำสั่ง Spark ในเครื่อง
ฉันได้เรียนรู้อะไรบ้าง: Spark มีค่าใช้จ่ายมากเกินไปเมื่อแก้ไขปัญหาง่ายๆ
ด้วยการดาวน์โหลดข้อมูลในรูปแบบที่ชาญฉลาด ฉันสามารถทดสอบความเร็วได้ ตั้งค่าสคริปต์ R เพื่อเรียกใช้เซิร์ฟเวอร์ Spark ในเครื่อง จากนั้นโหลดเฟรมข้อมูล Spark จากที่เก็บข้อมูลกลุ่ม Parquet (ถัง) ที่ระบุ ฉันพยายามโหลดข้อมูลทั้งหมดแต่ไม่สามารถทำให้ Sparklyr จดจำการแบ่งพาร์ติชันได้
sc <- Spark_connect(master = "local")
desired_snp <- 'rs34771739'
# Start a timer
start_time <- Sys.time()
# Load the desired bin into Spark
intensity_data <- sc %>%
Spark_read_Parquet(
name = 'intensity_data',
path = get_snp_location(desired_snp),
memory = FALSE )
# Subset bin to snp and then collect to local
test_subset <- intensity_data %>%
filter(SNP_Name == desired_snp) %>%
collect()
print(Sys.time() - start_time)
การดำเนินการใช้เวลา 29,415 วินาที ดีกว่ามาก แต่ก็ไม่ดีเกินไปสำหรับการทดสอบอะไรก็ตาม นอกจากนี้ ฉันไม่สามารถเร่งความเร็วสิ่งต่าง ๆ ด้วยการแคชได้ เพราะเมื่อฉันพยายามแคชเฟรมข้อมูลในหน่วยความจำ Spark จะขัดข้องอยู่เสมอ แม้ว่าฉันจะจัดสรรหน่วยความจำมากกว่า 50 GB ให้กับชุดข้อมูลที่มีน้ำหนักน้อยกว่า 15 ก็ตาม
กลับไปที่ AWK
ฉันได้เรียนรู้อะไรบ้าง: อาร์เรย์แบบเชื่อมโยงใน AWK มีประสิทธิภาพมาก
ฉันตระหนักว่าฉันสามารถบรรลุความเร็วที่สูงขึ้นได้ ฉันจำได้ว่าในความมหัศจรรย์
ในการทำเช่นนี้ฉันใช้บล็อกในสคริปต์ AWK BEGIN
. นี่คือโค้ดชิ้นหนึ่งที่ดำเนินการก่อนที่จะส่งข้อมูลบรรทัดแรกไปยังเนื้อหาหลักของสคริปต์
join_data.awk
BEGIN {
FS=",";
batch_num=substr(chunk,7,1);
chunk_id=substr(chunk,15,2);
while(getline < "snp_to_bin.csv") {bin[$1] = $2}
}
{
print $0 > "chunked/chr_"chr"_bin_"bin[$1]"_"batch_num"_"chunk_id".csv"
}
ทีม while(getline...)
โหลดแถวทั้งหมดจากกลุ่ม CSV (bin) ตั้งค่าคอลัมน์แรก (ชื่อ SNP) เป็นคีย์สำหรับอาร์เรย์ที่เชื่อมโยง bin
และค่าที่สอง (กลุ่ม) เป็นค่า แล้วในบล็อค. {
}
ซึ่งดำเนินการกับทุกบรรทัดของไฟล์หลัก แต่ละบรรทัดจะถูกส่งไปยังไฟล์เอาต์พุต ซึ่งได้รับชื่อที่ไม่ซ้ำกันขึ้นอยู่กับกลุ่ม (bin): ..._bin_"bin[$1]"_...
.
ตัวแปร batch_num
и chunk_id
ตรงกับข้อมูลที่ให้โดยไปป์ไลน์ การหลีกเลี่ยงสภาวะการแข่งขัน และเธรดการประมวลผลแต่ละรายการที่ทำงานอยู่ parallel
เขียนลงในไฟล์เฉพาะของตัวเอง
เนื่องจากฉันกระจายข้อมูลดิบทั้งหมดลงในโฟลเดอร์บนโครโมโซมที่เหลือจากการทดลองกับ AWK ครั้งก่อน ตอนนี้ฉันสามารถเขียนสคริปต์ Bash อื่นเพื่อประมวลผลโครโมโซมทีละตัว และส่งข้อมูลที่มีการแบ่งพาร์ติชันที่ลึกกว่าไปยัง S3
DESIRED_CHR='13'
# Download chromosome data from s3 and split into bins
aws s3 ls $DATA_LOC |
awk '{print $4}' |
grep 'chr'$DESIRED_CHR'.csv' |
parallel "echo 'reading {}'; aws s3 cp "$DATA_LOC"{} - | awk -v chr=""$DESIRED_CHR"" -v chunk="{}" -f split_on_chr_bin.awk"
# Combine all the parallel process chunks to single files and upload to rds using R
ls chunked/ |
cut -d '_' -f 4 |
sort -u |
parallel "echo 'zipping bin {}'; cat chunked/*_bin_{}_*.csv | ./upload_as_rds.R '$S3_DEST'/chr_'$DESIRED_CHR'_bin_{}.rds"
rm chunked/*
สคริปต์มีสองส่วน parallel
.
ในส่วนแรก ข้อมูลจะถูกอ่านจากไฟล์ทั้งหมดที่มีข้อมูลเกี่ยวกับโครโมโซมที่ต้องการ จากนั้นข้อมูลนี้จะถูกกระจายไปตามเธรด ซึ่งกระจายไฟล์ออกเป็นกลุ่ม (bin) ที่เหมาะสม เพื่อหลีกเลี่ยงสภาวะการแข่งขันเมื่อหลายเธรดเขียนลงในไฟล์เดียวกัน AWK จะส่งชื่อไฟล์เพื่อเขียนข้อมูลไปยังที่ต่างๆ เช่น chr_10_bin_52_batch_2_aa.csv
. เป็นผลให้มีการสร้างไฟล์ขนาดเล็กจำนวนมากบนดิสก์ (สำหรับสิ่งนี้ฉันใช้โวลุ่ม EBS เทราไบต์)
สายพานลำเลียงจากส่วนที่สอง parallel
ผ่านกลุ่ม (bin) และรวมไฟล์แต่ละไฟล์เป็น CSV ทั่วไป cat
แล้วจึงส่งออกไป
ออกอากาศใน R?
ฉันได้เรียนรู้อะไรบ้าง: สามารถติดต่อได้ stdin
и stdout
จากสคริปต์ R และใช้ในไปป์ไลน์
คุณอาจสังเกตเห็นบรรทัดนี้ในสคริปต์ Bash ของคุณ: ...cat chunked/*_bin_{}_*.csv | ./upload_as_rds.R...
. โดยจะแปลไฟล์กลุ่มที่ต่อกัน (bin) ทั้งหมดเป็นสคริปต์ R ด้านล่าง {}
เป็นเทคนิคพิเศษ parallel
ซึ่งจะแทรกข้อมูลใดๆ ที่ส่งไปยังสตรีมที่ระบุลงในคำสั่งโดยตรง ตัวเลือก {#}
จัดเตรียม ID เธรดที่ไม่ซ้ำใคร และ {%}
แสดงถึงหมายเลขช่องงาน (ซ้ำแต่ไม่เคยพร้อมกัน) รายการตัวเลือกทั้งหมดสามารถพบได้ใน
#!/usr/bin/env Rscript
library(readr)
library(aws.s3)
# Read first command line argument
data_destination <- commandArgs(trailingOnly = TRUE)[1]
data_cols <- list(SNP_Name = 'c', ...)
s3saveRDS(
read_csv(
file("stdin"),
col_names = names(data_cols),
col_types = data_cols
),
object = data_destination
)
เมื่อมีตัวแปร file("stdin")
ส่งไปยัง readr::read_csv
ข้อมูลที่แปลเป็นสคริปต์ R จะถูกโหลดลงในเฟรม ซึ่งจากนั้นจะอยู่ในรูปแบบ .rds
- การใช้ไฟล์ aws.s3
เขียนโดยตรงไปยัง S3
RDS มีลักษณะคล้ายกับ Parquet เวอร์ชันจูเนียร์ โดยไม่มีที่เก็บลำโพงที่หรูหรา
หลังจากเสร็จสิ้นสคริปต์ Bash ฉันก็ได้รับบันเดิล .rds
-ไฟล์ที่อยู่ใน S3 ซึ่งช่วยให้ฉันใช้การบีบอัดและประเภทในตัวได้อย่างมีประสิทธิภาพ
แม้จะใช้เบรก R แต่ทุกอย่างก็ทำงานเร็วมาก ไม่น่าแปลกใจที่ส่วนของ R ที่อ่านและเขียนข้อมูลได้รับการปรับให้เหมาะสมที่สุด หลังจากการทดสอบบนโครโมโซมขนาดกลางหนึ่งโครโมโซม งานบนอินสแตนซ์ C5n.4xl ก็เสร็จสมบูรณ์ภายในเวลาประมาณสองชั่วโมง
ข้อจำกัดของ S3
ฉันได้เรียนรู้อะไรบ้าง: ต้องขอบคุณการใช้งานเส้นทางอัจฉริยะ S3 จึงสามารถจัดการไฟล์ได้จำนวนมาก
ฉันกังวลว่า S3 จะสามารถจัดการไฟล์จำนวนมากที่ถ่ายโอนไปได้หรือไม่ ฉันสามารถทำให้ชื่อไฟล์ดูสมเหตุสมผลได้ แต่ S3 จะค้นหามันอย่างไร
โฟลเดอร์ใน S3 มีไว้เพื่อแสดงเท่านั้น จริงๆ แล้วระบบไม่ได้สนใจสัญลักษณ์นี้ /
.
ดูเหมือนว่า S3 จะแสดงเส้นทางไปยังไฟล์ใดไฟล์หนึ่งเป็นคีย์ธรรมดาในตารางแฮชหรือฐานข้อมูลตามเอกสาร ที่เก็บข้อมูลสามารถถือเป็นตาราง และไฟล์สามารถถือเป็นบันทึกในตารางนั้นได้
เนื่องจากความเร็วและประสิทธิภาพมีความสำคัญต่อการทำกำไรที่ Amazon จึงไม่น่าแปลกใจที่ระบบคีย์เป็นไฟล์พาธนี้ได้รับการปรับให้เหมาะสมอย่างยิ่ง ฉันพยายามหาจุดสมดุล: เพื่อที่ฉันจะได้ไม่ต้องส่งคำขอจำนวนมาก แต่คำขอได้รับการดำเนินการอย่างรวดเร็ว ปรากฎว่าวิธีที่ดีที่สุดคือสร้างไฟล์ bin ประมาณ 20 ไฟล์ ฉันคิดว่าหากเราเพิ่มประสิทธิภาพต่อไป เราจะสามารถบรรลุความเร็วที่เพิ่มขึ้นได้ (เช่น การสร้างที่เก็บข้อมูลพิเศษเฉพาะสำหรับข้อมูล ซึ่งจะช่วยลดขนาดของตารางการค้นหา) แต่ไม่มีเวลาหรือเงินสำหรับการทดลองเพิ่มเติม
แล้วความเข้ากันได้ข้ามล่ะ?
สิ่งที่ฉันเรียนรู้: สาเหตุอันดับหนึ่งของการเสียเวลาคือการปรับวิธีการจัดเก็บข้อมูลให้เหมาะสมก่อนเวลาอันควร
ณ จุดนี้ สิ่งสำคัญมากคือต้องถามตัวเองว่า “เหตุใดจึงต้องใช้รูปแบบไฟล์ที่เป็นกรรมสิทธิ์” เหตุผลอยู่ที่ความเร็วในการโหลด (ไฟล์ CSV แบบ gzipped ใช้เวลาโหลดนานกว่า 7 เท่า) และความเข้ากันได้กับขั้นตอนการทำงานของเรา ฉันอาจพิจารณาอีกครั้งว่า R สามารถโหลดไฟล์ Parquet (หรือ Arrow) ได้อย่างง่ายดายโดยไม่ต้องโหลด Spark หรือไม่ ทุกคนในห้องปฏิบัติการของเราใช้ R และหากฉันต้องการแปลงข้อมูลเป็นรูปแบบอื่น ฉันยังคงมีข้อมูลข้อความต้นฉบับอยู่ ดังนั้นฉันจึงสามารถเรียกใช้ไปป์ไลน์ได้อีกครั้ง
การแบ่งงาน
ฉันได้เรียนรู้อะไรบ้าง: อย่าพยายามปรับงานให้เหมาะสมด้วยตนเอง ปล่อยให้คอมพิวเตอร์จัดการเอง
ฉันได้แก้ไขจุดบกพร่องเวิร์กโฟลว์บนโครโมโซมหนึ่งแล้ว ตอนนี้ฉันต้องประมวลผลข้อมูลอื่นๆ ทั้งหมด
ฉันต้องการเพิ่ม EC2 instance หลายๆ ตัวสำหรับการแปลง แต่ในขณะเดียวกัน ฉันก็กลัวว่าจะได้รับโหลดที่ไม่สมดุลอย่างมากในงานประมวลผลต่างๆ (เช่นเดียวกับที่ Spark ได้รับความเดือดร้อนจากพาร์ติชันที่ไม่สมดุล) นอกจากนี้ ฉันไม่สนใจที่จะเพิ่มหนึ่งอินสแตนซ์ต่อโครโมโซม เนื่องจากสำหรับบัญชี AWS มีขีดจำกัดเริ่มต้นที่ 10 อินสแตนซ์
จากนั้นฉันตัดสินใจเขียนสคริปต์ใน R เพื่อปรับงานการประมวลผลให้เหมาะสม
ก่อนอื่น ฉันขอให้ S3 คำนวณว่าแต่ละโครโมโซมมีพื้นที่เก็บข้อมูลเท่าใด
library(aws.s3)
library(tidyverse)
chr_sizes <- get_bucket_df(
bucket = '...', prefix = '...', max = Inf
) %>%
mutate(Size = as.numeric(Size)) %>%
filter(Size != 0) %>%
mutate(
# Extract chromosome from the file name
chr = str_extract(Key, 'chr.{1,4}.csv') %>%
str_remove_all('chr|.csv')
) %>%
group_by(chr) %>%
summarise(total_size = sum(Size)/1e+9) # Divide to get value in GB
# A tibble: 27 x 2
chr total_size
<chr> <dbl>
1 0 163.
2 1 967.
3 10 541.
4 11 611.
5 12 542.
6 13 364.
7 14 375.
8 15 372.
9 16 434.
10 17 443.
# … with 17 more rows
จากนั้นผมเขียนฟังก์ชันที่เอาขนาดรวม สับลำดับโครโมโซม แบ่งออกเป็นกลุ่มๆ num_jobs
และบอกคุณว่างานประมวลผลทั้งหมดมีขนาดแตกต่างกันอย่างไร
num_jobs <- 7
# How big would each job be if perfectly split?
job_size <- sum(chr_sizes$total_size)/7
shuffle_job <- function(i){
chr_sizes %>%
sample_frac() %>%
mutate(
cum_size = cumsum(total_size),
job_num = ceiling(cum_size/job_size)
) %>%
group_by(job_num) %>%
summarise(
job_chrs = paste(chr, collapse = ','),
total_job_size = sum(total_size)
) %>%
mutate(sd = sd(total_job_size)) %>%
nest(-sd)
}
shuffle_job(1)
# A tibble: 1 x 2
sd data
<dbl> <list>
1 153. <tibble [7 × 3]>
จากนั้นฉันก็สุ่มเพลงเป็นพันครั้งโดยใช้เสียงฟี้อย่างแมวๆ และเลือกสิ่งที่ดีที่สุด
1:1000 %>%
map_df(shuffle_job) %>%
filter(sd == min(sd)) %>%
pull(data) %>%
pluck(1)
ดังนั้นฉันจึงลงเอยด้วยชุดงานที่มีขนาดใกล้เคียงกันมาก จากนั้นสิ่งที่เหลืออยู่ก็คือการรวมสคริปต์ Bash ก่อนหน้าของฉันไว้ในวงวนขนาดใหญ่ for
. การเพิ่มประสิทธิภาพนี้ใช้เวลาประมาณ 10 นาทีในการเขียน และนี่ก็น้อยกว่าที่ฉันจะใช้ไปกับการสร้างงานด้วยตนเองหากงานเหล่านั้นไม่สมดุลกันมาก ดังนั้นฉันคิดว่าฉันพูดถูกกับการเพิ่มประสิทธิภาพเบื้องต้นนี้
for DESIRED_CHR in "16" "9" "7" "21" "MT"
do
# Code for processing a single chromosome
fi
ในตอนท้ายฉันเพิ่มคำสั่งปิดระบบ:
sudo shutdown -h now
... และทุกอย่างก็สำเร็จ! เมื่อใช้ AWS CLI ฉันยกอินสแตนซ์โดยใช้ตัวเลือก user_data
ให้สคริปต์ Bash ของงานเพื่อการประมวลผลแก่พวกเขา พวกมันทำงานและปิดเครื่องโดยอัตโนมัติ ดังนั้นฉันจึงไม่ต้องจ่ายค่าพลังการประมวลผลเพิ่มเติม
aws ec2 run-instances ...
--tag-specifications "ResourceType=instance,Tags=[{Key=Name,Value=<<job_name>>}]"
--user-data file://<<job_script_loc>>
แพ็คกันเถอะ!
ฉันได้เรียนรู้อะไรบ้าง: API ควรเป็นแบบเรียบง่ายเพื่อความสะดวกและยืดหยุ่นในการใช้งาน
ในที่สุดฉันก็ได้รับข้อมูลในตำแหน่งและรูปแบบที่ถูกต้อง สิ่งที่เหลืออยู่คือทำให้กระบวนการใช้ข้อมูลง่ายขึ้นมากที่สุดเท่าที่จะเป็นไปได้เพื่อทำให้เพื่อนร่วมงานของฉันง่ายขึ้น ฉันต้องการสร้าง API อย่างง่ายสำหรับการสร้างคำขอ หากในอนาคตฉันตัดสินใจเปลี่ยนจาก .rds
กับไฟล์ Parquet นี่น่าจะเป็นปัญหาสำหรับฉัน ไม่ใช่สำหรับเพื่อนร่วมงาน สำหรับสิ่งนี้ฉันตัดสินใจสร้างแพ็คเกจ R ภายใน
สร้างและจัดทำเอกสารแพ็คเกจที่เรียบง่ายซึ่งมีฟังก์ชันการเข้าถึงข้อมูลเพียงไม่กี่ฟังก์ชันที่จัดระเบียบตามฟังก์ชัน get_snp
. ฉันยังสร้างเว็บไซต์สำหรับเพื่อนร่วมงานด้วย
แคชอัจฉริยะ
ฉันได้เรียนรู้อะไรบ้าง: หากข้อมูลของคุณเตรียมไว้อย่างดี การแคชจะเป็นเรื่องง่าย!
เนื่องจากหนึ่งในเวิร์กโฟลว์หลักใช้โมเดลการวิเคราะห์เดียวกันกับแพ็คเกจ SNP ฉันจึงตัดสินใจใช้ Binning เพื่อประโยชน์ของฉัน เมื่อส่งข้อมูลผ่าน SNP ข้อมูลทั้งหมดจากกลุ่ม (bin) จะถูกแนบไปกับอ็อบเจ็กต์ที่ส่งคืน นั่นคือ การสืบค้นแบบเก่าสามารถ (ในทางทฤษฎี) สามารถเร่งการประมวลผลการสืบค้นใหม่ได้
# Part of get_snp()
...
# Test if our current snp data has the desired snp.
already_have_snp <- desired_snp %in% prev_snp_results$snps_in_bin
if(!already_have_snp){
# Grab info on the bin of the desired snp
snp_results <- get_snp_bin(desired_snp)
# Download the snp's bin data
snp_results$bin_data <- aws.s3::s3readRDS(object = snp_results$data_loc)
} else {
# The previous snp data contained the right bin so just use it
snp_results <- prev_snp_results
}
...
เมื่อสร้างแพ็คเกจ ฉันรันการวัดประสิทธิภาพหลายอย่างเพื่อเปรียบเทียบความเร็วเมื่อใช้วิธีการต่างๆ ฉันขอแนะนำว่าอย่าละเลยสิ่งนี้ เพราะบางครั้งผลลัพธ์ก็ไม่คาดคิด ตัวอย่างเช่น, dplyr::filter
เร็วกว่าการจับแถวโดยใช้การกรองตามดัชนีมาก และการดึงคอลัมน์เดียวจากกรอบข้อมูลที่กรองได้เร็วกว่าการใช้ไวยากรณ์การจัดทำดัชนีมาก
โปรดทราบว่าวัตถุ prev_snp_results
มีกุญแจอยู่ snps_in_bin
. นี่คืออาร์เรย์ของ SNP ที่ไม่ซ้ำกันทั้งหมดในกลุ่ม (bin) ซึ่งช่วยให้คุณสามารถตรวจสอบได้อย่างรวดเร็วว่าคุณมีข้อมูลจากการสืบค้นครั้งก่อนแล้วหรือไม่ นอกจากนี้ยังทำให้ง่ายต่อการวนซ้ำ SNP ทั้งหมดในกลุ่ม (bin) ด้วยโค้ดนี้:
# Get bin-mates
snps_in_bin <- my_snp_results$snps_in_bin
for(current_snp in snps_in_bin){
my_snp_results <- get_snp(current_snp, my_snp_results)
# Do something with results
}
ผลการวิจัย
ตอนนี้เราสามารถ (และเริ่มจริงจัง) เรียกใช้โมเดลและสถานการณ์ต่างๆ ที่ก่อนหน้านี้เราไม่สามารถเข้าถึงได้แล้ว สิ่งที่ดีที่สุดคือเพื่อนร่วมงานในห้องปฏิบัติการของฉันไม่ต้องคิดถึงเรื่องยุ่งยากใดๆ พวกเขาแค่มีฟังก์ชั่นที่ใช้งานได้
และถึงแม้ว่าแพ็คเกจจะให้รายละเอียดแก่พวกเขา แต่ฉันก็พยายามทำให้รูปแบบข้อมูลง่ายพอที่จะทำให้พวกเขารู้ได้หากพรุ่งนี้ฉันหายไปทันที...
ความเร็วเพิ่มขึ้นอย่างเห็นได้ชัด โดยปกติเราจะสแกนชิ้นส่วนจีโนมที่มีนัยสำคัญเชิงหน้าที่ ก่อนหน้านี้เราไม่สามารถทำเช่นนี้ได้ (กลายเป็นว่ามีราคาแพงเกินไป) แต่ตอนนี้ด้วยโครงสร้างกลุ่ม (bin) และการแคช คำขอสำหรับ SNP หนึ่งรายการใช้เวลาโดยเฉลี่ยน้อยกว่า 0,1 วินาที และการใช้ข้อมูลก็เป็นเช่นนั้น ต่ำที่ต้นทุนสำหรับ S3 นั้นเป็นถั่วลิสง
เมื่อเร็วๆ นี้ ฉันได้รับการเปลี่ยนแปลงในการถกเถียงข้อมูลจีโนไทป์ดิบขนาด 25+ TB สำหรับแล็บของฉัน เมื่อฉันเริ่มต้น การใช้ spark ใช้เวลา 8 นาทีและมีค่าใช้จ่าย 20 ดอลลาร์ในการสืบค้น SNP หลังจากใช้ AWK+
#rstats ในการประมวลผล ตอนนี้ใช้เวลาน้อยกว่า 10 วินาทีและมีค่าใช้จ่าย 0.00001 ดอลลาร์ ส่วนตัวของฉัน#ข้อมูลใหญ่ ชนะpic.twitter.com/ANOXVGrmkk — นิค สเตรเยอร์ (@NicholasStrayer)
May 30, 2019
ข้อสรุป
บทความนี้ไม่ใช่คำแนะนำแต่อย่างใด การแก้ปัญหากลายเป็นเรื่องส่วนตัวและเกือบจะไม่เหมาะสมอย่างแน่นอน แต่เป็นบันทึกการเดินทาง ฉันต้องการให้คนอื่นเข้าใจว่าการตัดสินใจดังกล่าวไม่ได้เกิดขึ้นอย่างสมบูรณ์ในหัว แต่เป็นผลมาจากการลองผิดลองถูก นอกจากนี้ หากคุณกำลังมองหานักวิทยาศาสตร์ด้านข้อมูล โปรดจำไว้ว่าการใช้เครื่องมือเหล่านี้อย่างมีประสิทธิภาพต้องใช้ประสบการณ์ และประสบการณ์ต้องเสียค่าใช้จ่าย ฉันดีใจที่ฉันมีเงินพอจะจ่าย แต่คนอื่นๆ อีกหลายคนที่สามารถทำงานแบบเดียวกันได้ดีกว่าฉันจะไม่มีวันมีโอกาสได้ลองทำเลยเพราะขาดเงิน
เครื่องมือข้อมูลขนาดใหญ่มีความหลากหลาย หากคุณมีเวลา คุณเกือบจะสามารถเขียนโซลูชันที่รวดเร็วขึ้นได้อย่างแน่นอนโดยใช้เทคนิคการล้างข้อมูล การจัดเก็บ และการดึงข้อมูลอัจฉริยะ ท้ายที่สุดแล้วก็คือการวิเคราะห์ต้นทุนและผลประโยชน์
สิ่งที่ฉันเรียนรู้:
- ไม่มีวิธีที่ถูกในการแยกวิเคราะห์ครั้งละ 25 TB
- ระวังขนาดของไฟล์ Parquet และองค์กรของคุณ
- พาร์ติชันใน Spark จะต้องมีความสมดุล
- โดยทั่วไป อย่าพยายามสร้างพาร์ติชัน 2,5 ล้านพาร์ติชัน
- การเรียงลำดับยังคงทำได้ยาก เช่นเดียวกับการตั้งค่า Spark;
- บางครั้งข้อมูลพิเศษต้องใช้โซลูชันพิเศษ
- การรวม Spark ทำได้รวดเร็ว แต่การแบ่งพาร์ติชันยังคงมีราคาแพง
- อย่านอนตอนที่มันสอนเรื่องพื้นฐานให้กับคุณ เพราะอาจมีคนแก้ไขปัญหาของคุณไปแล้วในช่วงทศวรรษ 1980
gnu parallel
- นี่เป็นสิ่งมหัศจรรย์ ทุกคนควรใช้มัน- Spark ชอบข้อมูลที่ไม่มีการบีบอัดและไม่ชอบการรวมพาร์ติชัน
- Spark มีค่าใช้จ่ายมากเกินไปเมื่อแก้ไขปัญหาง่ายๆ
- อาร์เรย์เชื่อมโยงของ AWK มีประสิทธิภาพมาก
- คุณสามารถติดต่อได้
stdin
иstdout
จากสคริปต์ R และใช้ในไปป์ไลน์ - ต้องขอบคุณการใช้งานเส้นทางอัจฉริยะ S3 จึงสามารถประมวลผลไฟล์ได้จำนวนมาก
- สาเหตุหลักที่ทำให้เสียเวลาคือการปรับวิธีการจัดเก็บข้อมูลให้เหมาะสมก่อนเวลาอันควร
- อย่าพยายามปรับงานให้เหมาะสมด้วยตนเอง ปล่อยให้คอมพิวเตอร์ทำ
- API ควรเป็นแบบเรียบง่ายเพื่อความสะดวกและยืดหยุ่นในการใช้งาน
- หากข้อมูลของคุณได้รับการจัดเตรียมอย่างดี การแคชจะเป็นเรื่องง่าย!
ที่มา: will.com