(เพราะเหตุใด) เราต้องเรียกแคชหรือคงอยู่กับ RDD


171

เมื่อชุดข้อมูลที่กระจายแบบยืดหยุ่น (RDD) ถูกสร้างขึ้นจากไฟล์ข้อความหรือการรวบรวม (หรือจาก RDD อื่น) เราจำเป็นต้องเรียก "แคช" หรือ "คงอยู่" เพื่อเก็บข้อมูล RDD ไว้ในหน่วยความจำอย่างชัดเจนหรือไม่ หรือข้อมูล RDD ถูกจัดเก็บในลักษณะกระจายในหน่วยความจำโดยค่าเริ่มต้นหรือไม่?

val textFile = sc.textFile("/user/emp.txt")

ตามความเข้าใจของฉันหลังจากขั้นตอนข้างต้น textFile เป็น RDD และมีอยู่ใน / บางส่วนของหน่วยความจำของโหนด

ถ้าเป็นเช่นนั้นทำไมเราต้องเรียก "แคช" หรือ "คงอยู่" ใน textFile RDD?

คำตอบ:


300

การดำเนินการ RDD ส่วนใหญ่ขี้เกียจ คิดว่า RDD เป็นคำอธิบายของชุดปฏิบัติการ RDD ไม่ใช่ข้อมูล ดังนั้นบรรทัดนี้:

val textFile = sc.textFile("/user/emp.txt")

มันไม่ทำอะไรเลย มันสร้าง RDD ที่ระบุว่า "เราจะต้องโหลดไฟล์นี้" ไฟล์ไม่ได้ถูกโหลด ณ จุดนี้

การดำเนินการ RDD ที่ต้องการการตรวจสอบเนื้อหาของข้อมูลนั้นไม่สามารถเกิดขึ้นได้ (สิ่งเหล่านี้เรียกว่าการกระทำ ) ตัวอย่างคือRDD.count- เพื่อบอกจำนวนบรรทัดในไฟล์คุณจำเป็นต้องอ่านไฟล์ ดังนั้นหากคุณเขียนtextFile.countณ จุดนี้ไฟล์จะถูกอ่านบรรทัดจะถูกนับและการนับจะถูกส่งกลับ

ถ้าคุณโทรtextFile.countอีกครั้ง สิ่งเดียวกัน: ไฟล์จะถูกอ่านและนับอีกครั้ง ไม่มีอะไรถูกเก็บไว้ RDD ไม่ใช่ข้อมูล

แล้วจะRDD.cacheทำอย่างไรดี? หากคุณเพิ่มtextFile.cacheรหัสข้างต้น:

val textFile = sc.textFile("/user/emp.txt")
textFile.cache

มันไม่ทำอะไรเลย RDD.cacheเป็นการทำงานที่ขี้เกียจ ไฟล์ยังไม่ได้อ่าน แต่ตอนนี้ RDD บอกว่า "อ่านไฟล์นี้แล้วทำการแคชเนื้อหา" หากคุณเรียกใช้textFile.countครั้งแรกไฟล์จะถูกโหลดแคชและนับ หากคุณโทรtextFile.countครั้งที่สองการดำเนินการจะใช้แคช มันจะใช้ข้อมูลจากแคชและนับจำนวนบรรทัด

พฤติกรรมแคชขึ้นอยู่กับหน่วยความจำที่มีอยู่ หากไฟล์ไม่พอดีกับหน่วยความจำเช่นนั้นtextFile.countจะกลับไปใช้การทำงานตามปกติและอ่านไฟล์อีกครั้ง


4
สวัสดีแดเนียล - เมื่อคุณเรียกแคชนี่หมายความว่า RDD ไม่ถูกโหลดจากแหล่งที่มา (เช่นไฟล์ข้อความ) - คุณจะมั่นใจได้อย่างไรว่าข้อมูลจากไฟล์ข้อความล่าสุดเมื่อถูกแคช? (จุดประกายให้คิดออกหรือเป็นคู่มือการใช้งานเพื่อ unpersist () เป็นระยะเพื่อให้แน่ใจว่าข้อมูลแหล่งที่มาได้รับการคำนวณใหม่ในภายหลังเชื้อสาย?)
andrew.butkus

เช่นกัน - หากคุณต้องไม่ใส่คนอื่นเป็นระยะ - ถ้าคุณมี rdd ที่ถูกแคชขึ้นอยู่กับ RDD อื่นที่ถูกแคชคุณต้องไม่ใช้ทั้ง RDD เพื่อดูผลลัพธ์ที่คำนวณใหม่หรือไม่
andrew.butkus

21
Spark เพียงสมมติว่าไฟล์จะไม่เปลี่ยนแปลง มันอ่านไฟล์ที่จุดใดก็ได้ในเวลาและอาจอ่านบางส่วนของมันตามความจำเป็นในภายหลัง (เช่นถ้าข้อมูลถูกส่งออกมาจากแคช) ดังนั้นคุณควรเก็บไฟล์ไว้โดยไม่มีการเปลี่ยนแปลง! เพียงสร้างไฟล์ใหม่ด้วยชื่อใหม่เมื่อคุณมีข้อมูลใหม่จากนั้นโหลดเป็น RDD ใหม่ หากคุณได้รับข้อมูลใหม่อย่างต่อเนื่องให้ดูที่ Spark Streaming
Daniel Darabos

10
ใช่. RDD นั้นไม่เปลี่ยนรูปดังนั้นทุก RDD จะถือว่าการพึ่งพานั้นไม่เปลี่ยนรูปเช่นกัน Spark สตรีมมิ่งช่วยให้คุณสามารถตั้งค่าต้นไม้ดังกล่าวที่ทำงานในกระแสของการเปลี่ยนแปลง แต่วิธีที่ง่ายกว่าคือการสร้างทรีในฟังก์ชั่นที่ใช้ชื่อไฟล์เป็นพารามิเตอร์ จากนั้นเพียงเรียกใช้ฟังก์ชั่นสำหรับไฟล์ใหม่และกะเทยคุณจะได้แผนภูมิการคำนวณใหม่
Daniel Darabos

1
@Humoyun: บนแท็บที่เก็บข้อมูลของ Spark UI คุณสามารถดูจำนวนแคช RDD แต่ละรายการได้ ข้อมูลอาจใหญ่จนเหลือเพียง 40% ในหน่วยความจำทั้งหมดที่คุณมีสำหรับการแคช ตัวเลือกหนึ่งในกรณีนี้คือการใช้perisistและเลือกตัวเลือกการจัดเก็บข้อมูลที่อนุญาตให้หกข้อมูลแคชไปยังดิสก์
Daniel Darabos

197

ฉันคิดว่าคำถามจะเป็นสูตรที่ดีกว่าเป็น:

เมื่อใดที่เราต้องเรียกแคชหรือคงอยู่บน RDD?

กระบวนการของ Spark นั้นขี้เกียจนั่นคือจะไม่มีอะไรเกิดขึ้นจนกว่าจะจำเป็น หากต้องการตอบคำถามอย่างรวดเร็วหลังจากval textFile = sc.textFile("/user/emp.txt")ออกแล้วจะไม่มีสิ่งใดเกิดขึ้นกับข้อมูลเพียงอย่างเดียวที่HadoopRDDสร้างขึ้นโดยใช้ไฟล์เป็นแหล่งข้อมูล

สมมติว่าเราแปลงข้อมูลนั้นเล็กน้อย:

val wordsRDD = textFile.flatMap(line => line.split("\\W"))

อีกครั้งไม่มีอะไรเกิดขึ้นกับข้อมูล ขณะนี้มี RDD ใหม่wordsRDDที่มีการอ้างอิงถึงtestFileและฟังก์ชั่นที่จะใช้เมื่อจำเป็น

เฉพาะเมื่อมีการเรียกการกระทำจาก RDD เช่นเดียวwordsRDD.countกับห่วงโซ่ RDD ที่เรียกว่าเชื้อสายจะถูกดำเนินการ นั่นคือข้อมูลถูกแบ่งย่อยในพาร์ติชันจะถูกโหลดโดยตัวจัดการของคลัสเตอร์ Spark flatMapฟังก์ชันจะถูกนำไปใช้และผลลัพธ์จะถูกคำนวณ

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

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

val positiveWordsCount = wordsRDD.filter(word => isPositive(word)).count()
val negativeWordsCount = wordsRDD.filter(word => isNegative(word)).count()

ที่นี่แต่ละสาขามีการโหลดข้อมูลใหม่ การเพิ่มcacheคำสั่งที่ชัดเจนจะทำให้มั่นใจได้ว่าการประมวลผลที่ทำไว้ก่อนหน้านี้ได้รับการเก็บรักษาและนำกลับมาใช้ งานจะมีลักษณะเช่นนี้:

val textFile = sc.textFile("/user/emp.txt")
val wordsRDD = textFile.flatMap(line => line.split("\\W"))
wordsRDD.cache()
val positiveWordsCount = wordsRDD.filter(word => isPositive(word)).count()
val negativeWordsCount = wordsRDD.filter(word => isNegative(word)).count()

ด้วยเหตุผลcacheดังกล่าวจึงได้รับการกล่าวขานว่า 'ทำลายเชื้อสาย' เนื่องจากสร้างจุดตรวจสอบที่สามารถนำมาใช้ซ้ำเพื่อการประมวลผลต่อไป

Rule of thumb: ใช้cacheเมื่อเชื้อสายของ RDD ของคุณแตกแขนงออกหรือเมื่อใช้ RDD หลายครั้งเหมือนในลูป


1
น่ากลัว ขอบคุณ อีกหนึ่งคำถามที่เกี่ยวข้อง เมื่อเราแคชหรือคงอยู่ข้อมูลจะถูกเก็บไว้ในหน่วยความจำของผู้ปฏิบัติการหรือหน่วยความจำของโหนดผู้ปฏิบัติงาน หากเป็นหน่วยความจำของตัวจัดการวิธีที่ Spark จะระบุว่าผู้ปฏิบัติการรายใดมีข้อมูล
Ramana

1
@RamanaUppala มีการใช้งานหน่วยความจำของตัวจัดการ ส่วนของหน่วยความจำตัวจัดการที่ใช้สำหรับการแคชจะถูกควบคุมโดยการspark.storage.memoryFractionกำหนดค่า เกี่ยวกับตัวจัดการที่มีข้อมูลใด RDD จะติดตามพาร์ติชันที่แจกจ่ายบนตัวจัดการ
maasg

5
@maasg ถูกต้องฉันหากฉันผิด แต่ไม่cacheหรือpersist สามารถทำลายวงศ์ตระกูล
zero323

ที่ wordRDD จะถูกเก็บไว้ที่ไหนถ้าเราไม่ได้มีคำสั่ง. cache () ในตัวอย่างข้างต้น?
sun_dare

เกิดอะไรขึ้นถ้าก่อนที่ทั้งสองจะนับเรารวมสองสาขากลับไปที่หนึ่ง rdd และนับ? ในกรณีนี้แคชมีประโยชน์หรือไม่
Xiawei Zhang

30

เราจำเป็นต้องเรียก "แคช" หรือ "คงอยู่" อย่างชัดเจนเพื่อเก็บข้อมูล RDD ไว้ในหน่วยความจำหรือไม่?

ใช่เฉพาะในกรณีที่จำเป็น

ข้อมูล RDD จัดเก็บในลักษณะกระจายในหน่วยความจำโดยค่าเริ่มต้น?

No!

และนี่คือเหตุผลที่:

  • Spark สนับสนุนตัวแปรที่แชร์กันสองประเภท: ตัวแปรออกอากาศซึ่งสามารถใช้เพื่อแคชค่าในหน่วยความจำบนโหนดทั้งหมดและตัวสะสมซึ่งเป็นตัวแปรที่“ เพิ่ม” เท่านั้นเช่นเคาน์เตอร์และผลรวม

  • RDDs สนับสนุนการดำเนินการสองประเภท: การแปลงซึ่งสร้างชุดข้อมูลใหม่จากชุดข้อมูลที่มีอยู่และการดำเนินการซึ่งส่งคืนค่าให้กับโปรแกรมไดรเวอร์หลังจากเรียกใช้การคำนวณบนชุดข้อมูล ตัวอย่างเช่น map คือการแปลงที่ส่งผ่านองค์ประกอบแต่ละชุดข้อมูลผ่านฟังก์ชันและส่งคืน RDD ใหม่ที่แสดงผลลัพธ์ ในทางกลับกันการลดคือการกระทำที่รวมองค์ประกอบทั้งหมดของ RDD โดยใช้ฟังก์ชั่นบางอย่างและส่งกลับผลลัพธ์สุดท้ายไปยังโปรแกรมไดรเวอร์ (แม้ว่าจะมีการลดคู่ขนาน BBKeyKey ที่ส่งคืนชุดข้อมูลแบบกระจาย)

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

  • ตามค่าเริ่มต้นแต่ละ RDD ที่ถูกแปลงอาจถูกคำนวณใหม่ทุกครั้งที่คุณเรียกใช้การกระทำ อย่างไรก็ตามคุณยังคงมี RDD ในหน่วยความจำโดยใช้วิธี persist (หรือแคช) ซึ่งในกรณีนี้ Spark จะเก็บองค์ประกอบรอบ ๆ คลัสเตอร์ไว้เพื่อให้เข้าถึงได้เร็วขึ้นในครั้งถัดไปที่คุณทำการสืบค้น นอกจากนี้ยังมีการสนับสนุนสำหรับ RDDs ที่ยังคงอยู่บนดิสก์หรือจำลองแบบข้ามหลายโหนด

สำหรับรายละเอียดเพิ่มเติมโปรดตรวจสอบคู่มือการเขียนโปรแกรม Spark


1
ไม่ได้ตอบคำถามของฉัน
Ramana

อะไรที่ไม่ตอบ
eliasah

1
เมื่อข้อมูลของ RDD ถูกเก็บไว้ในหน่วยความจำเริ่มต้นทำไมเราต้องโทรหา Cache หรือ Persist
Ramana

RDD จะไม่ถูกเก็บไว้ในหน่วยความจำโดยค่าเริ่มต้นดังนั้นการคงอยู่ของ RDD จะทำให้ Spark ทำการแปลงได้เร็วขึ้นในคลัสเตอร์
eliasah

2
มันเป็นคำตอบที่ดีฉันไม่รู้ว่าทำไมมันจึงถูกลดระดับลง มันเป็นคำตอบจากบนลงล่างอธิบายว่า RDD ทำงานอย่างไรจากแนวคิดระดับสูง ฉันได้เพิ่มคำตอบอื่นที่มาจากล่างขึ้นบน: เริ่มต้นจาก "บรรทัดนี้ทำอะไร" บางทีการติดตามใครบางคนอาจเริ่มง่ายขึ้นด้วย Spark
Daniel Darabos

11

ด้านล่างเป็นสามสถานการณ์ที่คุณควรแคช RDD ของคุณ:

ใช้ RDD หลายครั้ง

ดำเนินการหลายการกระทำใน RDD เดียวกัน

สำหรับโซ่ยาวของการเปลี่ยนแปลง (หรือแพงมาก)


7

การเพิ่มเหตุผลอื่นในการเพิ่ม (หรือเพิ่มชั่วคราว) cacheการเรียกวิธีการ

สำหรับการแก้ไขปัญหาหน่วยความจำ

ด้วยcacheวิธีการจุดประกายจะให้ข้อมูลการแก้จุดบกพร่องเกี่ยวกับขนาดของ RDD ดังนั้นใน UI แบบรวมประกายไฟคุณจะได้รับข้อมูลการใช้หน่วยความจำ RDD และสิ่งนี้พิสูจน์แล้วว่ามีประโยชน์มากในการวินิจฉัยปัญหาหน่วยความจำ

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