تمكن مبرمجو الواجهة الخلفية من تخزين البيانات بصيغة JSON منذ فترة طويلة، منذ إصدار PostgresSQL 9.2. ولهذا الحل عدد من المزايا. ولسوء الحظ، له أيضًا عدة عيوب. 

منذ الإصدار 9.4، تم إدخال التمثيل الثنائي لـ JSON – وهو JSONB – الذي يحفظ البيانات بشكل أبطأ قليلًا من JSON، لكنه يسرّع معالجتها ويمنح إمكانية إنشاء الفهارس. من المعروف عمومًا أنه يمكنك إنشاء فهارس على عمود JSONB، لكن الموضوع معقد للغاية وقد ينتهي بك الأمر باستعلامات غير فعالة. 

في هذه المقالة، سنركز على JSONB. سأعرض الممارسات الجيدة لاستخدام هذا النوع من الأعمدة وأؤكد على قيوده. 

مثال على كيفية إنشاء جدول وتعبئته بالبيانات 

أحب تصور ما يُقال، لذا دعنا ننشئ جدولاً بعمود من نوع JSONB مع بعض البيانات: 

JSONB in JAVA projects Pic1

يحتوي عمود من نوع JSONB على حقل نصي (اسم)، قائمة (الشلالات)، وخريطة (معالم الجذب). 

بعد تنفيذ "اختيار * من العطلات» سنحصل على: 

JSONB in JAVA projects Pic2

إنشاء مثل هذا النموذج أمر بسيط. لا حاجة لتحديد أنواع الحقول. تم تحويل كل شيء إلى JSONB دون أي مشاكل. أُضيف عمود المعرّف لأن الجدول سيُربط بكيان في JPA يتطلب معرّفًا. 

تطبيق JSONB 

متى تستخدم JSONB: 

  • بنية بيانات غير متوقعة: يجب علينا الاحتفاظ بالبيانات في قاعدة البيانات، لكن ليس واضحًا ما البيانات التي سنحصل عليها من مورد خارجي. أو نسمح للعميل بتعريف حقوله الخاصة. 
  • الكثير من السمات (الحقول) التي نادراً ما تُستخدم، مثل البيانات التي تُحفظ في قاعدة البيانات فقط للعرض. أو نحتفظ بها فقط لأنه قد تكون مفيدة يوماً ما. 
  • العلاقة بعدة كائنات: عندما لا نرغب في تطبيق انضمام بند إلى جدول بعلاقات لأسباب تتعلق بالأداء. يمكننا بسهولة حفظ قائمة العلاقات في JSONB. 

متى لا تستخدم JSONB: 

  • استعلامات غير فعّالة: يستخدم Postgres في الاستعلامات إحصاءات غير محفوظة لـ JSONB. يخمّن كيفية تنفيذ الاستعلام وأحيانًا يخمّن بشكل خاطئ. بفضل الفهارس، يمكنك تسريع الاستعلام، لكنني سأكتب المزيد عن ذلك لاحقًا في المقال. 
  • مساحة قرص محدودة: يتم الاحتفاظ بـ KEY وVALUE في JSONB. إذا كان لديك مليون سجل وتحتفظ بـ KEY/VALUE لكل من هذه السجلات، فسيتم تكرار KEY مليون مرة. أقترح عليك قراءة المقال الذي يصف كيف تم تقليل مساحة القرص بنسبة 30% بعد إزالة 45 مفتاحاً إلى الأعمدة – https://heap.io/blog/when-to-avoid-jsonb-in-a-postgresql-schema 
  • عندما نريد فرض قيد على حقل ما: ليس من السهل جعل الحقل فريدًا. 

تحسين الاستعلامات: نظرة أقرب على الفهرسة 

JSONB in JAVA projects tab1 new

B-Tree: يعمل فقط مع عامل المساواة (=). يمكن تنفيذه على عمود JSONB بأكمله أو كدالة على حقل محدد.  

تجزئة: على غرار شجرة B، فإنه يعمل فقط مع عامل المساواة (=). ويمكن أيضًا استخدامه على حقل في JSONB. مع شجرة B، تكون الاستعلامات أسرع من التجزئة (hash)، لكنها تشغل مساحة أكبر وتستغرق عمليات الإدراج وقتًا أطول.  

Gin:   

يدعم فهرس GIN مع فئة المعامل JSONB_OPS (الافتراضية) معاملات الوجود (?,? &,? |) والاحتواء (@>). وهو يعمل فقط على KEY. ويمكن تنفيذه على عمود أو حقل. عليك الحذر من مساحة القرص، لأنه يستهلك مساحة كبيرة. وهو أقل فعالية من Hash وB-Tree، لكنه أكثر مرونة. 

  • Gin مع فئة المشغل JSONB_PATH_OPS يدعم فقط @> ولكنه يشغل مساحة أقل، كما أن SELECT وUPDATE وDELETE ووقت البناء أسرع من JSONB_OPS. 
  • يدعم Gin استخدام GIN_TRGM_OPS كفهرس وحيد عامل التشغيل like. لهذا، تحتاج إلى تثبيت امتداد PG_TRGM. وهو يعمل فقط للقيم، وليس للمفاتيح. 

كيف تبدو استعلامات Java؟ 

في مشاريعي، أستخدم طرقًا متنوعة لتنفيذ الاستعلامات باستخدام Spring Data JPA. لمحاولة استخدام الفهارس المُنشأة على جدول JSONB، سنركز على 3 خيارات: 

  • تعريفQuery@
  • المواصفات
  • @Query declaration with native query

تعريفQuery@ 

لا يدعم JPA مشغلات JSONB (- >>? @> إلخ). على سبيل المثال، يمكن استبدال المشغل – >> بالدالة JSONB_EXTRACT_PATH_TEXT. 

JSONB in JAVA projects code1

A مشابه استعلام يمكن be تم الحصول عليها باستخدام الـ المواصفات. 

المواصفات 

JSONB in JAVA projects code2

استخدام المواصفات للاستعلامات ليس قابلاً للقراءة، لكنه رائع لبناء استعلامات ديناميكية. لسوء الحظ، لن تكون كتابة استعلام باستخدام دالة فعالة. إذا أنشأنا فهرساً: BTREE ((data – >> ‘name’))، فسيتم تجاهله عند تنفيذ الاستعلام باستخدام المواصفات. إذا أردنا تسريع الاستعلام، يمكننا استخدام دالة CREATE INDEX index func_hname_indx ON holidays (jsonb_extract_path_text (data, ‘name’))؛ 

@Query باستخدام استعلام أصلي 

JSONB in JAVA projects code 3

تتيح الاستعلامات الأصلية إمكانية استخدام المعاملات المخصصة لعمليات البحث JSONB، على سبيل المثال data – >> ‘waterfalls’ وبالتالي استخدام الفهارس. 

في القسم التالي، سأصف قائمة من الاستعلامات مع معلومات الأداء. 

أمثلة على إنشاء الفهارس والاستعلامات 

* SEQ = البحث التسلسلي عبر الجدول بأكمله 

* فحص الفهرس = بحث سريع باستخدام الفهرس

JSONB in JAVA projects tab2 new

تُظهر القائمة أعلاه مرونة فهرس Gin، الذي يمكن استخدامه في العديد من الحالات. 

لمن يتجنبون الاستعلامات الأصلية في الكود، لكنهم يحتاجون إلى فهرس، أريد أن أعرض مثالاً على فهرس دالة يمكن استخدامه وتنفيذه مع @Query. 

JSONB in JAVA projects tab 3

ترتيب النتائج: جملة "order by" is فقط مدعوم بفهرس BTree 

JSONB in JAVA projects code 4

الملخص 

JSONB مفيد جدًا في العديد من الحالات، خاصةً للبيانات للقراءة فقط والبيانات التي لا تتطلب استعلامات متقدمة. 

إذا تطلب مشروعنا استخدام JSONB واستعلامات أكثر تعقيدًا، يمكننا دائمًا إنشاء فهرس وتنفيذ استعلام أصلي. 

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