Dalam penugasan ini Anda akan menerapkan caching hasil UDF (fungsi yang ditentukan pengguna) di Apache Spark, yang merupakan kerangka kerja untuk komputasi terdistribusi dalam cetakan MapReduce. Proyek ini akan menggambarkan konsep-konsep kunci dalam Evaluasi Data Rendezvous dan kueri, dan Anda akan mendapatkan beberapa pengalaman langsung memodifikasi Spark, yang banyak digunakan di lapangan. Selain itu, Anda akan mendapatkan eksposur ke Scala, bahasa berbasis JVM yang mendapatkan popularitas untuk gaya fungsionalnya yang bersih.
Tanggal jatuh tempo penugasan diterbitkan di situs web kelas.
Anda dapat menyelesaikan ini berpasangan , jika Anda memilih. Terakhir, ada banyak kode di direktori ini. Silakan lihat di sini untuk menemukan direktori tempat kode berada.
Spark adalah sistem komputasi terdistribusi sumber terbuka yang ditulis dalam Scala. Proyek ini dimulai oleh Ph.D. Siswa dari AMPLAB dan merupakan bagian integral dari tumpukan analitik data Berkeley (BDA-diucapkan secara efektif "bad-ass").
Seperti Hadoop MapReduce, Spark dirancang untuk menjalankan fungsi melalui koleksi besar data, dengan mendukung serangkaian operasi pemrosesan data tingkat tinggi yang disederhanakan yang mirip dengan iterator yang telah kami pelajari di kelas. Salah satu penggunaan sistem yang paling umum adalah menerapkan pemrosesan kueri paralel dalam bahasa tingkat tinggi seperti SQL. Faktanya, banyak upaya penelitian dan pengembangan baru -baru ini dalam Spark telah menuju mendukung abstraksi basis data relasional yang dapat diskalakan dan interaktif.
Kami akan menggunakan, memodifikasi, dan mempelajari aspek -aspek Spark di kelas ini untuk memahami konsep -konsep kunci sistem data modern. Lebih penting lagi Anda akan melihat bahwa ide -ide yang kami liput di kelas - beberapa di antaranya berumur beberapa dekade - masih sangat relevan saat ini. Secara khusus, kami akan menambahkan fitur untuk memicu SQL.
Salah satu batasan utama Spark SQL adalah saat ini merupakan sistem yang hanya memori utama. Sebagai bagian dari kelas ini, kami akan memperluasnya untuk memasukkan beberapa algoritma di luar inti juga.
Scala adalah bahasa yang diketik secara statis yang mendukung banyak paradigma pemrograman yang berbeda. Fleksibilitas, daya, dan portabilitasnya menjadi sangat berguna dalam penelitian sistem terdistribusi.
Scala menyerupai Java, tetapi memiliki serangkaian fitur sintaksis yang jauh lebih luas untuk memfasilitasi banyak paradigma. Mengetahui Java akan membantu Anda memahami beberapa kode Scala, tetapi tidak banyak dari itu, dan tidak tahu Scala akan mencegah Anda untuk sepenuhnya mengambil keuntungan dari kekuatan ekspresifnya. Karena Anda harus menulis kode di Scala, kami sangat menyarankan Anda untuk memperoleh setidaknya keakraban dengan bahasa tersebut.
Gagasan IntelliJ cenderung menjadi IDE yang paling umum digunakan untuk berkembang di Spark. Intellij adalah IDE Java yang memiliki plugin Scala (dan Vim!). Ada juga opsi lain seperti Scala-Id.
Anda mungkin menemukan tutorial berikut bermanfaat:
Fungsi yang ditentukan pengguna memungkinkan pengembang untuk mendefinisikan dan mengeksploitasi operasi khusus dalam ekspresi. Bayangkan, misalnya, bahwa Anda memiliki katalog produk yang menyertakan foto kemasan produk. Anda mungkin ingin mendaftarkan fungsi yang ditentukan pengguna extract_text
yang memanggil algoritma OCR dan mengembalikan teks dalam gambar, sehingga Anda bisa mendapatkan informasi yang dapat ditanyakan dari foto. Di SQL, Anda bisa membayangkan kueri seperti ini:
SELECT P.name, P.manufacturer, P.price, extract_text(P.image),
FROM Products P;
Kemampuan untuk mendaftarkan UDF sangat kuat - pada dasarnya mengubah kerangka pemrosesan data Anda menjadi kerangka komputasi terdistribusi umum. Tetapi UDF sering dapat memperkenalkan kemacetan kinerja, terutama saat kami menjalankannya lebih dari jutaan item data.
Jika kolom input ke UDF berisi banyak nilai duplikat, dapat bermanfaat untuk meningkatkan kinerja dengan memastikan bahwa UDF hanya disebut sekali per nilai input yang berbeda , daripada sekali per baris . (Misalnya dalam contoh produk kami di atas, semua konfigurasi berbeda dari PC tertentu mungkin memiliki gambar yang sama.) Dalam penugasan ini, kami akan menerapkan optimasi ini. Kami akan meminumnya secara bertahap-pertama-tama dapat bekerja untuk data yang sesuai dengan memori, dan kemudian untuk set yang lebih besar yang memerlukan pendekatan di luar inti. Kami akan menggunakan hashing eksternal sebagai teknik untuk "bertemu" semua baris dengan nilai input yang sama untuk UDF.
Jika Anda tertarik dengan topik ini, makalah berikut akan menjadi bacaan yang menarik (termasuk optimasi tambahan di luar apa yang kita punya waktu dalam pekerjaan rumah ini):
Semua kode yang akan Anda sentuh akan ada dalam tiga file - CS143Utils.scala
, basicOperators.scala
, dan DiskHashedRelation.scala
. Namun Anda mungkin perlu berkonsultasi dengan file lain dalam Spark atau General Scala API untuk menyelesaikan penugasan secara menyeluruh. Pastikan Anda melihat semua kode yang disediakan di tiga file yang disebutkan di atas sebelum mulai menulis kode Anda sendiri. Ada banyak fungsi yang berguna di CS143Utils.scala
serta di DiskHashedRelation.scala
yang akan menghemat banyak waktu dan mengutuk - manfaatkan mereka!
Secara umum, kami telah mendefinisikan sebagian besar (jika tidak semua) dari metode yang Anda butuhkan. Seperti sebelumnya, dalam proyek ini, Anda harus mengisi kerangka. Jumlah kode yang akan Anda tulis tidak terlalu tinggi - solusi staf total kurang dari 100 baris kode (tidak termasuk tes). Namun, merangkai komponen yang tepat dengan cara yang efisien memori (yaitu, tidak membaca seluruh hubungan ke dalam memori sekaligus) akan membutuhkan pemikiran dan perencanaan yang cermat.
Ada beberapa perbedaan yang berpotensi membingungkan antara terminologi yang kami gunakan di kelas, dan terminologi yang digunakan dalam basis kode SparksQL:
Konsep "iterator" yang kami pelajari dalam kuliah disebut "simpul" dalam kode SparksQL - ada definisi dalam kode untuk UnaryNode dan BinaryNode. Rencana kueri disebut sparkplan, dan pada kenyataannya UnaryNode dan BinaryNode memperpanjang sparkplan (setelah semua, satu iterator adalah rencana kueri kecil!) Anda mungkin ingin menemukan file SparkPlan.scala
di sumber sparksql untuk melihat API untuk ini untuk ini untuk ini untuk ini untuk ini untuk ini untuk ini untuk ini untuk ini untuk ini untuk ini untuk ini untuk ini untuk ini untuk ini node.
Dalam beberapa komentar di Sparksql, mereka juga menggunakan istilah "operator" untuk berarti "simpul". File basicOperators.scala
mendefinisikan sejumlah node spesifik (mis. Sortir, berbeda, dll.).
Jangan membingungkan scala antarmuka Iterator
dengan konsep iterator yang kami liput dalam kuliah. Iterator
yang akan Anda gunakan dalam proyek ini adalah fitur bahasa Scala yang akan Anda gunakan untuk mengimplementasikan node sparksql Anda. Iterator
menyediakan antarmuka untuk koleksi Scala yang menegakkan API tertentu: fungsi next
dan hasNext
.
git
dan github git
adalah sistem kontrol versi , membantu Anda melacak berbagai versi kode Anda, menyinkronkannya di berbagai mesin, dan berkolaborasi dengan orang lain. GitHub adalah situs yang mendukung sistem ini, menampungnya sebagai layanan.
Jika Anda tidak tahu banyak tentang git
, kami sangat menyarankan Anda untuk membiasakan diri dengan sistem ini; Anda akan menghabiskan banyak waktu dengannya! Ada banyak panduan untuk menggunakan git
online - ini yang bagus untuk dibaca.
Anda harus terlebih dahulu mengatur repositori pribadi jarak jauh (misalnya, spark-homework). GitHub memberikan repositori pribadi kepada siswa (tetapi ini mungkin memakan waktu). Jika Anda tidak memiliki repositori pribadi, pikirkan dua kali tentang memeriksanya di repositori publik, karena akan tersedia untuk orang lain untuk Checheckout.
$ cd ~
Kloning repositori pribadi Anda. Itu harus kosong.
$ git clone "https://github.com/xx/yy.git"
Masukkan repositori yang diklon, lacak repositori kursus dan klon.
$ cd yy/
$ git remote add course "https://github.com/ariyam/cs143_spark_hw.git"
$ git pull course master
Catatan: Tolong jangan kewalahan dengan jumlah kode yang ada di sini. Spark adalah proyek besar dengan banyak fitur. Kode yang akan kami sentuh akan terkandung dalam satu direktori spesifik: SQL/Core/SRC/Main/Scala/org/Apache/Spark/SQL/Execution/. Tes semua akan terkandung dalam SQL/Core/Src/Test/Scala/org/Apache/Spark/SQL/Execution/
Dorong klon ke repositori pribadi Anda.
$ git push origin master
Setiap kali Anda menambahkan beberapa kode, Anda dapat melakukan modifikasi ke repositori jarak jauh.
$ git commit -m 'update to homework'
$ git push origin master
Mungkin perlu menerima pembaruan untuk tugas kami (meskipun kami mencoba melepaskannya sebagai "sempurna" mungkin pertama kali). Dengan asumsi Anda mengatur pelacakan dengan benar, Anda cukup menjalankan perintah berikut ini untuk menerima pembaruan tugas:
$ git pull course master
Perintah UNIX berikut akan berguna, ketika Anda perlu menemukan lokasi file. Contoh- Temukan lokasi file bernama 'diskhashedrelation.scala' di repositori saya saat ini.
$ find ./ -name 'DiskHashedRelation.scala'
Setelah Anda menarik kode, cd
ke {repo root}
dan jalankan make compile
. Pertama kali Anda menjalankan perintah ini, perlu beberapa saat - sbt
akan mengunduh semua dependensi dan menyusun semua kode dalam Spark (ada sedikit kode). Setelah perintah perakitan awal selesai, Anda dapat memulai proyek Anda! (Bangunan di masa depan tidak boleh memakan waktu selama ini - sbt
cukup pintar untuk hanya mengkompilasi ulang file yang diubah, kecuali jika Anda berjalan make clean
, yang akan menghapus semua file kelas yang dikompilasi.)
Kami telah memberi Anda kode kerangka untuk DiskHashedRelation.scala
. File ini memiliki 4 hal penting:
trait DiskHashedRelation
yang dapat menentukan antarmuka diskhashedrelationclass GeneralDiskHashedRelation
adalah implementasi kami dari sifat DiskedHashedRelation
class DiskPartition
mewakili satu partisi di diskobject DiskHashedRelation
dapat dianggap sebagai pabrik objek yang membangun GeneralDiskHashedRelation
DiskPartition
dan GeneralDiskHashedRelation
Pertama, Anda perlu mengimplementasikan metode insert
, closeInput
, dan getData
dalam DiskPartition
untuk bagian ini. Untuk keduanya, Docstrings harus memberikan deskripsi komprehensif tentang apa yang harus Anda terapkan. Peringatan dengan getData
adalah bahwa Anda tidak dapat membaca seluruh partisi ke dalam memori sekali. Alasan kami menegakkan batasan ini adalah karena tidak ada cara yang baik untuk menegakkan memori membebaskan di JVM, dan ketika Anda mengubah data ke berbagai bentuk, akan ada banyak salinan yang tergeletak di sekitar. Dengan demikian, memiliki banyak salinan dari seluruh partisi akan menyebabkan hal -hal tumpah ke disk dan akan membuat kita semua sedih. Sebaliknya, Anda harus mengalirkan satu blok ke dalam memori sekaligus.
Pada titik ini, Anda harus lulus tes di DiskPartitionSuite.scala
.
object DiskHashedRelation
Tugas Anda di bagian ini adalah menerapkan fase 1 hashing eksternal-menggunakan fungsi hash berbutir kasar untuk mengalirkan input ke dalam beberapa hubungan partisi pada disk. Untuk tujuan kami, metode hashCode
yang dimiliki setiap objek sudah cukup untuk menghasilkan nilai hash, dan mengambil modulo dengan jumlah partisi adalah fungsi hash yang dapat diterima.
Pada titik ini, Anda harus lulus semua tes di DiskHashedRelationSuite.scala
.
Di bagian ini, kami akan berurusan dengan case class CacheProject
DI basicOperators.scala
. Anda mungkin memperhatikan bahwa hanya ada 4 baris kode di kelas ini dan, yang lebih penting, no // IMPLEMENT ME
s. Anda sebenarnya tidak harus menulis kode apa pun di sini. Namun, jika Anda melacak panggilan fungsi di baris 66, Anda akan menemukan bahwa ada dua bagian dari tumpukan ini yang harus Anda terapkan untuk memiliki implementasi UDF dalam memori fungsional.
CS143Utils
Untuk tugas ini, Anda perlu mengimplementasikan getUdfFromExpressions
dan metode Iterator
di CachingIteratorGenerator#apply
. Harap baca Docstrings - terutama untuk apply
- dekat sebelum memulai.
Setelah menerapkan metode ini, Anda harus lulus tes di CS143UtilsSuite.scala
.
Petunjuk: Pikirkan baik -baik tentang mengapa metode ini mungkin menjadi bagian dari utils
Sekarang datanglah momen kebenaran! Kami telah menerapkan partisi hash berbasis disk, dan kami telah menerapkan caching UDF dalam memori-apa yang kadang-kadang disebut memoisasi. Memoisasi adalah alat yang sangat kuat dalam banyak konteks, tetapi di sini di lahan basis data, kami menangani jumlah data yang lebih besar daripada yang dapat ditangani oleh memoisasi. Jika kita memiliki nilai yang lebih unik daripada yang bisa sesuai dengan cache dalam memori, kinerja kita akan dengan cepat menurun. Dengan demikian, kita kembali ke tradisi basis data yang dihormati waktu dari pembagian-dan-penakluk. Jika data kami tidak sesuai dengan memori, maka kami dapat mempartisi ke disk sekali, baca satu partisi pada satu waktu (pikirkan mengapa ini berfungsi (petunjuk: rendezvous!)), Dan melakukan caching UDF, mengevaluasi satu partisi pada satu waktu .
PartitionProject
Tugas akhir ini mengharuskan Anda mengisi implementasi PartitionProject
. Semua kode yang perlu Anda tulis ada dalam metode generateIterator
. Pikirkan baik -baik tentang bagaimana Anda perlu mengatur implementasi Anda. Anda tidak boleh buffering semua data dalam memori atau apa pun yang mirip dengan itu.
Pada titik ini, Anda harus lulus semua tes yang diberikan.
Tidak ada kode yang harus Anda tulis di sini, tetapi untuk Edifikasi Anda sendiri, luangkan waktu memikirkan pertanyaan berikut:
Salah satu nilai jual utama Spark adalah "dalam memori". Yang mereka maksud adalah sebagai berikut: Ketika Anda merangkai sejumlah pekerjaan Hadoop (atau kerangka kerja MapReduce lainnya) bersama -sama, Hadoop akan menulis hasil dari setiap fase ke disk dan membacanya lagi yang sangat mahal; Spark, di sisi lain, menyimpan datanya dalam memori. Namun, jika asumsi kami adalah bahwa jika data kami tidak sesuai dengan memori, lalu mengapa Spark SQL tidak mengirim dengan implementasi berbasis disk dari operator ini? Dalam hal ini, mengapa Spark berbeda dari database relasional paralel "tradisional" yang kita pelajari di kelas? Tidak ada jawaban yang benar untuk pertanyaan ini!
Kami telah memberi Anda beberapa tes sampel di DiskPartitionSuite.scala
, DiskHasedRelationSuite.scala
, CS143UtilsSuite.scala
dan ProjectSuite.scala
. Tes ini dapat memandu Anda saat Anda menyelesaikan proyek ini. Namun, perlu diingat bahwa mereka tidak komprehensif, dan Anda disarankan untuk menulis tes Anda sendiri untuk menangkap bug. Mudah -mudahan, Anda dapat menggunakan tes ini sebagai model untuk menghasilkan tes Anda sendiri.
Untuk menjalankan tes kami, kami telah menyediakan makefile sederhana. Untuk menjalankan tes untuk Tugas 1, jalankan make t1
. Sejalan dengan tugas, jalankan make t2
, dan sama untuk semua tes lainnya. make all
akan menjalankan semua tes.
Tautan pengiriman akan dibuat di CCLE, di mana Anda dapat mengirimkan kode Anda pada tanggal jatuh tempo.
Terima kasih banyak untuk Matteo Interlandi.
Semoga beruntung!