JSONB בפרויקטים של JAVA
מתכנתי Backend יכולים לאחסן נתונים בפורמט JSON כבר זמן רב, מאז PostgresSQL 9.2. לפתרון זה יתרונות רבים. למרבה הצער, יש לו גם מספר חסרונות.
מאז גרסה 9.4 הוצגה הייצוג הבינארי של JSON – JSONB – השומר נתונים לאט יותר מ-JSON, אך מאיץ את עיבודם ומאפשר יצירת אינדקסים. זה ידוע לכל שניתן ליצור אינדקסים על עמודת JSONB, אך הנושא מורכב למדי ואפשר בסופו של דבר לקבל שאילתות לא יעילות.
במאמר זה נתמקד ב-JSONB. אציג שיטות עבודה מומלצות לשימוש בעמודה מסוג זה ואדגיש את מגבלותיה.
דוגמה כיצד ליצור טבלה ולמלא אותה בנתונים
אני אוהב להמחיש את הנאמר, אז בואו ניצור טבלה עם עמודה מסוג JSONB עם קצת נתונים:
עמודה מסוג JSONB מכילה שדה טקסט (שם), רשימה (מפלים), ומפה (אטרקציות).
לאחר הרצת "select * מחופשות" נקבל:
יצירת מודל כזה היא פשוטה. אין צורך לציין סוגי שדות. הכול הומר ל-JSONB ללא כל בעיות. עמודת ה-ID נוספה מכיוון שהטבלה תמופה לישות ב-JPA שדורשת ID.
יישום 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
- כאשר אנו רוצים להטיל מגבלה על שדה: לא קל להפוך שדה לייחודי.
אופטימיזציה של שאילתות: מבט מעמיק על אינדוקס
B-Tree: זה עובד רק עם אופרטור השוויון (=). ניתן לבצע זאת עבור עמודת JSONB כולה או כפונקציה על שדה ספציפי.
Hash: בדומה ל-B-tree, הוא פועל רק עם אופרטור השוויון (=). ניתן להשתמש בו גם על שדה ב-JSONB. עם B-tree, שאילתות מהירות יותר מאשר hash, אך תופסות יותר מקום וההכנסות אורכות יותר זמן.
Gin:
- ל-Postgres יש מחלקות אופרטור מובנות עבור אינדקס GIN (https://www.postgresql.org/docs/current/gin-builtin-opclasses.html)
אינדקס 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.
A דומה query יכול be הושג באמצעות ה מפרט.
מפרטים
שימוש במפרטים לשאילתות אינו קריא, אך הוא מצוין לבניית שאילתות דינמיות. למרבה הצער, כתיבת שאילתה באמצעות פונקציה לא תהיה יעילה. אם ניצור אינדקס: BTREE ((data – >> ‘name’)), הוא יושמט בעת ביצוע השאילתה באמצעות מפרטים. אם ברצוננו להאיץ את השאילתה, נוכל להשתמש בפונקציה CREATE INDEX index func_hname_indx ON holidays (jsonb_extract_path_text (data, ‘name’));
@Query עם שאילתה מקורית
שאילתות מקוריות מאפשרות שימוש באופרטורים המיועדים לחיפושי JSONB, למשל data – >> ‘waterfalls’, ובכך שימוש באינדקסים.
בסעיף הבא אתאר רשימה של שאילתות עם מידע על ביצועים.
הדוגמאות ליצירת אינדקסים ושאילתות
* SEQ = חיפוש סדרתי על פני כל הטבלה
* סריקת אינדקס = חיפוש מהיר באמצעות האינדקס
הרשימה לעיל ממחישה את הגמישות של אינדקס Gin, אשר יכול לשמש במצבים רבים.
לאנשים שנמנעים משאילתות מקוריות בקוד, אך זקוקים לאינדקס, אני רוצה להראות דוגמה לאינדקס פונקציה שניתן להשתמש בו ולממש אותו עם @Query.
מיון התוצאות: סעיף ה-"order by" is רק נתמך על ידי אינדקס BTree
תקציר
JSONB שימושי מאוד במקרים רבים, במיוחד עבור נתונים לקריאה בלבד ונתונים שאינם דורשים שאילתות מתקדמות.
אם הפרויקט שלנו דורש שימוש ב-JSONB ושאילתות מורכבות יותר, תמיד נוכל ליצור אינדקס וליישם שאילתה מקורית.
עבור מסדי נתונים גדולים (למעלה ממיליון רשומות), יש להיות זהירים במיוחד ולשקול לשמור את הנתונים בעמודות המסורתיות שעבורן נשמרת הסטטיסטיקה ומשמשת לאופטימיזציה של שאילתות.
arrow_circle_rightצרו קשר
נעזור לכם לבחור את הטכנולוגיה המתאימה ביותר לפתרון שלכם
arrow_circle_right המאמרים שלנו






