في هذه المهمة ، ستنفذ تأكيد udf (وظيفة محددة من قبل المستخدم) في التخزين المؤقت في Apache Spark ، وهو إطار للحوسبة الموزعة في قالب MapReduce. سيوضح هذا المشروع مفاهيم رئيسية في تقييم البيانات وتقييم الاستعلام ، وستحصل على بعض الخبرة العملية في تعديل الشرارة ، والتي تستخدم على نطاق واسع في هذا المجال. بالإضافة إلى ذلك ، ستحصل على التعرض لـ Scala ، وهي لغة قائمة على JVM تكتسب شعبية لأسلوبها الوظيفي النظيف.
يتم نشر تاريخ استحقاق المهمة على موقع الفصل.
يمكنك إكمال هذا في أزواج ، إذا اخترت ذلك. أخيرًا ، هناك الكثير من التعليمات البرمجية في هذا الدليل. يرجى النظر هنا هنا للعثور على الدليل الذي يقع فيه الرمز.
Spark هو نظام حوسبة موزع مفتوح المصدر مكتوب في Scala. بدأ المشروع الدكتوراه الطلاب من Amplab وهو جزء لا يتجزأ من مكدس تحليلات بيانات Berkeley (BDAS-وضوحا "Bad-Ass").
مثل Hadoop MapReduce ، تم تصميم Spark لتشغيل وظائف على مجموعات كبيرة من البيانات ، من خلال دعم مجموعة مبسطة من عمليات معالجة البيانات عالية المستوى والتي تشبه المتكررين الذين نتعلمهم في الفصل. أحد أكثر الاستخدامات شيوعًا لهذه الأنظمة هو تنفيذ معالجة الاستعلام المتوازية بلغات عالية المستوى مثل SQL. في الواقع ، ذهبت العديد من جهود البحث والتطوير الحديثة في Spark إلى دعم تجريد قاعدة بيانات علائقية قابلة للتطوير وتفاعلية.
سنستخدم جوانب الشرارة في هذا الفصل وتعديلها ودراستها لفهم المفاهيم الرئيسية لأنظمة البيانات الحديثة. والأهم من ذلك أنك سترى أن الأفكار التي نغطيها في الفصل - والتي يبلغ عمرها عقود من الزمن - لا تزال ذات صلة كبيرة اليوم. على وجه التحديد ، سنضيف ميزات لإشراق SQL.
أحد القيود الرئيسية لـ Spark SQL هو أنه نظام رئيسي في الذاكرة فقط. كجزء من هذه الفئة ، سنقوم بتمديدها لتشمل بعض الخوارزميات خارج النوى أيضًا.
Scala هي لغة من النوع الثابت تدعم العديد من نماذج البرمجة المختلفة. أصبحت مرونتها وقوتها وقابلية النقل مفيدة بشكل خاص في أبحاث الأنظمة الموزعة.
تشبه Scala Java ، ولكنها تمتلك مجموعة أوسع بكثير من ميزات بناء الجملة لتسهيل نماذج متعددة. ستساعدك معرفة Java على فهم بعض رمز Scala ، ولكن ليس الكثير منها ، وعدم معرفة Scala سيمنعك من الاستفادة الكاملة من قوتها التعبيرية. نظرًا لأنه يجب عليك كتابة التعليمات البرمجية في Scala ، فإننا نوصيك بشدة باكتساب الألفة المارة على الأقل مع اللغة.
تميل Idea Intellij إلى أن تكون IDE الأكثر استخدامًا للتطوير في Spark. Intellij هو java ide الذي يحتوي على مكون إضافي Scala (و Vim!). هناك أيضًا خيارات أخرى مثل Scala-ide.
قد تجد أن البرامج التعليمية التالية مفيدة:
تسمح الوظائف المعرفة من قبل المستخدم للمطورين بتحديد واستغلال العمليات المخصصة داخل التعبيرات. تخيل ، على سبيل المثال ، أن لديك كتالوج منتج يتضمن صورًا لتعبئة المنتج. قد ترغب في تسجيل دالة محددة من قبل المستخدم extract_text
التي تستدعي خوارزمية OCR وإرجاع النص في صورة ، بحيث يمكنك الحصول على معلومات قابلة للاستعلام من الصور. في SQL ، يمكنك أن تتخيل استعلام مثل هذا:
SELECT P.name, P.manufacturer, P.price, extract_text(P.image),
FROM Products P;
القدرة على تسجيل UDFS قوية للغاية - إنها تحول بشكل أساسي إطار معالجة البيانات إلى إطار حوسبة موزع عام. لكن في كثير من الأحيان يمكن أن تقدم UDFs اختناقات الأداء ، خاصةً مع تشغيلها على ملايين عناصر البيانات.
إذا كان عمود (أعمدة) الإدخال إلى UDF يحتوي على الكثير من القيم المكررة ، فقد يكون من المفيد تحسين الأداء من خلال التأكد من أن UDF يسمى مرة واحدة فقط لكل قيمة إدخال مميزة ، بدلاً من مرة واحدة لكل صف . (على سبيل المثال ، مثال على منتجاتنا أعلاه ، قد تحتوي جميع التكوينات المختلفة لجهاز كمبيوتر معين على نفس الصورة.) في هذه المهمة ، سنقوم بتنفيذ هذا التحسين. سنأخذها على مراحل-أولاً ، قم بعملها أولاً من أجل البيانات التي تناسب الذاكرة ، ثم في وقت لاحق لمجموعات أكبر تتطلب نهجًا خارج النواة. سوف نستخدم التجزئة الخارجية كتقنية لـ "Rendezvous" جميع الصفوف مع نفس قيم الإدخال لـ UDF.
إذا كنت مهتمًا بالموضوع ، فستكون الورقة التالية قراءة مثيرة للاهتمام (بما في ذلك التحسينات الإضافية التي تتجاوز ما لدينا وقت في هذا الواجب المنزلي):
جميع التعليمات البرمجية التي ستسلمها ستكون في ثلاثة ملفات - CS143Utils.scala
و basicOperators.scala
و DiskHashedRelation.scala
. ومع ذلك ، قد تحتاج إلى استشارة الملفات الأخرى داخل Spark أو APIs General Scala من أجل إكمال المهمة بدقة. يرجى التأكد من إلقاء نظرة على جميع الرمز المقدم في الملفات الثلاثة المذكورة أعلاه قبل البدء في كتابة التعليمات البرمجية الخاصة بك. هناك الكثير من الوظائف المفيدة في CS143Utils.scala
وكذلك في DiskHashedRelation.scala
التي ستوفر لك الكثير من الوقت والملعين - الاستفادة منها!
بشكل عام ، حددنا معظم الأساليب التي ستحتاجها (إن لم يكن كلها). كما كان من قبل ، في هذا المشروع ، تحتاج إلى ملء الهيكل العظمي. إن مقدار الكود الذي ستكتبه ليس مرتفعًا جدًا - فإن إجمالي حل الموظفين أقل من 100 سطر من التعليمات البرمجية (لا يشمل الاختبارات). ومع ذلك ، فإن ربط المكونات الصحيحة بطريقة فعالة للذاكرة (أي ، فإن عدم قراءة العلاقة كلها في الذاكرة في وقت واحد) سيتطلب بعض التفكير والتخطيط الدقيق.
هناك بعض الاختلافات المحتملة المحتملة بين المصطلحات التي نستخدمها في الفصل ، والمصطلحات المستخدمة في قاعدة رمز Sparksql:
يُطلق على مفهوم "التكرار" الذي تعلمناه في المحاضرة "عقدة" في رمز Sparksql - هناك تعريفات في الكود الخاص بـ UnaryNode و BinaryNode. تسمى خطة الاستعلام "Sparkplan SparkPlan.scala
، وفي الواقع ، تمتد أحادي الصناديق و BinaryNode (بعد كل شيء ، التكرار الواحد هو خطة استعلام صغيرة!) قد ترغب في العثور العقد.
في بعض التعليقات في Sparksql ، يستخدمون أيضًا مصطلح "المشغل" ليعني "العقدة". يحدد File basicOperators.scala
عددًا من العقد المحددة (على سبيل المثال ، متميز ، إلخ).
لا تخلط بين Iterator
واجهة سكالا ومفهوم التكرار الذي غطناه في المحاضرة. يعد Iterator
الذي ستستخدمه في هذا المشروع ميزة لغة Scala التي ستستخدمها لتنفيذ عقد SparksQL الخاصة بك. يوفر Iterator
واجهة لمجموعات Scala التي تفرض واجهة برمجة تطبيقات محددة: الوظائف next
و hasNext
.
git
و github git
هو نظام التحكم في الإصدار ، ويساعدك على تتبع إصدارات مختلفة من الكود الخاص بك ، ومزامنةها عبر أجهزة مختلفة ، والتعاون مع الآخرين. Github هو موقع يدعم هذا النظام ، ويستضيفه كخدمة.
إذا كنت لا تعرف الكثير عن git
، فإننا نوصيك بشدة بالتعرف على هذا النظام ؛ ستقضي الكثير من الوقت معها! هناك العديد من الأدلة لاستخدام git
Online - إليك واحدة رائعة للقراءة.
يجب عليك أولاً إنشاء مستودع خاص عن بُعد (على سبيل المثال ، عمل الشرارة). يعطي Github مستودعًا خاصًا للطلاب (ولكن هذا قد يستغرق بعض الوقت). إذا لم يكن لديك مستودع خاص ، فكر مرتين في التحقق منه في المستودع العام ، حيث سيكون متاحًا للآخرين إلى Checheckout.
$ cd ~
استنساخ مستودعك الشخصي. يجب أن يكون فارغا.
$ git clone "https://github.com/xx/yy.git"
أدخل مستودع الاستنساخ ، وتتبع مستودع الدورة التدريبية واستنساخه.
$ cd yy/
$ git remote add course "https://github.com/ariyam/cs143_spark_hw.git"
$ git pull course master
ملاحظة: من فضلك لا تغمرها مقدار الرمز الموجود هنا. Spark هو مشروع كبير مع الكثير من الميزات. سيتم احتواء الكود الذي سنقوم بلمسه ضمن دليل واحد محدد: SQL/CORE/SRC/MAIN/SCALA/ORG/APACHE/SQL/SQL/EXECUTION/. سيتم احتواء جميع الاختبارات في SQL/CORE/SRC/TEST/SCALA/ORG/APACHE/Spark/SQL/Execution/
ادفع استنساخ إلى مستودعك الشخصي.
$ git push origin master
في كل مرة تضيف فيها بعض التعليمات البرمجية ، يمكنك ارتكاب التعديلات على المستودع البعيد.
$ git commit -m 'update to homework'
$ git push origin master
قد يكون من الضروري تلقي تحديثات لمهمتنا (على الرغم من أننا نحاول إطلاقها "بشكل مثالي" قدر الإمكان في المرة الأولى). على افتراض أنك تقوم بإعداد التتبع بشكل صحيح ، يمكنك ببساطة تشغيل هذا الأمر التالي لتلقي تحديثات المهمة:
$ git pull course master
سيأتي أمر UNIX التالي في متناول يدي ، عندما تحتاج إلى العثور على موقع ملف. مثال- ابحث عن موقع ملف يسمى "diskhashedrelation.scala" في مستودعي الحالي.
$ find ./ -name 'DiskHashedRelation.scala'
بمجرد سحب الكود ، cd
إلى {repo root}
وقم make compile
. في المرة الأولى التي تقوم فيها بتشغيل هذا الأمر ، يجب أن يستغرق الأمر بعض الوقت - ستقوم sbt
بتنزيل جميع التبعيات وتجميع جميع الكود في Spark (هناك القليل من التعليمات البرمجية). بمجرد الانتهاء من أوامر التجميع الأولية ، يمكنك بدء مشروعك! (يجب ألا تستغرق الإنشاءات المستقبلية هذا الوقت الطويل - sbt
ذكي بما يكفي لإعادة ترجمة الملفات التي تم تغييرها فقط ، إلا إذا قمت بتشغيل make clean
، والتي ستقوم بإزالة جميع ملفات الفصل المترجمة.)
لقد وفرنا لك رمز الهيكل العظمي ل DiskHashedRelation.scala
. يحتوي هذا الملف على 4 أشياء مهمة:
trait DiskHashedRelation
واجهة diskhashedrelationclass GeneralDiskHashedRelation
هي تنفيذنا لسمات DiskedHashedRelation
class DiskPartition
قسمًا واحدًا على القرصobject DiskHashedRelation
مصنع للكائن الذي يبني GeneralDiskHashedRelation
sDiskPartition
و GeneralDiskHashedRelation
أولاً ، ستحتاج إلى تنفيذ أساليب insert
closeInput
و getData
في DiskPartition
لهذا الجزء. بالنسبة للثمين ، يجب أن توفر docstrings وصفًا شاملاً لما يجب تنفيذه. التحذير مع getData
هو أنه لا يمكنك قراءة القسم بأكمله في الذاكرة مرة واحدة. السبب في أننا نفرض هذا التقييد هو أنه لا توجد طريقة جيدة لفرض الذاكرة الحرة في JVM ، وبينما تقوم بتحويل البيانات إلى أشكال مختلفة ، ستكون هناك نسخ متعددة ملقاة. على هذا النحو ، فإن وجود نسخ متعددة من قسم كامل من شأنه أن يتسبب في انسكاب الأمور على القرص وسيجعلنا جميعًا حزينًا. بدلاً من ذلك ، يجب عليك بث كتلة واحدة في الذاكرة في وقت واحد.
في هذه المرحلة ، يجب أن تجتاز الاختبارات في DiskPartitionSuite.scala
.
object DiskHashedRelation
ستكون مهمتك في هذا الجزء هي تنفيذ المرحلة الأولى من التجزئة الخارجية-باستخدام دالة التجزئة الخشنة الحبيبية لدفق مدخلات في علاقات قسم متعددة على القرص. لأغراضنا ، فإن طريقة hashCode
التي لدى كل كائن تكفي لإنشاء قيمة تجزئة ، وأخذ modulo بعدد الأقسام هو وظيفة تجزئة مقبولة.
في هذه المرحلة ، يجب أن تجتاز جميع الاختبارات في DiskHashedRelationSuite.scala
.
في هذا القسم ، سوف نتعامل مع case class CacheProject
في basicOperators.scala
. قد تلاحظ أنه لا يوجد سوى 4 أسطر من التعليمات البرمجية في هذه الفئة ، والأهم من ذلك ، لا // IMPLEMENT ME
. ليس عليك في الواقع كتابة أي رمز هنا. ومع ذلك ، إذا قمت بتتبع استدعاء الوظيفة في السطر 66 ، فستجد أن هناك جزءين من هذه المكدس يجب أن تنفذه من أجل الحصول على تطبيق UDF وظيفي.
CS143Utils
بالنسبة لهذه المهمة ، ستحتاج إلى تنفيذ getUdfFromExpressions
وطرق Iterator
في CachingIteratorGenerator#apply
. يرجى قراءة docstrings - وخاصة apply
- عن كثب قبل البدء.
بعد تنفيذ هذه الأساليب ، يجب أن تجتاز الاختبارات في CS143UtilsSuite.scala
.
تلميح: فكر بعناية في سبب أن تكون هذه الأساليب جزءًا من utils
الآن تأتي لحظة الحقيقة! لقد قمنا بتنفيذ تقسيم التجزئة القائم على القرص ، وقمنا بتنفيذ التخزين المؤقت في الذاكرة UDF-ما يسمى أحيانًا المذكرات. تعتبر Memoization أداة قوية للغاية في العديد من السياقات ، ولكن هنا في قواعد البيانات ، نتعامل مع كميات أكبر من البيانات مما يمكن أن تتعامل معه المذكرة. إذا كان لدينا قيم فريدة أكثر مما يمكن أن يتناسب مع ذاكرة التخزين المؤقت في الذاكرة ، فإن أدائنا سوف يتحلل بسرعة. وبالتالي ، فإننا نعود إلى تقليد قواعد البيانات المحفوظة بالوقت من الفجوة والقهر. إذا كانت بياناتنا لا تتناسب مع الذاكرة ، فيمكننا تقسيمها إلى القرص مرة واحدة ، وقراءة قسم واحد في وقت واحد (فكر في سبب عمل هذا (تلميح: rendezvous!) ، وأداء التخزين المؤقت UDF ، وتقييم قسم واحد في وقت واحد .
PartitionProject
تتطلب هذه المهمة النهائية أن تملأ تنفيذ PartitionProject
. كل الرمز الذي ستحتاج إلى كتابته هو في طريقة generateIterator
. فكر بعناية في كيفية حاجة لتنظيم تنفيذك. يجب ألا تكون تخفي كل البيانات في الذاكرة أو أي شيء مشابه لذلك.
في هذه المرحلة ، يجب أن تجتاز جميع الاختبارات المعطاة.
لا يوجد رمز يجب أن تكتبه هنا ، ولكن من أجل تنفياتك الخاصة ، قضاء بعض الوقت في التفكير في السؤال التالي:
واحدة من نقاط البيع الرئيسية لشركة Spark هي أنها "في الذاكرة". ما يعنيه هو ما يلي: عندما تقوم بتوصيل عدد من وظائف Hadoop (أو أي إطار عمل آخر MapReduce) معًا ، ستقوم Hadoop بكتابة نتائج كل مرحلة إلى القرص وقراءتها مرة أخرى باهظة الثمن ؛ Spark ، من ناحية أخرى ، تحافظ على بياناتها في الذاكرة. ومع ذلك ، إذا كان افتراضنا هو أنه إذا كانت بياناتنا لا تتناسب مع الذاكرة ، فلماذا لا يشحن Spark SQL مع تنفيذ هؤلاء المشغلين القائم على القرص بالفعل؟ في هذا الصدد ، لماذا تختلف Spark عن قواعد البيانات العلائقية المتوازية "التقليدية" التي نتعرف عليها في الفصل؟ لا يوجد إجابة صحيحة على هذا السؤال!
لقد قدمنا لك بعض اختبارات العينة في DiskPartitionSuite.scala
، DiskHasedRelationSuite.scala
، CS143UtilsSuite.scala
و ProjectSuite.scala
. يمكن أن توجهك هذه الاختبارات أثناء إكمال هذا المشروع. ومع ذلك ، ضع في اعتبارك أنها ليست شاملة ، ويُنصح جيدًا بكتابة اختباراتك الخاصة للقبض على الأخطاء. نأمل أن تتمكن من استخدام هذه الاختبارات كنماذج لإنشاء الاختبارات الخاصة بك.
من أجل إجراء اختباراتنا ، قدمنا makefile بسيطة. من أجل إجراء اختبارات المهمة 1 ، قم بتشغيل make t1
. في المقابل للمهمة ، تشغيل make t2
، والشيء نفسه لجميع الاختبارات الأخرى. make all
يدير جميع الاختبارات.
سيتم إنشاء رابط التقديم على Ccle ، حيث يمكنك إرسال الرمز الخاص بك في تاريخ الاستحقاق.
شكرا جزيلا لماتيو إنترلاندي.
حظ سعيد!