(Tại sao) chúng ta cần gọi bộ đệm hoặc duy trì trên RDD


171

Khi bộ dữ liệu phân tán linh hoạt (RDD) được tạo từ tệp văn bản hoặc bộ sưu tập (hoặc từ RDD khác), chúng ta có cần gọi "bộ đệm" hoặc "tồn tại" một cách rõ ràng để lưu trữ dữ liệu RDD vào bộ nhớ không? Hoặc là dữ liệu RDD được lưu trữ theo cách phân tán trong bộ nhớ theo mặc định?

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

Theo hiểu biết của tôi, sau bước trên, textFile là RDD và có sẵn trong tất cả / một số bộ nhớ của nút.

Nếu vậy, tại sao chúng ta cần gọi "cache" hoặc "vẫn tồn tại" trên textFile RDD?

Câu trả lời:


300

Hầu hết các hoạt động RDD là lười biếng. Hãy nghĩ về một RDD như là một mô tả của một loạt các hoạt động. Một RDD không phải là dữ liệu. Vì vậy, dòng này:

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

Nó không làm gì cả. Nó tạo ra một RDD có nội dung "chúng ta sẽ cần tải tệp này". Các tập tin không được tải tại thời điểm này.

Các hoạt động RDD yêu cầu quan sát nội dung của dữ liệu không thể lười biếng. (Chúng được gọi là hành động .) Một ví dụ là RDD.count- để cho bạn biết số lượng dòng trong tệp, tệp cần phải được đọc. Vì vậy, nếu bạn viết textFile.count, tại thời điểm này, tệp sẽ được đọc, các dòng sẽ được tính và số đếm sẽ được trả về.

Nếu bạn gọi textFile.countlại thì sao? Điều tương tự: tập tin sẽ được đọc và đếm lại. Không có gì được lưu trữ. Một RDD không phải là dữ liệu.

Vậy RDD.cachelàm gì? Nếu bạn thêm textFile.cachevào đoạn mã trên:

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

Nó không làm gì cả. RDD.cachecũng là một hoạt động lười biếng. Các tập tin vẫn không được đọc. Nhưng bây giờ RDD nói "đọc tệp này và sau đó lưu trữ nội dung". Nếu bạn sau đó chạy textFile.countlần đầu tiên, các tập tin sẽ được nạp, lưu trữ, và đếm. Nếu bạn gọi textFile.countlần thứ hai, thao tác sẽ sử dụng bộ đệm. Nó sẽ chỉ lấy dữ liệu từ bộ đệm và đếm các dòng.

Các hành vi bộ nhớ cache phụ thuộc vào bộ nhớ có sẵn. Ví dụ, nếu tệp không vừa trong bộ nhớ, thì textFile.countsẽ quay lại hành vi thông thường và đọc lại tệp.


4
Xin chào, - khi bạn gọi bộ đệm, điều này có nghĩa là RDD không được tải lại từ nguồn (ví dụ: tệp văn bản) - làm thế nào bạn có thể chắc chắn rằng dữ liệu từ tệp văn bản gần đây nhất khi được lưu trong bộ đệm? (có phát hiện ra điều này hay đây là một hoạt động thủ công để unpersist () định kỳ để đảm bảo dữ liệu nguồn được tính toán lại sau trong dòng dõi?)
andrew.butkus

cũng vậy - nếu bạn phải hủy theo dõi định kỳ, - nếu bạn có một rdd được lưu trong bộ nhớ cache, phụ thuộc vào một RDD khác được lưu trong bộ nhớ cache, bạn có phải hủy cả hai RDD để xem kết quả được tính toán lại không?
andrew.butkus

21
Spark chỉ giả định rằng tập tin sẽ không bao giờ thay đổi. Nó đọc tệp tại một thời điểm tùy ý và có thể đọc lại các phần của nó khi cần thiết sau này. (Ví dụ: nếu một phần dữ liệu bị đẩy ra khỏi bộ đệm.) Vì vậy, tốt hơn hết bạn nên giữ các tệp của mình không thay đổi! Chỉ cần tạo một tệp mới với một tên mới khi bạn có dữ liệu mới, sau đó tải nó dưới dạng RDD mới. Nếu bạn liên tục nhận được dữ liệu mới, hãy xem Spark Streaming.
Daniel Darabos

10
Đúng. RDD là bất biến, vì vậy mọi RDD đều thừa nhận sự phụ thuộc của nó là bất biến. Spark Streaming cho phép bạn thiết lập những cây như vậy hoạt động theo luồng thay đổi. Nhưng một giải pháp thậm chí đơn giản hơn là xây dựng cây trong một hàm lấy tên tệp làm tham số. Sau đó, chỉ cần gọi hàm cho tệp mới và gặp sự cố, bạn đã có cây tính toán mới.
Daniel Darabos

1
@Humoyun: Trên tab Storage của Spark UI, bạn có thể thấy số lượng mỗi RDD được lưu trong bộ nhớ cache. Dữ liệu có thể lớn đến mức chỉ 40% trong số đó phù hợp với tổng bộ nhớ bạn có cho bộ nhớ đệm. Một tùy chọn trong trường hợp này là sử dụng perisistvà chọn tùy chọn lưu trữ cho phép đổ dữ liệu bộ đệm vào đĩa.
Daniel Darabos

197

Tôi nghĩ rằng câu hỏi sẽ được xây dựng tốt hơn như:

Khi nào chúng ta cần gọi bộ đệm hoặc tiếp tục sử dụng RDD?

Quá trình Spark là lười biếng, nghĩa là, sẽ không có gì xảy ra cho đến khi nó được yêu cầu. Để trả lời nhanh câu hỏi, sau khi val textFile = sc.textFile("/user/emp.txt")được ban hành, không có gì xảy ra với dữ liệu, chỉ có a HadoopRDDđược xây dựng, sử dụng tệp làm nguồn.

Giả sử chúng ta biến đổi dữ liệu đó một chút:

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

Một lần nữa, không có gì xảy ra với dữ liệu. Bây giờ có một RDD mới wordsRDDchứa tham chiếu đến testFilevà một chức năng sẽ được áp dụng khi cần thiết.

Chỉ khi một hành động được gọi theo RDD, như wordsRDD.countchuỗi RDD, được gọi là dòng sẽ được thực thi. Đó là, dữ liệu, được chia nhỏ trong các phân vùng, sẽ được tải bởi người thi hành của cụm Spark, flatMapchức năng sẽ được áp dụng và kết quả sẽ được tính toán.

Trên một dòng dõi tuyến tính, như một trong ví dụ này, cache()là không cần thiết. Dữ liệu sẽ được tải cho người thực thi, tất cả các biến đổi sẽ được áp dụng và cuối cùng countsẽ được tính toán, tất cả trong bộ nhớ - nếu dữ liệu phù hợp với bộ nhớ.

cachelà hữu ích khi dòng dõi của RDD phân nhánh. Giả sử bạn muốn lọc các từ của ví dụ trước thành số đếm cho các từ tích cực và tiêu cực. Bạn có thể làm điều này như thế:

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

Ở đây, mỗi chi nhánh phát hành tải lại dữ liệu. Thêm một cachetuyên bố rõ ràng sẽ đảm bảo rằng việc xử lý được thực hiện trước đó được bảo tồn và tái sử dụng. Công việc sẽ như thế này:

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()

Vì lý do đó, cacheđược cho là 'phá vỡ dòng dõi' vì nó tạo ra một điểm kiểm tra có thể được sử dụng lại để xử lý thêm.

Nguyên tắc chung: Sử dụng cachekhi dòng dõi RDD của bạn phân nhánh hoặc khi RDD được sử dụng nhiều lần như trong một vòng lặp.


1
Tuyệt vời. Cảm ơn. Thêm một câu hỏi liên quan. Khi chúng tôi lưu trữ hoặc lưu trữ lâu dài, dữ liệu sẽ được lưu trữ trong bộ nhớ của người thực thi hoặc bộ nhớ của nút worker. Nếu đó là bộ nhớ của người thực thi, How Spark xác định người thực thi nào có dữ liệu.
Ramana

1
@RamanaUppala bộ nhớ thực thi được sử dụng. Phần bộ nhớ thực thi được sử dụng cho bộ đệm được điều khiển bởi cấu hình spark.storage.memoryFraction. Về việc người thực thi có dữ liệu nào, RDD sẽ theo dõi các phân vùng được phân phối trên các người thi hành.
maasg

5
@maasg Sửa lỗi cho tôi nếu tôi sai nhưng cachecũng không persist thể phá vỡ dòng dõi .
zero323

WordsRDD sẽ được lưu trữ ở đâu nếu chúng ta không có câu lệnh .cache () trong ví dụ trên?
sun_dare

Điều gì sẽ xảy ra nếu trước khi hai người đếm, chúng ta hợp nhất hai nhánh lại thành một rdd và đếm? trong trường hợp này, bộ nhớ cache có lợi không?
Xiawei Zhang

30

Chúng ta có cần gọi "bộ đệm" hoặc "kiên trì" một cách rõ ràng để lưu trữ dữ liệu RDD vào bộ nhớ không?

Có, chỉ khi cần thiết.

Theo mặc định, dữ liệu RDD được lưu trữ theo cách phân tán trong bộ nhớ?

Không!

Và đây là những lý do tại sao:

  • Spark hỗ trợ hai loại biến được chia sẻ: biến quảng bá, có thể được sử dụng để lưu trữ một giá trị trong bộ nhớ trên tất cả các nút và bộ tích lũy, là các biến chỉ được thêm vào, ví dụ như bộ đếm và tổng.

  • RDD hỗ trợ hai loại hoạt động: biến đổi, tạo ra một tập dữ liệu mới từ một kiểu hiện có và các hành động, trả về một giá trị cho chương trình trình điều khiển sau khi chạy một tính toán trên tập dữ liệu. Ví dụ, bản đồ là một phép biến đổi chuyển từng phần tử tập dữ liệu qua một hàm và trả về một RDD mới biểu thị các kết quả. Mặt khác, giảm là một hành động tổng hợp tất cả các yếu tố của RDD bằng cách sử dụng một số chức năng và trả về kết quả cuối cùng cho chương trình trình điều khiển (mặc dù cũng có một lessByKey song song trả về một tập dữ liệu phân tán).

  • Tất cả các biến đổi trong Spark đều lười biếng, ở chỗ chúng không tính toán kết quả của chúng ngay lập tức. Thay vào đó, họ chỉ nhớ các phép biến đổi được áp dụng cho một số tập dữ liệu cơ sở (ví dụ: một tệp). Các phép biến đổi chỉ được tính khi một hành động yêu cầu kết quả được trả về chương trình trình điều khiển. Thiết kế này cho phép Spark chạy hiệu quả hơn - ví dụ: chúng ta có thể nhận ra rằng một tập dữ liệu được tạo thông qua bản đồ sẽ được sử dụng để giảm và chỉ trả về kết quả giảm cho trình điều khiển, thay vì tập dữ liệu được ánh xạ lớn hơn.

  • Theo mặc định, mỗi RDD được chuyển đổi có thể được tính toán lại mỗi khi bạn chạy một hành động trên nó. Tuy nhiên, bạn cũng có thể duy trì RDD trong bộ nhớ bằng phương thức kiên trì (hoặc bộ đệm), trong trường hợp đó Spark sẽ giữ các phần tử xung quanh trên cụm để truy cập nhanh hơn nhiều vào lần tiếp theo bạn truy vấn. Ngoài ra còn có hỗ trợ để duy trì RDD trên đĩa hoặc được nhân rộng trên nhiều nút.

Để biết thêm chi tiết xin vui lòng kiểm tra hướng dẫn lập trình Spark .


1
Điều đó đã không trả lời câu hỏi của tôi.
Ramana

Điều gì không trả lời nó?
eliasah 11/03/2015

1
khi dữ liệu của RDD được lưu trong mặc định bộ nhớ, tại sao chúng ta cần gọi Cache hoặc Persist?
Ramana

Theo mặc định, RDD không được lưu trữ trong bộ nhớ, do đó, việc lưu giữ RDD giúp Spark thực hiện chuyển đổi nhanh hơn trên cụm
eliasah 11/03/2015

2
Đó là một câu trả lời hay, tôi không biết tại sao nó lại bị đánh giá thấp. Đó là câu trả lời từ trên xuống, giải thích cách RDD hoạt động từ các khái niệm cấp cao. Tôi đã thêm một câu trả lời khác từ dưới lên: bắt đầu từ "dòng này làm gì". Có lẽ dễ dàng hơn để theo dõi đối với một người mới bắt đầu với Spark.
Daniel Darabos

11

Dưới đây là ba tình huống bạn nên lưu trữ RDD của mình:

sử dụng RDD nhiều lần

thực hiện nhiều hành động trên cùng một RDD

cho chuỗi dài biến đổi (hoặc rất tốn kém)


7

Thêm một lý do khác để thêm (hoặc tạm thời thêm) cachecuộc gọi phương thức.

cho các vấn đề bộ nhớ gỡ lỗi

với cachephương thức, spark sẽ cung cấp thông tin gỡ lỗi liên quan đến kích thước của RDD. vì vậy, trong UI được tích hợp tia lửa, bạn sẽ nhận được thông tin tiêu thụ bộ nhớ RDD. và điều này đã chứng minh rất hữu ích trong việc chẩn đoán các vấn đề về bộ nhớ.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.