أفضل ممارسات أداء Apache Phoenix

يتمثل أهم جانب في أداء Apache Phoenix في تحسين Apache HBase الأساسي. ينشئ Phoenix نموذج بيانات علائقية أعلى HBase يحول استعلامات SQL إلى عمليات HBase، مثل عمليات الفحص. يؤثر تصميم مخطط الجدول الخاص بك، واختيار الحقول في المفتاح الأساسي وترتيبها، واستخدامك للفهارس على أداء Phoenix.

تصميم مخطط الجدول

عند إنشاء جدول في Phoenix، يتم تخزين هذا الجدول في جدول HBase. يحتوي جدول HBase على مجموعات من الأعمدة (عائلات الأعمدة) التي يمكن الوصول إليها معاً. الصف في جدول Phoenix هو صف في جدول HBase، حيث يتكون كل صف من خلايا ذات إصدارات مرتبطة بعمود واحد أو أكثر. منطقياً، صف HBase الفردي عبارة عن مجموعة من أزواج المفتاح والقيمة، ولكل منها نفس قيمة مفتاح الصف. أي أن كل زوج من المفاتيح والقيمة له سمة مفتاح صف، وقيمة سمة مفتاح الصف هذه هي نفسها لصف معين.

يتضمن تصميم مخطط جدول Phoenix تصميم المفتاح الأساسي وتصميم عائلة العمود وتصميم العمود الفردي وكيفية تقسيم البيانات.

تصميم المفتاح الأساسي

يحدد المفتاح الأساسي المحدد في جدول في Phoenix كيفية تخزين البيانات داخل مفتاح الصف لجدول HBase الأساسي. في HBase، الطريقة الوحيدة للوصول إلى صف معين هي استخدام مفتاح rowkey. بالإضافة إلى ذلك، يتم فرز البيانات المخزنة في جدول HBase بواسطة مفتاح الصف. يبني Phoenix قيمة مفتاح الصف عن طريق ربط قيم كل عمود من الأعمدة في الصف بالترتيب المحدد في المفتاح الأساسي.

على سبيل المثال، يحتوي جدول جهات الاتصال على الاسم الأول واسم العائلة ورقم الهاتف والعنوان، وكلها في نفس عائلة العمود. يمكنك تحديد مفتاح أساسي بناءً على رقم تسلسل متزايد:

rowKey العنوان الهاتف firstName الاسم الأخير
1000 1111 San Gabriel Dr. 1-425-000-0002 أشرف Dole
8396 5415 San Gabriel Dr. 1-230-555-0191 Calvin Raji

ومع ذلك، إذا كنت تقوم بالاستعلام عن طريق اسم العائلة بشكل متكرر، فقد لا يعمل هذا المفتاح الأساسي بشكل جيد، لأن كل استعلام يتطلب فحصاً كاملاً للجدول لقراءة قيمة كل اسم عائلة. بدلاً من ذلك، يمكنك تحديد مفتاح أساسي في أعمدة LastName والاسم الأول ورقم الضمان الاجتماعي. هذا العمود الأخير لإزالة الغموض عن اثنين من المقيمين في نفس العنوان ويحملان نفس الاسم، مثل الأب والابن.

rowKey العنوان الهاتف firstName الاسم الأخير socialSecurityNum
1000 1111 San Gabriel Dr. 1-425-000-0002 أشرف Dole 111
8396 5415 San Gabriel Dr. 1-230-555-0191 Calvin Raji 222

باستخدام هذا المفتاح الأساسي الجديد، ستكون مفاتيح الصف التي تم إنشاؤها بواسطة Phoenix:

rowKey العنوان الهاتف firstName الاسم الأخير socialSecurityNum
دول جون 111 1111 San Gabriel Dr. 1-425-000-0002 أشرف Dole 111
Raji-Calvin-222 5415 San Gabriel Dr. 1-230-555-0191 Calvin Raji 222

في الصف الأول من الجدول المحدد، يتم تمثيل بيانات مفتاح الصف كما هو موضح:

rowKey المفتاح قيمة
دول جون 111 العنوان 1111 San Gabriel Dr.
دول جون 111 الهاتف 1-425-000-0002
دول جون 111 firstName أشرف
دول جون 111 الاسم الأخير Dole
دول جون 111 socialSecurityNum 111

يخزن مفتاح الصف هذا الآن نسخة مكررة من البيانات. ضع في اعتبارك حجم وعدد الأعمدة التي تقوم بتضمينها في المفتاح الأساسي، لأن هذه القيمة مضمنة مع كل خلية في جدول HBase الأساسي.

أيضاً، إذا كان المفتاح الأساسي يحتوي على قيم تتزايد بشكل رتيب، فيجب عليك إنشاء الجدول باستخدام مجموعات الملح للمساعدة في تجنب إنشاء نقاط فعالة للكتابة- راجع بيانات التقسيم.

تصميم عائلة العمود

إذا تم الوصول إلى بعض الأعمدة بشكل متكرر أكثر من غيرها، فيجب عليك إنشاء مجموعات أعمدة متعددة لفصل الأعمدة التي يتم الوصول إليها بشكل متكرر عن الأعمدة التي نادراً ما يتم الوصول إليها.

أيضاً، إذا كان يتم الوصول إلى أعمدة معينة معاً، فضع هذه الأعمدة في نفس عائلة الأعمدة.

تصميم العمود

  • احتفظ بأعمدة VARCHAR أقل من 1 ميغابايت بسبب تكاليف الإدخال/ الإخراج للأعمدة الكبيرة. عند معالجة الاستعلامات، تقوم HBase بتجسيد الخلايا بالكامل قبل إرسالها إلى العميل، ويستقبلها العميل بالكامل قبل تسليمها إلى كود التطبيق.
  • قم بتخزين قيم العمود باستخدام تنسيق مضغوط مثل protobuf أو Avro أو msgpack أو BSON. JSON غير موصى به، لأنه أكبر.
  • ضع في اعتبارك ضغط البيانات قبل التخزين لتقليل وقت الاستجابة وتكاليف الإدخال/ الإخراج.

بيانات التقسيم

تتيح لك Phoenix التحكم في عدد المناطق التي يتم فيها توزيع بياناتك، ما قد يؤدي إلى زيادة أداء القراءة/ الكتابة بشكل ملحوظ. عند إنشاء جدول Phoenix، يمكنك استخدام بياناتك أو تقسيمها مسبقاً.

لتمليح طاولة أثناء الإنشاء، حدد عدد دلاء الملح:

CREATE TABLE CONTACTS (...) SALT_BUCKETS = 16

يؤدي هذا التمليح إلى تقسيم الجدول على طول قيم المفاتيح الأساسية، واختيار القيم تلقائياً.

للتحكم في مكان حدوث انقسامات الجدول، يمكنك تقسيم الجدول مسبقاً عن طريق توفير قيم النطاق التي يحدث الانقسام على طولها. على سبيل المثال، لإنشاء جدول مقسم على ثلاث مناطق:

CREATE TABLE CONTACTS (...) SPLIT ON ('CS','EU','NA')

تصميم الفهرس

فهرس العنقاء هو جدول HBase يخزن نسخة من بعض أو كل البيانات من الجدول المفهرس. يعمل الفهرس على تحسين أداء أنواع معينة من الاستعلامات.

عند تحديد فهارس متعددة ثم الاستعلام عن جدول، يقوم Phoenix تلقائياً بتحديد أفضل فهرس للاستعلام. يتم إنشاء الفهرس الأساسي تلقائياً بناءً على المفاتيح الأساسية التي تحددها.

للاستعلامات المتوقعة، يمكنك أيضاً إنشاء فهارس ثانوية بتحديد أعمدتها.

عند تصميم الفهارس الخاصة بك:

  • فقط قم بإنشاء الفهارس التي تحتاجها.
  • حدد عدد الفهارس في الجداول التي يتم تحديثها بشكل متكرر. تترجم التحديثات التي يتم إجراؤها على الجدول إلى عمليات كتابة في كل من الجدول الرئيسي وجداول الفهرس.

إنشاء فهارس ثانوية

يمكن للفهارس الثانوية تحسين أداء القراءة من خلال تحويل ما يمكن أن يكون فحصاً كاملاً للجدول إلى بحث عن نقطة، على حساب مساحة التخزين وسرعة الكتابة. يمكن إضافة الفهارس الثانوية أو إزالتها بعد إنشاء الجدول ولا تتطلب إجراء تغييرات على الاستعلامات الموجودة - تعمل الاستعلامات بشكل أسرع. بناءً على احتياجاتك، ضع في اعتبارك إنشاء فهارس مغطاة أو فهارس وظيفية أو كليهما.

استخدم الفهارس المغطاة

الفهارس المغطاة هي فهارس تتضمن بيانات من الصف بالإضافة إلى القيم المفهرسة. بعد العثور على إدخال الفهرس المطلوب، ليست هناك حاجة للوصول إلى الجدول الأساسي.

على سبيل المثال، في مثال جدول جهات الاتصال، يمكنك إنشاء فهرس ثانوي على عمود socialSecurityNum فقط. سيؤدي هذا الفهرس الثانوي إلى تسريع الاستعلامات التي تقوم بالتصفية حسب قيم socialSecurityNum، ولكن استرداد قيم الحقول الأخرى يتطلب قراءة أخرى مقابل الجدول الرئيسي.

rowKey العنوان الهاتف firstName الاسم الأخير socialSecurityNum
دول جون 111 1111 San Gabriel Dr. 1-425-000-0002 أشرف Dole 111
Raji-Calvin-222 5415 San Gabriel Dr. 1-230-555-0191 Calvin Raji 222

ومع ذلك، إذا كنت تريد عادةً البحث عن الاسم الأول والاسم الأخير وفقاً لـ socialSecurityNum، فيمكنك إنشاء فهرس مغطى يتضمن الاسم الأول والاسم الأخير كبيانات فعلية في جدول الفهرس:

CREATE INDEX ssn_idx ON CONTACTS (socialSecurityNum) INCLUDE(firstName, lastName);

يمكّن هذا الفهرس المغطى الاستعلام التالي من الحصول على جميع البيانات بمخزون القراءة من الجدول الذي يحتوي على الفهرس الثانوي:

SELECT socialSecurityNum, firstName, lastName FROM CONTACTS WHERE socialSecurityNum > 100;

استخدم الفهارس الوظيفية

تسمح لك الفهارس الوظيفية بإنشاء فهرس على تعبير عشوائي تتوقع استخدامه في الاستعلامات. بمخزون أن يكون لديك فهرس وظيفي في مكانه ويستخدم الاستعلام هذا التعبير، ويمكن استخدام الفهرس لاسترداد النتائج بدلاً من جدول البيانات.

على سبيل المثال، يمكنك إنشاء فهرس للسماح لك بإجراء عمليات بحث غير حساسة لحالة الأحرف على الاسم الأول واسم العائلة المدمجين لشخص ما:

CREATE INDEX FULLNAME_UPPER_IDX ON "Contacts" (UPPER("firstName"||' '||"lastName"));

تصميم الاستعلام

الاعتبارات الرئيسية في تصميم الاستعلام هي:

  • افهم خطة الاستعلام وتحقق من سلوكها المتوقع.
  • انضم بكفاءة.

فهم خطة الاستعلام

في SQLLine، استخدم EXPLAIN متبوعا باستعلام SQL لعرض خطة العمليات التي ينفذها Phoenix. تحقق من أن الخطة:

  • تستخدم مفتاحك الأساسي عندما يكون ذلك مناسباً.
  • تستخدم الفهارس الثانوية المناسبة، بدلاً من جدول البيانات.
  • تستخدم RANGE SCAN أو SKIP SCAN كلما أمكن ذلك، بدلاً من TABLE SCAN.

أمثلة الخطة

على سبيل المثال، لنفترض أن لديك جدولاً يسمى FLIGHTS يخزن معلومات تأخير الرحلة.

لتحديد جميع إصدارات التقييم مع airlineid من 19805، حيث airlineid يوجد حقل غير موجود في المفتاح الأساسي أو في أي فهرس:

select * from "FLIGHTS" where airlineid = '19805';

قم بتشغيل الأمر الموضح كما يلي:

explain select * from "FLIGHTS" where airlineid = '19805';

تبدو خطة الاستعلام كما يلي:

CLIENT 1-CHUNK PARALLEL 1-WAY ROUND ROBIN FULL SCAN OVER FLIGHTS
   SERVER FILTER BY AIRLINEID = '19805'

في هذه الخطة، لاحظ عبارة FULL SCAN عبر الرحلات الجوية. تشير هذه العبارة إلى أن التنفيذ يقوم به TABLE SCAN على جميع الصفوف في الجدول، بدلاً من استخدام خيار RANGE SCAN أو SKIP SCAN الأكثر كفاءة.

الآن، لنفترض أنك تريد الاستعلام عن الرحلات الجوية في 2 كانون الثاني (يناير) 2014 لشركة النقل AA حيث كان رقم الرحلة أكبر من 1. لنفترض أن العمود، والشهر، واليوم، والشهر، والناقل، ورقم الطيران موجودة في الجدول النموذجي، وجميعها جزء من المفتاح الأساسي المركب. سيبدو الاستعلام كما يلي:

select * from "FLIGHTS" where year = 2014 and month = 1 and dayofmonth = 2 and carrier = 'AA' and flightnum > 1;

دعنا نفحص خطة هذا الاستعلام باستخدام:

explain select * from "FLIGHTS" where year = 2014 and month = 1 and dayofmonth = 2 and carrier = 'AA' and flightnum > 1;

الخطة الناتجة هي:

CLIENT 1-CHUNK PARALLEL 1-WAY ROUND ROBIN RANGE SCAN OVER FLIGHTS [2014,1,2,'AA',2] - [2014,1,2,'AA',*]

القيم الموجودة بين قوسين مربعين هي نطاق القيم للمفاتيح الأساسية. في هذه الحالة، يتم إصلاح قيم النطاق مع عام 2014، والشهر الأول، واليوم من الشهر الثاني، ولكنها تسمح بقيم رقم الرحلة التي تبدأ بالرقم 2 وما فوق (*). تؤكد خطة الاستعلام هذه أنه يتم استخدام المفتاح الأساسي كما هو متوقع.

بعد ذلك، قم بإنشاء فهرس في جدول FLIGHTS المسمى carrier2_idx الموجود في حقل الناقل فقط. يتضمن flightdateهذا الفهرس أيضا و tailnumoriginو و flightnum كأعمدة مغطاة يتم تخزين بياناتها أيضا في الفهرس.

CREATE INDEX carrier2_idx ON FLIGHTS (carrier) INCLUDE(FLIGHTDATE,TAILNUM,ORIGIN,FLIGHTNUM);

لنفترض أنك تريد الحصول على الناقل مع flightdate و tailnum، كما في الاستعلام التالي:

explain select carrier,flightdate,tailnum from "FLIGHTS" where carrier = 'AA';

يجب أن ترى هذا الفهرس قيد الاستخدام:

CLIENT 1-CHUNK PARALLEL 1-WAY ROUND ROBIN RANGE SCAN OVER CARRIER2_IDX ['AA']

للحصول على قائمة كاملة بالعناصر التي يمكن أن تظهر في نتائج الخطة التوضيحية، راجع قسم شرح الخطط في دليل ضبط Apache Phoenix.

انضم بكفاءة

بشكل عام، تريد تجنب الصلات ما لم يكن جانب واحد صغيراً، خاصةً في الاستعلامات المتكررة.

إذا لزم الأمر، يمكنك إجراء صلات كبيرة باستخدام التلميح /*+ USE_SORT_MERGE_JOIN */، لكن الصلة الكبيرة تعتبر عملية مكلفة على عدد كبير من الصفوف. إذا كان الحجم الإجمالي لجميع الجداول الموجودة على الجانب الأيمن يتجاوز الذاكرة المتوفرة، فاستخدم التلميح /*+ NO_STAR_JOIN */.

السيناريوهات

تصف الإرشادات التالية بعض الأنماط الشائعة.

أحمال العمل الثقيلة للقراءة

لحالات الاستخدام الكثيفة القراءة، تأكد من استخدام الفهارس. بالإضافة إلى ذلك، لتوفير وقت القراءة الزائد، ضع في اعتبارك إنشاء فهارس مغطاة.

أعباء العمل الثقيلة للكتابة

بالنسبة لأحمال العمل التي تتضمن الكثير جداً من الكتابة حيث يتزايد المفتاح الأساسي بشكل رتيب، قم بإنشاء دلاء الملح للمساعدة في تجنب كتابة النقاط الفعالة، على حساب إجمالي معدل النقل القراءة بسبب عمليات الفحص الإضافية المطلوبة. أيضاً، عند استخدام UPSERT لكتابة عدد كبير من السجلات، قم بإيقاف تشغيل الالتزام التلقائي وتجميع السجلات.

حذف بالجملة

عند حذف مجموعة بيانات كبيرة، قم بتشغيل autoCommit قبل إصدار استعلام DELETE، بحيث لا يحتاج العميل إلى تذكر مفاتيح الصفوف لجميع الصفوف المحذوفة. يمنع AutoCommit العميل من التخزين المؤقت للصفوف المتأثرة بـ DELETE، بحيث يمكن لـ Phoenix حذفها مباشرة على خوادم المنطقة دون حساب إعادتها إلى العميل.

غير قابل للتغيير وإلحاق فقط

إذا كان السيناريو الخاص بك يفضل سرعة الكتابة على تكامل البيانات، ففكر في تعطيل سجل الكتابة المسبقة عند إنشاء الجداول:

CREATE TABLE CONTACTS (...) DISABLE_WAL=true;

للحصول على تفاصيل حول هذا والخيارات الأخرى، راجع Apache Phoenix Grammar.

الخطوات التالية