תארו לעצמכם: שרת עם 32 GB RAM, MongoDB משתמש ב-16 GB עבור מטמון WiredTiger, אבל השאילתות עדיין לוקחות שניות. הסיבה — אינדקסים לא יעילים ותצורה שגויה. ראינו את זה פעמים רבות: COLLSCAN במקום IXSCAN, פינוי מטמון מדפי דיסק, ואגרגציות שצורכות את כל הזיכרון. אופטימיזציה של MongoDB היא משימה מורכבת הכוללת כוונון מנוע WiredTiger, עיצוב אינדקסים ופרופילינג של שאילתות איטיות. ללא גישה שיטתית, אפילו שרתים חזקים רצים בחוסר יעילות. למהנדסים שלנו יש הסמכות MongoDB וניסיון של 10+ שנים בתחום. במאמר זה, אנו חולקים טכניקות מעשיות שעזרו ללקוחותינו להפחית את זמן התגובה ב-50%. נסקור טעויות נפוצות וכיצד לתקן אותן. תשומת לב מיוחדת ניתנת לכלל ESR עבור אינדקסים וכוונון מטמון WiredTiger — שני תחומים אלה מניבים את ההשפעה הגדולה ביותר.
בעיות שאנו פותרים
תצורת מטמון WiredTiger לא יעילה
כברירת מחדל, MongoDB מקצה 50% מה-RAM עבור המטמון. על שרת עם 32 GB, זה 16 GB. אבל ללא הגדרה מפורשת של cacheSizeGB, המטמון עלול להתפנות על ידי תהליכים אחרים. אנו תמיד מגדירים אותו ידנית, ומשאירים מרווח עבור מערכת ההפעלה ומטמון הדיסק.
---
# /etc/mongod.conf
storage:
wiredTiger:
engineConfig:
cacheSizeGB: 12 # Для сервера 32 GB
journalCompressor: snappy
collectionConfig:
blockCompressor: snappy
indexConfig:
prefixCompression: true
---ניטור: # /etc/mongod.conf storage: wiredTiger: engineConfig: cacheSizeGB: 12 # Для сервера 32 GB journalCompressor: snappy collectionConfig: blockCompressor: snappy indexConfig: prefixCompression: true . אם db.serverStatus().wiredTiger.cache > 0, המטמון תחת לחץ — הגדל את cacheSizeGB.
אינדקסים: חסרים או בסדר שגוי
ללא אינדקס, כל שאילתה הופכת ל-COLLSCAN. הכלל המרכזי הוא ESR (Equality, Sort, Range). כך בונים אינדקס מורכב:
// Запрос: найти активные заказы пользователя, отсортировать по дате
db.orders.find({ user_id: ObjectId("..."), status: "active" }).sort({ created_at: -1 })
// Правильный индекс: equality → sort
db.orders.createIndex({ user_id: 1, status: 1, created_at: -1 }) אגרגציות איטיות עם $lookup
הטעות הנפוצה ביותר היא הצבת $match אחרי $lookup. הסדר האופטימלי:
db.orders.aggregate([
{ $match: { status: "completed", created_at: { $gte: ISODate("текущий год-01-01") } } },
{ $lookup: { from: "users", localField: "user_id", foreignField: "_id", as: "user", pipeline: [{ $match: { country: "RU" } }, { $project: { name: 1, email: 1 } }] }},
{ $project: { _id: 1, total: 1, "user.name": 1 } }
])עבור צינורות מקבילים, השתמש ב-pages evicted by application threads.
כוונון מטמון WiredTiger: פרמטרים מרכזיים
הפרמטר המרכזי הוא cacheSizeGB. הגדר אותו ל-60–70% מה-RAM הזמין, אך לא יותר מ-20 GB בגרסאות מודרניות (לפי התיעוד הרשמי של MongoDB). השאר את השאר למערכת ההפעלה ולמטמון הדיסק. עבור שרת עם 64 GB, cacheSizeGB אופטימלי הוא 40, אך בהתחשב בתהליכים אחרים, בדרך כלל 35–40.
אינדקסים: עיצוב ותחזוקה
השתמש בכלל ESR עבור אינדקסים מורכבים. בדוק באופן קבוע אינדקסים שאינם בשימוש: // Запрос: найти активные заказы пользователя, отсортировать по дате db.orders.find({ user_id: ObjectId("..."), status: "active" }).sort({ created_at: -1 }) // Правильный индекс: equality → sort db.orders.createIndex({ user_id: 1, status: 1, created_at: -1 }) . אינדקסים עם accesses.ops == 0 הם משקל מת — יש להסירם. זה יכול לחסוך עד 20% מה-RAM.
מתי לבצע Sharding ל-MongoDB?
Sharding מוצדק אם הנתונים עולים על 200 GB או שעומס הכתיבה גבוה מ-10,000 RPS על שרת יחיד. בחר מפתח shard עם קרדינליות גבוהה, למשל hash של user_id.
איך אנחנו עושים את זה: מקרה בוחן
ביצענו אופטימיזציה ל-MongoDB עבור לקוח מסחר אלקטרוני — עם קטלוג של 5 מיליון מוצרים. זמני השאילתות עבור קטגוריות ומחירים היו 2-3 שניות. הפתרון שלנו:
- אינדקסים מורכבים על (category, price, created_at).
- cacheSizeGB = 20 על שרת עם 64 GB.
- שאילתות אנליטיות נותבו ל-secondary (readPreference: secondaryPreferred).
- החלפנו $lookup בסינון לאחר באמצעות pipeline.
תוצאה: זמן התגובה ירד ל-50 ms, עומס המעבד ירד ב-30%. הצוות שלנו מבטיח שגישת אינדקסים זו מקצרת את זמן התגובה בחצי בהשוואה לתצורה טיפוסית. למקרים דחופים, אבחון מהיר זמין תוך יום אחד — צור קשר.
בדיקה קבועה של אינדקסים שאינם בשימוש
אינדקסים עם accesses.ops == 0 צורכים זיכרון ומאטים כתיבות. הפעל $indexStats חודשי והסר אינדקסים שאינם בשימוש. זה חוסך עד 20% מה-RAM על אינדקסים. cacheSizeGB נכון נותן שיפור מהירות של 40% לעומת הגדרות ברירת מחדל.
תהליך ועלות
- ביקורת: profiler, explain, ניתוח אינדקסים.
- עיצוב: חישוב cacheSizeGB, אינדקסים לפי ESR.
- יישום: הגדרה, יצירה/הסרה של אינדקסים, אופטימיזציה של שאילתות.
- בדיקות: בדיקות עומס, השוואת מדדים.
- פריסה: יישום שינויים, ניטור.
לוחות זמנים לאופטימיזציה נעים בין 3 ל-10 ימי עסקים. העלות מחושבת באופן אישי לאחר ביקורת. חיסכון בתשתית שרתים יכול להגיע ל-30% בשל הפחתת עומס. האופטימיזציה שלנו מחזירה את עצמה תוך 2-3 חודשים.
מה כלול באופטימיזציה של MongoDB
- ביקורת מלאה של התצורה והביצועים הנוכחיים.
- עיצוב סכמת אינדקסים המותאמת להיגיון העסקי.
- כוונון WiredTiger (cacheSizeGB, דחיסה, journal).
- אופטימיזציה של שאילתות איטיות ואגרגציות.
- תיעוד שינויים והמלצות תפעוליות.
- הדרכת צוות והעברת גישה.
- 30 ימי תמיכה לאחר הפריסה.
רשימת בדיקה לכוונון
| רכיב | פעולה | קריטריון |
|---|---|---|
| מטמון WiredTiger | הגדר במפורש | cacheSizeGB = 60-70% מה-RAM |
| אינדקסים | בדוק את כל השאילתות הרגילות | ללא COLLSCAN |
| אינדקסים שאינם בשימוש | הסר | accesses.ops == 0 |
| אגרגציה | $match קודם | ללא $lookup ללא פילטר |
| העדפת קריאה | אנליטיקה על secondary | readPreference: secondaryPreferred |
השוואת גישות נפוצות לאינדקסים
| גישה | יתרון | חיסרון |
|---|---|---|
| אינדקסים מורכבים לפי ESR | אופטימלי למיון וסינון | דורש סדר שדות מדויק |
| אינדקסים מכוסים | השאילתה לא ניגשת למסמך | מגדיל את גודל האינדקס |
| אינדקסי Hash | אידיאלי ל-sharding | רק שוויון מדויק |
דוגמה: הפעלת Profiler
db.setProfilingLevel(1, { slowms: 50 });
db.getProfilingStatus();
db.system.profile.aggregate([
{ $group: { _id: "$ns", avgMillis: { $avg: "$millis" }, count: { $sum: 1 } } },
{ $sort: { avgMillis: -1 } },
{ $limit: 10 }
]) טיפ לאבחון
אם אינך יודע מאיפה להתחיל, הפעל את ה-profiler ב-50 ms ולאחר שעה בדוק את 10 השאילתות האיטיות המובילות. לעתים קרובות אינדקס אחד פותר את הבעיה.הזמן ביקורת ביצועים ל-MongoDB — קבל ייעוץ מהנדס ותוכנית אופטימיזציה.







