"תסביר לי" – גרסת ה SQL
לקחת את הביצועים ברצינות, בעזרת MySQL Performance Schema
נושאים מתקדמים ב MySQL: חלק א' – עבודה עם JSON
נושאים מתקדמים ב MySQL: חלק ב' – json_each ו Generated Columns
MySQL הוא בסיס-נתונים פשוט.
הפשטות היא יתרון (קל לשלוט בו, הוא מוכר היטב ויש לו הרבה כלים, קשה למפתח בודד להסתבך ביכולות אזוטריות) – אבל גם חיסרון (יש כמה דברים שקשה עד קשה מאוד לעשות איתו).
גרסה 8 שיצאה לא מכבר הייתה עשויה להיקרא גרסה 5.8 – היא הוסיפה כמות נאה של תוספות, אבל בוודאי לא מהפיכה. (במיוחד לאחר שכמה שינויים זכו ל down-port לגרסה 5.7). לא ניתן להשוות אותה לחידושים של גרסה 5.
MySQL עדיין בסיס הנתונים הפופולרי ביותר בעולם אחרי Oracle המסחרי, ובפער גדול גם על PostgreSQL שזכה לצמיחה יפה בשנים האחרונות. MariaDB – ה fork של MySQL שמשוחרר מההשפעה של חברת אורקל, נמצא במקום 13 ברשימה למטה, ואפשר להחשיב אותו כעוד פלח-שוק של MySQL – וכנראה כמחליף העתידי.
אם אתם עובדים בסטארט-אפ – אזי יש סיכוי טוב ש MySQL נמצא בסט הכלים שלכם.
הרבה פעמים נתקלתי בטיעונים שעדיף להשתמש ב MongoDB או PostgreSQL על פני MySQL.
כשניסיתי לחקור מדוע, קיבלתי מגוון תשובות שלא שיכנעו אותי:
- "בסיסי-נתונים רלציונים הם העבר"
- "ל PostgresSQL יש יותר שיטות לבצע join – אז הביצועים שלו טובים יותר"
- "בכנס חשוב-כלשהו היו 3 הרצאות על PostgreSQL ורק אחת על MySQL"
- "ל MySQL אין רפליקציה טובה (כמו ל Mongo, לכאורה)". "הוא לא בנוי ל Scale".
- "ל Postgres יש פי 3 יכולות מאשר ל MySQL".
| דיאגרמה (לא בהכרח מאלה) של Databases בתחום הרלציוני. מקור: 451 Research. |
בכמה פוסטים הקרובים הייתי רוצה להתעמק קצת ביכולות של MySQL. יחסית לפופולריות השימוש שבו, MySQL לא "מסופר" בצורה טובה , כבר כמה שנים טובות. הרבה יכולות טובות נוספו בכמה שנים הללו – אך נראה שחלק נכבד מקהל השמתמשים לא מכיר אותן.
אמנם עכשיו עם השחרור של MySQL גרסה 8, הולכים ויוצאים ספרים חדשים – אך חלק גדול מהפוקוס שלהם הוא על מה שחדש בגרסה החדשה, ופחות במה שאפשר לעשות עם גרסה 5.7 – שהיא כנראה תישאר הגרסה הדומיננטית בשנתיים הקרובות (בסיס נתונים לא משדרגים כ"כ מהר – וטוב שכך).
הנה רשימת של נושאים שנראים לי מעניינים:
-
- עבודה עם JSON וה Document Store.
- Generated columns
- פיצוי על יכולות חסרות ב MySQL כמו json_each או fist_value/last_value – איך אפשר להסתדר בלעדיהם.
- מנועי Storage וההבדלים ביניהם: InnoDB ל MyISAM, וכו' (לא חדש בכלל – אך ידע חסר, ממה שנתקלתי).
- סטטיסטיקות של אינדקסים וטבלאות – ואיך זה משפיע עלינו (גם לא חדש).
- Full Text indexes
- Partitioning
- Large Hadron Migrator- https://github.com/soundcloud/lhm, ביצוע migrations גדולים ללא downtime.
אני מתחיל לכתוב, ואיני יודע היכן אסיים.
אם יש נקודות שנראות לכם חשובות במיוחד – אתם מוזמנים להגיב ולהשפיע.
JSON וה MySql Document Store
לפני כעשור, עולם ה Databases התחלק ל-2 קבוצות דומיננטיות של שימוש עיקרי:
- בסיסי-נתונים רלציוניים
- בסיסי-נתונים מבוססי-מסמכים (Document-Based)
ההרכב הנפוץ הוא שארגון מחזיק את רוב האובייקטים בבסיס נתונים רלציוני / מסמכים, והיכן שצריך scale גבוה יותר – פונה לפתרונות יותר ממוקדים לצורך הספציפי.
- "מסמכים" (כלומר: JSON), מאוחסנים ב Collections.
- מאחורי הקלעים, לאכסון collection של מסמכים, נוצרות טבלאות בנות שתי-עמודות id_ ו doc (כמו שהיינו עושים פעם, בעצמנו…)
- בעזרת API (או ה shell החדש, mysqlsh) ניתן לגשת ל"מסמכים" ב API המוכר מבסיסי-נתונים מבוססי-מסמכים. למשל:
- ("db.product.find('_id = "iPhone XI
- (…)db.createCollection
- (…)add(…), sort(…), modify, וכו'
- את המסמכים ניתן לאנקס
- מאחורי הקלעים MySQL יוצר generated columns – נושא שארצה לכסות בהרחבה.
- ל API יש clients זמינים ל JavaScript ול Python – אם כי כמה פעולות, תחזוקה וטיפול בעיקר, עדיין יש לעשות ב SQL.
עדכון: תודה לנדב נווה שעדכון אותי שכן יש plugin ל User Defined Functions ב js עבור MySQL. מעניין!
JSON ב MySQL דרך SQL
column מטיפוס JSON הוסף ב MySQL גרסה 5.7.8 (אוג' 2015), אם כי ניתן להשתמש ביכולות ה JSON שנוספו לבסיס הנתונים גם על גבי עמודות מסוג text, varchar, BLOB וכו'. עמודות טקסטואליות.
אם אתם רוצים לשמור בבסיס הנתונים ייצוג של אובייקט או מבנה היררכי מורכב – קידוד שלו ל JSON יהיה פשוט עשרות מונים מיצירת קבוצה של סכמות (טבלאות) רלציוניות שיכילו את אותו המידע ויתארו את ההיררכיה.
יתרון נוסף בשימוש ב"מסמך" JSON הוא האטומיות: הכל משתנה ביחד – הכל, או לא כלום.
בקבוצה של סכמות יהיה עליכם להתעסק עם טרנזקציות בכדי לקבל הגנה בפני 2 תהליכים שמשנים באופן לא-עקבי שני חלקים של האובייקט. טרנזקציות על עדכון של שורה של טבלאות (פעם ראיתי אובייקט שמופה ל 35 טבלאות שונות בבסיס הנתונים) – הוא לא דבר פשוט.
החיסרון הגדול של JSON הוא הקושי לבצע שאילתות הנוגעות לערכים המקוננים בתוך ה JSON והביצועים שנובעים מכך:
- יש ב MySQL תחביר המאפשר לאמוד אם שדה x בתוך אובייקט o שבתוך ה JSON כחלק מפקודת WHERE.
כמו שנראה בהמשך, כשמבנה ה json הוא מורכב יותר – זה הולך והופך להיות קשה יותר. - ביצועים: כאשר אנו רוצים להשוות שדה אחד מתוך אובייקט עם 50 שדות – עלינו לטעון לזיכרון את כל 50 השדות בכל פעם, שזה הרבה I/O מיותר (מדד חשוב מאוד בביצועים של בסיסי-נתונים).
הגישה המקובלת להתמודד עם בעיית הביצועים היא להוציא לעמודות מקבילות לעמודת ה JSON "שכפול" של שדות אותן נרצה לתשאל בצורה תדירה (ולכן גם לאנדקס).
בהמשך נראה כיצד MySQL יכול לסייע להפוך לעשות את התהליך הזה לפשוט יותר בעזרת Generated Columns.
- בהכנסת ערך לעמודה מסוג json – בסיס הנתונים יוודא את תקינות ה format של ה json.
- בעמודה מסוג json ולא text – בסיס הנתונים ידחוס את ה json לפורמט בינארי דחוס יותר, בו המפתחות ממוינים (בדומה לפורמט bson שנעשה בו שימוש ב MongoDB).
json תקין הוא כמובן אובייקט ({}), מערך ([]), מחרוזת (""), או null.
שווה לציין שמחרוזת ה json שהוכנסה לעמודה מסוג json לא תחזור כמחרוזת זהה: במעבר לפורמט בינארי ינקו את ה whitespace וסדר המפתחות ישתנה.
כמו כן, אם יש אובייקטים עם מפתחות "כפולים" – אזי המפתח הראשון הוא זה שישמר, עד לגרסה 8.0.3 ממנה המפתח האחרון הוא זה שישמר (מה שיותר עקבי עם רוב המימושים של javaScript).
כל עוד אנחנו עובדים עם json בצורה תקינה – זה פרט שלא נשים אליו לב.
טיפוס ה json של mySQL הוא optimized לקריאות, כך שאם אנחנו הולכים לכתוב יותר (למשל: audit) – יכול להיות שיהיה עדיף, מבחינת ביצועים, להשתמש בעמודה מסוג text.
הפקודה הבסיסית בעבודה עם json ב MySQL היא JSON_EXTRACT:
FROM some_table
WHERE JSON_EXTRACT(c, "$.id") > 1
ORDER BY JSON_EXTRACT(c, "$.name");
יש גם תחביר מקוצר:
FROM some_table
WHERE c->"$.id" > 1
ORDER BY c->'$.name';
כאשר <<- הוא תחליף ל ((JSON_UNQUOTE( JSON_EXTRACT(column, path. הפונקציה JSON_UNQUOTE מסירה את ה quotes – אם קיימים.
- את כל הביטוי יש לעטוף במירכאות בודדות או כפולות – כי זו מחרוזת ב SQL.
- יש לציין את ה $ – המתאר את ה root של ה json (לפי תקן ה json path – ה $ נקרא "context").
- כאשר יש שמות של keys המשתמשים בסימנים מסוימים – יש לעטוף אותם ב quotes, למשל:
'column->'$[2].person."my-pets"[1].name - ניתן להשתמש ב * בכמה מצבים:
- [*]$ – יחזיר את כל האיברים במערך (או null אם הפעלתם על אובייקט או מחרוזת)
- price.*.$ יחזיר מערך של כל שדות ה price בכל האובייקטים שבתוך העמודה.
- price.**.$ יחזיר מערך של כל שדות ה price בכל האובייקטים, או תתי-האובייקטים, שבתוך העמודה.
- יש פונקציות כגון ()JSON_KEYS ו ()JSON_SEARCH – שיחזירו בהתאמה את רשימת ה keys באובייקט, או רשימת האובייקטים המכילים ערכים מסוימים.
| יכולות ה JSON של PostgreSQL הן מתקדמות יותר משל MySQL – אך נראה ש PostgreSQL הוא פשוט פחות סטנדרטי. מקור/2016. |
- יצירה של אובייקטי json כחלק משאילתה, בעזרת הפונקציות ()JSON_ARRAY(), JSON_OBJECT ו ()JSON_MERGE_PRESERVE.
- שינוי של ה json מתוך SQL בעזרת הפונקציות:
JSON_APPEND(), JSON_ARRAY_APPEND() JSON_ARRAY_INSERT(), JSON_INSERT(), JSON_REMOVE(), JSON_REPLACE(), JSON_SET. - פונקציות עזר שימושיות הן:
()JSON_UNQUOTE(), JSON_QUOTE(), JSON_DEPTH(), JSON_PRETTY(), JSON_LENGTH(), JSON_TYPE ו ()JSON_VALID
הערה: יש פתרון לשליפת האיבר האחרון במערך ב MySQL 8 בצורת:
אני אראה גם "תרגיל" איך ניתן לעשות זאת גם בגרסה 5.7, וללא התמיכה של operator ה last.
סיכום
המידע שסיפקתי בפוסט זה לבדו על השימוש ב JSON – הוא מספיק לשימוש מעשי, אם כי עדיין אפשר לעשות דברים קצת יותר מתקדמים – אותם אציג בפוסט המשך (אני מקווה).שיהיה בהצלחה!

