לפני כחודשיים, בערך, התרחש מהפך קטן-גדול.
לא, אני לא מדבר על מה שקרה (וקורה, בעת כתיבת הפוסט) באוקראינה. גם לא על נתח השוק שמאבדת מייקרוסופט בהתמדה, או על החדירה של אינטל ל high end האמיתי של החומרה. דווקא החברה שמפנה במקרה זה את מקומה היא לא אחרת מאשר הענק הכחול - IBM, והיא עשויה לא-להיות האחרונה.
בסיס נתונים לא-רלציוני, בשם MongoDB תפס את המקום החמישי הנחשק במדד הפופולריות של DB-Engine, ובעצם הפך לבסיס הנתונים הלא-רלציוני הראשון שנכנס לחמישייה הפותחת [א].
יש היגיון מסוים בכך שדווקא MongoDB הוא הראשון מבין בסיסי הנתונים מסוג NoSQL לפרוץ לרשימת בסיסי הנתונים הרלציוניים: מבחינות מסוימות הוא קצת יותר דומה להם: יש לו (יחסית ל NoSQL DB) עושר רב של יכולות, וותק, וזמינות של תמיכה מסודרת (לאחרונה גם בישראל) - הכוללת "גרסאות Enterprise".
מקור השם של מונגו הוא מהמילה humongous (גדול, אדיר), אולם היכולת לטפל בכמויות גדולות של Data היא כנראה לא התכונה הבולטת, המושכת אליו כ"כ הרבה מפתחים. בעולם ה Internet Scale דווקא אני שומע קולות שמחשיבים את Mongo כבסיס נתונים בעל יכולות Scale מתונות.
הפופולריות של MongoDB (בקיצור: "מונגו") נובעת מהקלות שהוא מספק למפתחים בעזרת הסכֶמה (db schema) הגמישה שלו. לא עוד Migrations, או יותר גרוע: תקלות migration על סביבת production (המוכרות כנראה היטב לכל מי שעבד עם SQL בסביבת ה Enterprise).
מונגו הופך להיות מועמד לקחת את מקומו של MySQL כבסיס נתונים זול ויעיל ל"משימות רגילות" - ויש הרבה מאוד כאלו.
הבהרה חשובה: מונגו הוא לא תחליף ישיר ל RDBMS. הוא שונה. כאשר זקוקים להרבה joins על נתונים, ופעולות רוחביות ("SELECT") על הרבה אובייקטים - מונגו (או Document-Based Database אחר) כנראה לא יתאים. מונגו מתאים כאשר יש אובייקטים עצמאיים ("לקוח", "אתר", "משלוח"), ורוב הפעולות נעשות על האובייקטים הבודדים - ורק מיעוט הפעולות הן רוחביות ("כל המשלוחים שיצאו אתמול ויגיעו מחר").
בפוסט זה אינני מתכוון לגעת בנושאים של Scalability או Availability של מונגו או בכלל ,לא הקדמה כללית ל NoSQL (נתתי כזו פעם, היא עדיין רלוונטית דייה) ולא דיון על ה CAP theorem.
בפוסט זה אני רוצה לגעת רק בפן הפונקציונלי של מונגו, כזה שעוזר לפתח (ולתחזק) אפליקציה יותר מהר. לספק תחושה כללית כיצד זה לעבוד עם MongoDB.
מונגו DB - מי זה הבחור הזה?
ראשית הייתי רוצה להזכיר בכמה מילים את מודל ה Document-Based Database בכלל, ואת זה של מונגו בפרט.
אם נשווה את המודל של Mongo לבסיס נתונים רלציוני ההשוואה תראה בערך כך:
מונגו מנהל מספר בסיסי נתונים, ממש כמו RDBMS. יש אוספים של "מסמכים" (ניתן לחשוב עליהם בשלב זה כמחרוזות של JSON). בתוך ה JSON יש ערכי key:value (= שדות) שמונגו מודע להם (בניגוד לבסיס נתונים K/V - שם הוא לא).
ניתן ליישם הדמייה ל KVDB (בסיסי נתונים מסוג K/V) על גבי RDBMS בכך שמייצרים טבלה עם 2 עמודות: ID ו BLOB של נתונים (למשל: קובץ JSON).
באופן עקרוני, בסיס הנתונים הרלציוני יספק את היכולת הבסיסית של KVDB, אך ללא הביצועים / Scalability / זמינות שניתן לקבל מבסיס נתונים KVDB. הוא עתיד "להתפרק" אחרי מאות מיליונים ספורים של רשומות והקצב בו הוא ישרת את לקוחותיו לא הולך להיות מרשים במיוחד.
בסיס נתונים מבוסס מסמכים (בקיצור DBDB) הוא כמו[ב] KVDB עם 2 הבדלים משמעותיים נפוצים:
דייט ראשון - התקנה וכלים ראשוניים
ההתקנה של מונגו היא פשוטה למדי:
בין קובצי ה exe ניתן למצוא כמה מעניינים:
בגדול, כדי להפעיל ולנסות את mongo בצורה קלה יש פשוט להפעיל את mongod.exe ואחריו את mogno.exe. דרך ה console ניתן לבצע / לנסות את רוב הפעולות המשמעותיות של בסיס הנתונים - ולקבל פידבק מהיר.
בואו ננסה משהו:
סביבת ה shell, כדרך אגב, היא javaScript לכל דבר - וניתן להשתמש ביכולות ה JavaScript שאתם מכירים (var, פונקציות ועוד).
בלינק זה תוכלו למצוא עוד פקודות רבות שניתן להפעיל ב shell. בעיקרון ה shell הוא כלי מצוין ללמידה, ניסוי, או תפעול של mongoDB.
כפי ששמתם לב מונגו הוסיף לנו שדה בתוך המסמך בשם id_. הוא יוסיף שדה זה אם לא הגדרנו אותו בעצמנו (בד"כ נשאיר זאת לו).
תכונה מעניינת של ה id_ (נקרא גם object id) היא שה timestamp הוא ראשון, מה שמאפשר לבצע מיון של אובייקטים ע"פ זמן היצירה, בקירוב. הזמן נשמר ע"פ epoch[ה] של unix.
בבסיסי נתונים מסוג KVDB ו DBDB יש חשיבות לא-מעטה לדרך בה בונים את ה ids/keys:
לא, אני לא מדבר על מה שקרה (וקורה, בעת כתיבת הפוסט) באוקראינה. גם לא על נתח השוק שמאבדת מייקרוסופט בהתמדה, או על החדירה של אינטל ל high end האמיתי של החומרה. דווקא החברה שמפנה במקרה זה את מקומה היא לא אחרת מאשר הענק הכחול - IBM, והיא עשויה לא-להיות האחרונה.
בסיס נתונים לא-רלציוני, בשם MongoDB תפס את המקום החמישי הנחשק במדד הפופולריות של DB-Engine, ובעצם הפך לבסיס הנתונים הלא-רלציוני הראשון שנכנס לחמישייה הפותחת [א].
יש היגיון מסוים בכך שדווקא MongoDB הוא הראשון מבין בסיסי הנתונים מסוג NoSQL לפרוץ לרשימת בסיסי הנתונים הרלציוניים: מבחינות מסוימות הוא קצת יותר דומה להם: יש לו (יחסית ל NoSQL DB) עושר רב של יכולות, וותק, וזמינות של תמיכה מסודרת (לאחרונה גם בישראל) - הכוללת "גרסאות Enterprise".
האופן בו אנשי מונגו אוהבים להציג את בסיס הנתונים: מעט פחות יכולות והרבה יותר Scalability מ RDBMS + הרבה-הרבה יותר יכולות מבסיסי נתונים NoSQL אחרים... |
מקור השם של מונגו הוא מהמילה humongous (גדול, אדיר), אולם היכולת לטפל בכמויות גדולות של Data היא כנראה לא התכונה הבולטת, המושכת אליו כ"כ הרבה מפתחים. בעולם ה Internet Scale דווקא אני שומע קולות שמחשיבים את Mongo כבסיס נתונים בעל יכולות Scale מתונות.
הפופולריות של MongoDB (בקיצור: "מונגו") נובעת מהקלות שהוא מספק למפתחים בעזרת הסכֶמה (db schema) הגמישה שלו. לא עוד Migrations, או יותר גרוע: תקלות migration על סביבת production (המוכרות כנראה היטב לכל מי שעבד עם SQL בסביבת ה Enterprise).
מונגו הופך להיות מועמד לקחת את מקומו של MySQL כבסיס נתונים זול ויעיל ל"משימות רגילות" - ויש הרבה מאוד כאלו.
הבהרה חשובה: מונגו הוא לא תחליף ישיר ל RDBMS. הוא שונה. כאשר זקוקים להרבה joins על נתונים, ופעולות רוחביות ("SELECT") על הרבה אובייקטים - מונגו (או Document-Based Database אחר) כנראה לא יתאים. מונגו מתאים כאשר יש אובייקטים עצמאיים ("לקוח", "אתר", "משלוח"), ורוב הפעולות נעשות על האובייקטים הבודדים - ורק מיעוט הפעולות הן רוחביות ("כל המשלוחים שיצאו אתמול ויגיעו מחר").
סקר המראה מה מושך מפתחים ב NoSQL: לא, זהו לא ה Scale במקום הראשון - אלא דווקא הגמישות. מקור |
בפוסט זה אינני מתכוון לגעת בנושאים של Scalability או Availability של מונגו או בכלל ,לא הקדמה כללית ל NoSQL (נתתי כזו פעם, היא עדיין רלוונטית דייה) ולא דיון על ה CAP theorem.
בפוסט זה אני רוצה לגעת רק בפן הפונקציונלי של מונגו, כזה שעוזר לפתח (ולתחזק) אפליקציה יותר מהר. לספק תחושה כללית כיצד זה לעבוד עם MongoDB.
... עוד קיטלוג של בסיסי נתונים NoSql |
מונגו DB - מי זה הבחור הזה?
ראשית הייתי רוצה להזכיר בכמה מילים את מודל ה Document-Based Database בכלל, ואת זה של מונגו בפרט.
אם נשווה את המודל של Mongo לבסיס נתונים רלציוני ההשוואה תראה בערך כך:
מונגו מנהל מספר בסיסי נתונים, ממש כמו RDBMS. יש אוספים של "מסמכים" (ניתן לחשוב עליהם בשלב זה כמחרוזות של JSON). בתוך ה JSON יש ערכי key:value (= שדות) שמונגו מודע להם (בניגוד לבסיס נתונים K/V - שם הוא לא).
ניתן ליישם הדמייה ל KVDB (בסיסי נתונים מסוג K/V) על גבי RDBMS בכך שמייצרים טבלה עם 2 עמודות: ID ו BLOB של נתונים (למשל: קובץ JSON).
באופן עקרוני, בסיס הנתונים הרלציוני יספק את היכולת הבסיסית של KVDB, אך ללא הביצועים / Scalability / זמינות שניתן לקבל מבסיס נתונים KVDB. הוא עתיד "להתפרק" אחרי מאות מיליונים ספורים של רשומות והקצב בו הוא ישרת את לקוחותיו לא הולך להיות מרשים במיוחד.
בסיס נתונים מבוסס מסמכים (בקיצור DBDB) הוא כמו[ב] KVDB עם 2 הבדלים משמעותיים נפוצים:
- הוא מכיר את תוכן המסמך, והפורמט שלו - והוא מאנדקס חלקים ממנו, לצורך Queries רוחביים יעילים ו/או מתוחכמים.
- לעתים קרובות: הוא מאפשר הגדרת קשרים (למשל: היררכי) בין המסמכים.
אפשר גם "לקמבן" התנהגות כזו בעזרת RDBMS בעזרת כמה stored procedures, מה שעשוי ליצור יישום יעיל אפילו פחות מ"חיקוי ה KVDB" שתארתי למעלה.
דייט ראשון - התקנה וכלים ראשוניים
ההתקנה של מונגו היא פשוטה למדי:
- פתיחת ZIP הכולל את קבצי ה exe. לאיזו תיקיה (ב"חלונות").
- יצירת תיקיה ריקה בשם data/db/ (יחסית לכונן בו מונגו מותקן) - התיקייה בה מונגו מאכסן את ה data שלו. ניתן לקנפג תיקיה אחרת, כמובן.
בין קובצי ה exe ניתן למצוא כמה מעניינים:
- mongod.exe - השדון החרוץ (daemon [ג]) של בסיס הנתונים. כלומר: התהליך המרכזי.
- mongo.exe - ה Admin Shell, דרכו ניתן לבצע פעולות רבות.
- mongoexport.exe - פעולות import/export של קבצי נתונים (BSON).
- mongodump.exe/mongorestore.exe - גיבוי / אחזור של בסיס הנתונים (כקובץ בינארי דחוס).
- mongostat.exe - תהליך שאוסף נתונים על השימוש ב mongo.
בגדול, כדי להפעיל ולנסות את mongo בצורה קלה יש פשוט להפעיל את mongod.exe ואחריו את mogno.exe. דרך ה console ניתן לבצע / לנסות את רוב הפעולות המשמעותיות של בסיס הנתונים - ולקבל פידבק מהיר.
- ביקשנו לראות אלו בסיסי נתונים קיימים בעזרת פקודת show dbs. בסיס הנתונים היחידי הקיים הוא local, למרות שמונגו חיבר אותנו בכניסה ל test - בסיס נתונים ריק. מונגו הוא לעתים רבות עצלן, וייצור אובייקטים מבנים רק כאשר יש בהם תוכן ממשי.
- השתמשנו ב use בכדי לעבור לבסיס נתונים חדש (שעדיין לא קיים). מונגו יזכור אותו, אך עדיין לא ייצור אותו.
- פקודת db בודקת באיזה בסיס נתונים אנו כעת.
- נתחיל בהכנסות: נכניס לתוך אוסף שעדיין לא קיים (people), בבסיס הנתונים שעדיין לא קיים ("db" הוא האובייקט המייצג את בסיס הנתונים הנוכחי) רשומה - כלומר מסמך, המתאר את ג'ון.
ברגע זה מונגו ייצור את בסיס הנתונים ואת האוסף (collection) בשם people - ויכניס לתוכו את הרשומה.
שימו לב שמונגו יקצה קובץ בשם data/db/myNewDb.0 בו הוא יאכסן את בסיס הנתונים. למרות שיש לי רק מסמך אחד קטן - מונגו בחר להקצות אצלי כ 200MB בדיסק - מה שעשוי להראות קצת מבהיל. הסברים - בהמשך הפוסט.
לאחר שלמונגו יש קובץ המייצג את בסיס הנתונים - הוא יכניס במהירות את המסמך השני - לו סכמה דומה אך רחבה יותר. - בשלב זה אני בודק אלו אוספים יש בבסיס הנתונים: מכיוון שבסיס הנתונים נוצר - האוספים כבר קיימים (people ואוסף האינדקסים - שמכיל רשומות ראשוניות בלבד). בדומה ל RDBMS, מונגו משתמש בעצמו כדי לנהל מידע מערכת.
- אני אבצע שאילתה (מקבילה ל * SELECT) על אוסף האנשים.
הממ... אני לא זוכר שהכנסתי שדה בשם id_, אתם זוכרים?!
סביבת ה shell, כדרך אגב, היא javaScript לכל דבר - וניתן להשתמש ביכולות ה JavaScript שאתם מכירים (var, פונקציות ועוד).
בלינק זה תוכלו למצוא עוד פקודות רבות שניתן להפעיל ב shell. בעיקרון ה shell הוא כלי מצוין ללמידה, ניסוי, או תפעול של mongoDB.
שדה ה id_
כפי ששמתם לב מונגו הוסיף לנו שדה בתוך המסמך בשם id_. הוא יוסיף שדה זה אם לא הגדרנו אותו בעצמנו (בד"כ נשאיר זאת לו).
מבנה ה id_ |
תכונה מעניינת של ה id_ (נקרא גם object id) היא שה timestamp הוא ראשון, מה שמאפשר לבצע מיון של אובייקטים ע"פ זמן היצירה, בקירוב. הזמן נשמר ע"פ epoch[ה] של unix.
בבסיסי נתונים מסוג KVDB ו DBDB יש חשיבות לא-מעטה לדרך בה בונים את ה ids/keys:
- ה key הוא לעתים קרובות המפתח ל partitioning, ויכול להשפיע רבות על הביצועים.
- יעילות של ה hash function יכול להיות משמעותי כאשר מדברים על המון פעולות בשנייה.
- מה קורה כאשר שני ids מתמפים לאותו אובייקט? זה לא סביר כאשר יש אלפי ערכים - אך סביר יותר, כאשר יש מיליארדים.
חיפוש ואינדקסים
במונגו, ניתן לחפש אחר ערכים בתוך collection בעזרת פקודת find, למשל:
db.persons.find({ lastname: 'Smith' });
החיפוש נעשה ע"י "דוגמה" או prototype: אנו מספקים את הערכים שאנו מחפשים ומונגו יחזיר לנו cursor המצביע על האובייקטים שמכילים את הערכים הללו.
ניתן לעשות חיפוש קצת יותר כללי, בעזרת query modifiers המספקים יכולות מספריות / לוגיות, למשל:
db.persons.find( { childrenCount: { $gt: 3 } } );
gt$ הוא קיצור של greater than, כלומר: אנשים בעלי 3 ילדים או יותר. שדה שלא הוגדר באובייקט (כלומר: מסמך) מסוים, יהיה בעל ערך 0 - לצורך העניין. יש גם query modifiers נוספים כגון min$ או or$ ועוד.
אפשר לחפש על אובייקטים מקוננים, למשל address.city או להגדיר שאילותות מורכבות הכוללת מספר רב של שדות או query modifiers לדוגמה:
db.persons.find( { childrenCount: { $gt: 3, $lt 20 } } , { 'address.city': 'Holon' } );
הערה: הסיבה ששמתי את address.city בתוך מרכאות נובעת מג'אווהסקריפט. שימוש בנקודה איננו תקני במפתח של אובייקט.
אפשר גם לצמצם את התשובה לשדות מסוים (בדומה ל RDBMS select) בעזרת אובייקט projection - אותו מגדירים בפרמטר השני של הפקודה find:
אפשר גם לצמצם את התשובה לשדות מסוים (בדומה ל RDBMS select) בעזרת אובייקט projection - אותו מגדירים בפרמטר השני של הפקודה find:
db.persons.find({ lastname: 'Smith' }, { tile:1, lastname: 1 });
בדוגמה זו אנו רוצים לקבל רק "תואר" ושם משפחה. ערכי ה "1" הם דרך מקוצרת לכתוב true - כלומר: החזר לי את השדה הזה. שימו לב: השדות צריכים להיות כולם true או כולם false - לא ניתן לערבב. יוצא הדופן היחיד הוא השדה id_, אותו ניתן להשמיט גם כאשר יש רשימה "פוזיטיבית" של שדות בהם מעוניינים.
מה עוד ניתן לעשות ב queries? ניתן לעשות הרבה. אציין שליפת מספר קבוע-מראש של ערכים מתוך מערך במסמך (נאמר תגובות בבלוג: רק 10 תגובות ראשונות מכל פוסט), או את היכולת לעשות שאילתות קיבוץ מורכבות (db.collection.group) - ולהוסיף להן פונקציות פילטור בג'אווהסקריפט, ad-hoc.
התיעוד של מונגו על חיפוש הוא מצויין - ואין טעם שאשכפל אותו, מעבר להצגת היכולות העקרוניות.
בעולם ה NoSQL עושים הפרדה בין "planned Queries" ו "Ad-hoc Queries".
Planned Queries הן כאלו שהתכוננו אליהן, לרוב בעזרת יצירת אינדקסים (פנימיים או חיצוניים לבסיס הנתונים), בעוד Ad-hoc Queries הן כאלו שעושים בצורה "ספונטנית" - ללא קיום של אינדקסים, pre-fetch או כל הכנה מקדימה.
בעוד ב RDBMS אינדקסים משפרים את הביצועים, במונגו הם כמעט-הכרחיים בכדי לבצע שאילתה על collection גדול - ולסיים בזמן סביר. ההמלצה הכללית במונגו היא לא לבצע שאילתה על collection לא-קטן (עשרות אלפי אובייקטים או יותר) - מבלי שיש עליו אינדקס, פשוט לא.
אינדקסים
הגדרה של אינדקסים היא פשוטה למדי, דומה להגדרת החיפוש:
db.persons.ensureIndex( { firstname: 1 } );
ייצור אינדקס לשדה ה firstname.
db.person.ensureIndex( { "address.city" : 1 } );
ייצור אינדקס לשדה city בתוך תת-האובייקט (או המסמך) address. כמו כן:
db.person.ensureIndex( { lastname: 1, firstname: 1} );
ייצור אינדקס של מפתח המורכב מ2 שדות: שם פרטי, ושם משפחה.
התחביר ensureIndex מצביע על כך שאם אינדקס קיים - מונגו לא ישכפל אותו.
מבני הנתונים המשמשים את מונגו. מקור |
אכסון (Persistency)
שאלה שבוודאי מעניינת מאוד את מי שמגיע מרקע והבנה כיצד עובד RDBMS היא "מה קורה שם בתוך מונגו?" או "כיצד ממימשו זאת?".
בחלק זה אנסה לענות על שאלה זו בקיצור.
מונגו כתוב ב ++C וש לו הפצות למערכות הפעלה שונות. מערכת ההפעלה אליה הוא תוכנן במקור היא Unix/Linux (כלומר: מערכת POSIX). יוצרי מונגו לקחו כמה החלטות Design מהותיות:
- מונגו יעבוד קודם כל עם זיכרון (עבור המהירות). מונגו אוהב זיכרון, והרבה!
כל בסיס נתונים שמכבד את עצמו מרוויח מזיכרון, אך כשלמונגו חסר זיכרון - נפילת הביצועים היא משמעותית מאוד. - עדכון של הזיכרון לדיסק יינתן (delegated) למערכת ההפעלה. התכנון המקורי התבסס על הדרך בה מערכת Linux עובדת, וספציפית פקודת mmap למיפוי קבצים - ישירות לתוך הזיכרון. המימוש עבור מערכת ההפעלה "חלונות" הוא כנראה דומה (במסגרת הכלים ש"חלונות" מספקת), אך ידוע כפחות יעיל.
היתרונות של גישה זו היא קלות פיתוח שמונגו הרוויח, ואי שכפול caches בין מערכת ההפעלה למונגו (מה שמנצל את הזיכרון בצורה יעילה יותר). החיסרון: המנגנון מאחורי mmap הוא כללי ואינו האופטימלי-ביותר לצרכים של מונגו. - למונגו יש רמות "durability" שונות הניתנות לקנפוג. האם כתיבה לבסיס הנתונים תכנס לתור של כתיבה לדיסק ("fire and forget" - דומה ל INSERT_DELAYED של MySQL) או האם כל כתיבה נכתבת במקום ל journal על הדיסק ("fully safe"). ברירת המחדל היא "fire and forget".
מונגו מאכסן כל Database בסדרת קבצים. הקבצים מתחילים בקובץ בגודל 64MB (למשל db.0) ומכפילים את עצמם בכל פעם (128MB - עבור db.1 ואז 256MB עבור db.2) וכו' עד שמגיעים לגודל של 2GB - ונשארים שם. מונגו מקצה יותר מקום ממה שנדרש למידע בפועל - כדי לא "להתקע" באמצע רצף של כתיבות. במערכת ההפעלה שלי הוא מקצה 200MB לכל בסיס נתונים שרק נוצר.
את הקבצים עצמם מונגו מחלק ל Extents - מן בלוקים הכוללים data או אינדקסים של collection מסוים (לא מערבבים סוגים ולא מערבבים collections).
גם בתוך extent של data, מונגו שומר לכל document מעט Padding - כלומר: יותר מקום ממה שנדרש, במידה והמסמך יגדל במעט. לדוגמה: עדכון של שדה לערך ארוך יותר או הוספה של שדה. המסמכים וה extents הם רציפים בדיסק / זכרון.
db.collection.dataSize();
יספק לנו מידע מה גודל ה collection בבתים, עבור המסמכים וה paddings שלהם.
db.collection.storageSize();
יספק לנו את גודל ה data extents של ה collection. כלומר ()dataSize ועוד מקום שהוקצה בדיסק ועדיין לא בשימוש.
db.collection.totalIndexSize();
סטטיסטיקות נוסף ניתן לקבל בעזרת אובייקט ה dbStat.
מונגו משתמש בפקודת mmap של לינוקס בכדי למפות extents (בלוקים בקבצים) לזיכרון. הקבצים הם כמו "גדר הגנה" בפני fragmentation שיכולה להיווצר באופן טבעי במערכת הקבצים.
כאשר מסמכים גדלים מעבר ל padding הקיים - הם מועברים למקום חדש עם padding חדש. מונגו לומד את קצב השינויים ב collection ולפיו מבסס את ה padding factor לפיו יקבע גודל padding חדש. גדילות תכופות של מסמכים --> paddings גדולים יותר.
כאשר מסמכים מועברים ממקומם או נמחקים - נוצרים "רווחים ללא שימוש". עד כמה שידוע לי - מונגו לא מנסה למלא אותם. רווחים אלו הם לא-טובים לביצועים של מונגו - כפי שניתן לראות בניתוח הזה.
fragments בתוך extents של מונגו. מקור. |
הפתרון בעיקרון הוא לבצע de-fragmantation בעזרת פקודת compact.
הקושי: למונגו יש thread יחיד לכתיבות, ופעולת compact תתקע את בסיס הנתונים כולו לזמן מה (כלומר: down time). אפשר לתזמן פעולות תחזוקה אלו או אפשר, בעזרת replication של nodes של מונגו - לעשות את זה offline.
זה כנראה לא מה שהייתם מצפים מ"בסיס הנתונים מהדור החדש" - אבל ככה זה.
עוד נקודה כואבת במונגו היא נעילות (לצורך consistency):
נעילות הן ברמה של בסיס נתונים (database). עד גרסה 2.1 - נעילות בכלל היו ברמת כל ה instance של מונגו. ישנן תוכניות לעתיד לשפר את רמת הנעילה במונגו.
המשמעות היא כמובן מגבלה משמעותית לביצוע כתיבות ב scale, לפחות כאשר אנו עובדים ברמת durability (או safety) גבוהה.
הברירה היא בין לוותר על מידה מסוימת של durability, לצמצם כתיבות, או להשתמש ב very-high-end SSD עם IOPS (פעולות IO בשנייה) גבוה במיוחד: איזה חצי מיליון IOPS, הייתי אומר. האפשרות השלישית - חסומה בחומרה הקיימת כיום, כמובן.
ה consistency במונגו מובטח רק ברמת פעולה על מסמך - ואין להניח על סדר הפעולות בכלל המערכת (isolation).
מה קורה כאשר רוצים לבצע שינוי במסמך א' בצורה אטומית (למשל: להגדיל ערך מספרי ב 3)?
או שמוכנים לספוג חוסר consistency מסוים, או שאפשר להשתמש בכלי מיוחד של מונגו בשם collection.findAndModify המקבלת הוראות ומבצעת שינויים במסמכים בצורה אטומית.
הפקודה תבצע שינוי אטומי במסמך אחר מסמך, אך כל פעם - במסמך בודד.
מה קורה כאשר רוצים לבצע שינוי במסמך ב' על בסיס של נתונים ממסמך א'?
לבעיה זו אין למונגו פתרון מובנה, ויש כל מיני פתרונות מקובלים / "design patterns" כיצד לפתור את הבעיה. בגדול סט הפתרונות מתבסס על יצירת מסמך שלישי זמני בשם (מפתיע:) transaction שמסייע לנהל את הפעולה.
נקודה אחרונה מעניינת היא נושא גודל המידע. לחוסר הסכמה של מונגו יש חסרון אחד ברור: יש לאכסן את ה keys בכל פעם, מה שיכול להגדיל את גודל המידע בצורה משמעותית הן בדיסק והן ברשת (מול ה clients של מונגו).
אמנם JSON הוא מבנה נתונים יחסית יעיל, וכאשר מונגו שולח / מאכסן מסמכים - הוא בעצם משתמש בצורה בינרית יותר יעילה שנקראת BSON [ד] (קיצור של Binary JSON), אך עדיין מדובר בבזבוז.
סיכום
מונגו הוא לא בסיס נתונים מושלם, אך כנראה שהוא מספיק טוב למערכות רבות, ויכול לסייע בקיצור זמני פיתוח ותחזוקה.
ב technology stacks כמו MEAN (קיצור של Mongo, Express, Angular and Node.js) - השימוש הטבעי של מונגו ב javaScript ו JSON מאפשר לכתוב פתרון קצה-אל-קצה בשפה יחידה: javaScript.
למרות שמונגו יחסית עשיר ביכולות, יש לא מעט ספריות ODM (קיצור של Object-Document Mapping), כגון mongoose או doctrine, המסייעות במידול הנתונים או בהעשרת היכולות בשימוש במונגו.
מונגו, ומודל ה documents בכלל, טבעי מאוד לפיתוח מערכות בהן היינו משתמשים בכלי ORM: מסמך (document) הוא סוג של אובייקט, ועבודת התרגום בין שפת התכנות לשפת בסיס הנתונים, ובחזרה - נחסכת מאיתנו. ביוחד כאשר יש לפרק/להרכיב "אובייקט" יחיד מטבלאות רבות.
מונגו הוא בד"כ מהיר מ RDBMSs, לא בגלל שהמפתחים שלו חכמים יותר - אלא בגלל שהם עשו בחירות תכנוניות שמעדיפות מהירות על דברים אחרים (למשל: ההחלטה ל durability לא מושלם, כברירת מחדל). בטלו את הבחירות הללו - והפערים יצטמצמו. בעוד מימוש של KVDB הוא יחסית פשוט על גבי RDBMSs קיים (עם scale מוגבל יותר), מימוש של DBDB על גבי RDBMS הוא קשה - בגלל תהליכי האינדוקס.
כמו בסיס NoSQL רבים אחרים, מונגו מוסיף אחריות על המפתח: לדאוג לשלמות ואחידות הנתונים. מונגו העלים סט בעיות אחד מהמפתח - אך חושף אותו לסט אחר (בשאיפה: מצומצם יותר) של בעיות אחרות.
כשאתם ניגשים לעבוד במונגו - על תצפו ל Internet Scale. הוא כנראה טוב ב Scale בהרבה מ RDBMS - אך לא טוב כמו Riak או Cassandra. כמו כן אל תצפו לבגרות של RDBMS - המבוססים על מודל שכבר בעבודה במשך עשרות שנים.
בכל זאת, אם אתם מתחילים לפתח מערכת חדשה, במיוחד מערכות ווב (במידה, "המגרש הביתי של מונגו") ובמיוחד כאלו שאינן מוגדרות היטב עדיין - יש סיכוי טוב שעם מונגו תתקדמו מהר וטוב יותר מאשר עם MySQL או PostgresDB.
שיהיה בהצלחה!
זמן קצר לאחר שחרור הפוסט שוחררה גרסה משמעותית, 2.6, של מונגו. ניתן לקרוא highlights בלינק הבא.
---
לינקים רלוונטיים:
מצגת מעניינת על מידול נתונים במונגו
השוואה (מעט פרובוקטיבית) בין הביצועים של מונגו ו MS SQL
----
[א] DB-Engines קיים "כולו" שנתיים ומשהו, אבל ע"פ כל מדד מקובל - לא נראה שבסיס נתונים לא-רלציוני היה איפשהו ברשימה הפותחת ב 30 שנה האחרונות.
עוד הערה: Cassandra ומונגו נמצאים ברשימת ה top10 מאז המדד החל, אולם Sybase ASE "נדחף" ל Cassandra באמצע והחזיר אותו למקום העשירי. הסיבה היא כנראה החיזוק ש ASE קיבל מרכישת SAP את Sybase, אולם אין בכך לגרוע מהמגמה הכללית: ASE צמח בעיקר על חשבון אורקל.
[ב] ב NoSQL, כמו ב NoSQL - אין הגדרות מוסכמות או חד-משמעיות. התייחסו לכל הגדרה כ "נקודת מבט אפשרית".
[ג] מקור המונח daemon הוא מניסוי מחשבתי בתחום התרמודינמיקה בשם Maxwell's demon בו יש שדון שעובד ללא הפסקה ברקע ופותח דלת קטנה לחלקיקים שיעברו דרכה... daemon היא צורת כתיבה עתיקה של המילה demon (שד).
[ד] פורמט BSON גם מאפשר ניהול טיפוסים שלא קיימים ב javaScript כגון object id או date כפרמיטיב.
[ה] epoch (תאריך בסיס) הוא נקודת ייחוס ממנה שומרים תאריך בצורה מספרית, בדרך כלל בשניות. MS DOS, היתה מערכת 16bit ולא רצו לשמור תאריך, בשניות, משנת 0 (או אפילו מ 1970, כמו unix) - ולכן ה epoch הוא 1/1/1980.
כדי לתאר את התאריך 2/1/1980 (יום אחרי) - יש לשמור בשדה התאריך את המספר 24*60*60 = 86,400.
במערכת הקבצים NTFS (חלונות NT) זמן הייחוס הוא שנת 1601 - השנה הראשונה במחזור 400-השנים של לוח השנה הגרוגאני בו, שימו לב: שוחררה חלונות NT!. אפל (כתגובה?) קבעה את ה epoch של OS X לשנת 2001 - השנה בה יצאה מערכת ההפעלה OS X. בגרסאות קודמות של MAC OS, ה epoch היה שנת 1904 - שנקבעה בגלל שזו הייתה "השנה המעוברת הראשונה במאה ה-20", ואולי כדי להתבשם בעובדה שזו הייתה מערכת שרצה על מעבדי 24 ביט (בזמן ש DOS הייתה תלויה בעבדי 16 ביט, ו epoch כזה היה מגביל אותה).