כיצד Huge Pages משפרים את הביצועים של PostgreSQL, Oracle ו-Redis

כאשר מסד הנתונים גדל והזיכרון מנוצל בלא יעילות, השאילתות מתחילות להתעכב דווקא בשעות שיא. אנו מגדירים עמודים גדולים ב-Linux באופן מלא: בוחרים מצב הקצאה, בודקים את התוצאה בעומס שלכם ומעבירים לצוות שלכם נוהל תחזוקה ברור. זמני התגובה מתקצרים בלי לכתוב מחדש את האפליקציה.

היכולות שלנו:

שאלות נפוצות

העבודות האחרונות

  • פיתוח אפליקציית ווב עבור FEEDME
    פיתוח אפליקציית ווב עבור FEEDME
    1359
  • פיתוח חנות מקוונת לחברת FURNORO
    פיתוח חנות מקוונת לחברת FURNORO
    1330
  • עיצוב לוגו לחברת B2B Advance
    עיצוב לוגו לחברת B2B Advance
    776
  • פיתוח אפליקציית ווב עבור Enviok
    פיתוח אפליקציית ווב עבור Enviok
    1077
  • פיתוח לוגו חברת AIDER
    פיתוח לוגו חברת AIDER
    1022
  • פיתוח CRM עבור Chasseurs
    פיתוח CRM עבור Chasseurs
    1113

למה מסד הנתונים נתקע בזיכרון: התפקיד של Huge Pages Linux

כל עוד מערך העבודה של מסד הנתונים נכנס לזיכרון ולמטמון המעבד, ההבדל בין דף רגיל לדף גדול אינו נראה. ברגע שנפח הנתונים גדל מעבר למטמון, כל החטאת טבלת דפים הופכת למעבר על כמה רמות — וזה כבר לא יחידות מחזורים לבקשה אחת, אלא אלפים לכל מעבר מלא על האינדקס. שאילתות מתחילות להצטבר בתור לא בגלל מחסור במשאב, אלא בגלל האופן שבו המשאב מחולק.

כברירת מחדל, הליבה מחלקת את הזיכרון לדפים של 4KB. למקרה כללי זה נוח, אבל עבור שרת מסד נתונים זה אומר דבר אחר: טבלת הדפים גדלה יחד עם מאגר החוצץ, הרשומות שלה כל הזמן מחטיאות את מטמון תרגום הכתובות, והוצאות הניהול של הזיכרון אוכלות זמן מעבד שאמור ללכת לנתונים עצמם.

לזה מתווספת פרגמנטציה של זיכרון הליבה. במשך חודשי פעילות, האזורים החופשיים מתפזרים, קשה יותר ויותר לאסוף אזור רציף למאגר גדול, והליבה מבזבזת מחזורים על איחוי במקום על מתן שירות לשאילתות. הפעלה מחדש של השירות עוזרת לזמן מה — סכימת חלוקת הזיכרון עצמה לא משתנה.

בגרפים זה נראה כעלייה בשיעור זמן המערכת ובתורים על דפים. עליית ההשהיות של שאילתות מתחילה הרבה לפני שהזיכרון נגמר: הלקוח רואה לא כשל, אלא "בשיא זה נעשה איטי יותר". זה סוג לא נוח של דעיכה — בדרך כלל מייחסים אותו לעומס ומשלימים איתו חודשים.

Huge Pages Linux משנים את קנה המידה של המשימה: דף אחד מכסה אזור שנדרש קודם לכן מאות רשומות, טבלת הדפים מתכווצת, החטאות מתרחשות לעתים רחוקות יותר, והליבה מפסיקה להיקרע בין מתן שירות לשאילתות לבין ניהול הזיכרון. בהמשך נפרק איך זה בנוי ובאילו תרחישים זה נותן אפקט מדיד.

מה נותן המעבר לדפים גדולים: אפקט מדיד

דפים גדולים מסירים עבודה שלא אמורה להיות במסד. כאשר PostgreSQL, Oracle או Redis מחזיקים את מערך העבודה בזיכרון משותף, הליבה מחלקת אותו לדפים קטנים — וכמעט כל גישה מאלצת את המעבד לחפש רשומה בטבלת הדפים. בנתיב החם זהו חלק ניכר מהמחזורים, שהולך לא לשאילתות אלא לחישוב מחדש של כתובות.

המעבר לדפים גדולים משנה את קנה המידה: רשומה אחת במאגר תרגום מכסה פי מאות יותר זיכרון, וההחטאות בו יורדות בסדר גודל. זה נראה בשלושה גדלים — העומס על מעבד הליבה באותה שאילתה נמוך יותר, זמן התגובה של המסד מפסיק לרעוד בשיא, והתפוקה עולה בלי להוסיף חומרה.

האפקט החזק ביותר הוא במקומות שבהם יש הרבה נתונים בזיכרון והגישות אליהם אקראיות: אנליטיקה, אינדקסים, מטמון הפעלות.

התוצאה השנייה — סוג כשלים של זיכרון שנמנע. מקטע רציף למאגר המשותף מוקצה בעת ההפעלה, ואם הזיכרון עד אז מפוצל, השירות או לא עולה, או נופל מאוחר יותר, עם גידול העומס. דפים גדולים שנשמרו מראש מוקצים באופן דטרמיניסטי, ולכן עלות ההשבתה מפסיקה להיות תלויה בכמה זמן המארח פועל.

האפקט ניתן לבדיקה, ולא מקבלים אותו כאמונה: מצלמים פרופיל לפני ואחרי — זמן תגובה בשיא, עומס ליבות, מספר החטאות תרגום והתנהגות המסד בעת הפעלה מחדש.

מצבי Huge Pages Linux: הקצאה מפורשת ומצב שקוף

ב-Linux על התמיכה בדפים גדולים אחראים שני מנגנונים בלתי תלויים: הקצאה מפורשת ואיחוד שקוף. הם מקצים זיכרון בדרכים שונות ומתנהגים אחרת כשהוא חסר — ולכן בחירת המצב משפיעה ישירות על יציבות ה-DBMS תחת עומס.

הקצאה מפורשת (hugetlbfs) מקבעת מראש מאגר דפים, באמצעות פרמטרים של הליבה, ומחזיקה אותו כל זמן הפעילות. מסדי נתונים רואים דפים מוכנים, גודל המאגר צפוי ואינו תלוי במצב הזיכרון הנוכחי. המחיר לכך — יש לחשב את המאגר לשיא, והתכלותו פירושה כשל בהקצאה, ולא דעיכה שקטה.

אפשרות מתי מתאימה מגבלות
הקצאה מפורשת מסדי נתונים ושירותים עם זיכרון משותף ארוך-טווח: PostgreSQL, Oracle, Redis המאגר מקובע מראש; חישוב מחדש דורש הפעלה מחדש של השירות
איחוד שקוף עומס כללי, חישובים, תהליכים קצרי-טווח אין הבטחת הקצאה: דפרגמנטציה ברקע עשויה לא להספיק, החזרת זיכרון מתעכבת
היברידי: שקוף מופעל, מפורש — נקודתית צמתים משולבים שבהם המסד אינו הצרכן היחיד יש לוודא שהרקע לא ידחוק את המוקצה מראש

איחוד שקוף אינו דורש הגדרה ידנית: הליבה עצמה אוספת דפים גדולים מדפים קטנים. אבל ההקצאה מתרחשת לפי יכולת, ולא לפי דרישה — עבור תהליך מסד ארוך-טווח זה אומר גודל בלתי צפוי של מערך העבודה.

לכן עבור מסדי נתונים בוחרים הקצאה מפורשת: רק היא נותנת גודל דטרמיניסטי והשהיה אחידה. המצב השקוף משאירים לשאר התהליכים או מכבים בצמתים שבהם המסד הוא הצרכן העיקרי של הזיכרון.

איך אנחנו מטמיעים דפים גדולים: שלבי העבודה ההנדסית

סדר העבודות נבנה כך שלא ניקח זיכרון משירות פעיל ושתהיה אפשרות חזרה בכל שלב.

קודם ביקורת תצורה: מוציאים Hugepagesize ואת המונים HugePages_Total/Free מ-/proc/meminfo, בודקים את מצב הדפים השקופים, מגבלות תהליכים, התפלגות בין צמתי זיכרון והגודל בפועל של מערכי העבודה של המסד או המטמון.

  1. ביקורת תצורה. מתעדים את הצריכה הנוכחית, שיעור ההחטאות במאגר תרגום הכתובות ומלאי הזיכרון הפנוי — בלי זה חישוב הדפים יהיה בניחוש.
  2. חישוב ההקצאה. מחשבים את מספר הדפים למערך העבודה בשיא עם מרווח לגדילה ובנפרד — עבור תהליכי שירות, כדי שאחרי הפעלת ההקצאה המערכת לא תעבור ל-swap.
  3. הגדרת הליבה. רושמים את הקצאת הדפים, מכבים או מעבירים למצב מנוהל את הדפים השקופים, מקבעים את הפרמטרים בטוען האתחול וביחידות המערכת, כדי שישרדו אתחול מחדש.
  4. הפעלה ניסיונית. מרימים על צומת אחד או רפליקה, בודקים שהשירות קלט את הדפים ושפרגמנטציית הזיכרון לא מפריעה להקצות אותם אחרי זמן פעילות ארוך.
  5. בדיקה תחת עומס. משווים למדידה הבסיסית: זמן תגובה בשיא, התנהגות המטמון, יציבות ההשהיות, היעדר הפעלות מחדש וגידול הצריכה בעבודה ממושכת.
  6. נראות. מגדירים איסוף מוני דפים והתראות על מחסור — אחרת הבעיה תתגלה ככשל פתאומי בהפעלה הבאה של השירות.
  7. העברת התחזוקה. מוסרים לצוות שלכם תצורות, סכימת פריסה, הוראות להפעלה מחדש מתוכננת ורשימת בדיקות אבחון.

פרמטרים בכל שלב מתאמים עם המהנדסים שלכם: שינויים בהקצאת הזיכרון נוגעים לכל שירותי הצומת, לכן מטמיעים אותם בהדרגה, תוך שמירה על אפשרות לחזור לתצורה הקודמת.

מה נשאר לצוות שלכם אחרי ההטמעה

את ההגדרה אנחנו לא משאירים כסיכומים בעל פה: כל פרמטר של Huge Pages מתועד יחד עם הסיבה שבגללה נבחר. להלן — מערכת התוצרים שלפיה המהנדסים שלכם יוכלו לתחזק את התצורה בעצמם ולשחזר אותה על צמתי Linux חדשים בלי מעורבות שלנו.

  • מערך תיעוד — ערכי פרמטרים של הליבה והמסד עם הנמקה: למה נבחר גודל הקצאה כזה ומה ישתנה אם מקטינים אותו.
  • סכימת פריסת זיכרון — מפת צמתים: כמה דפים מקובעים לכל מופע, היכן הושאר מרווח, איך הזיכרון מתחלק בהפעלה מחדש של שירותים שכנים.
  • נוהל תחזוקה — סדר בדיקת ההקצאה אחרי עדכון ליבה ואתחול מחדש, בקרת פרגמנטציה ודליפות של דפים מקובעים לפי לוח זמנים.
  • מדיניות גישה — מי רשאי לשנות פרמטרים, דרך איזו מערכת ניהול תצורה, אילו עריכות דורשות אישור ואיך מתבצעת ביקורת.
  • הוראות חזרה לאחור — חזרה מדורגת לדפים רגילים: אילו שירותים נעצרים, באיזה סדר, איך לוודא שהמדדים חזרו לערכים ההתחלתיים.
  • רשימת בדיקות לצומת חדש — מה מריצים לפני העלאה לעבודה: ההקצאה הוקצתה, המסד רואה אותה, המדדים תקינים.

התוצרים מעוצבים כפי שמשתמשים בהם בפועל: פקודות מוכנות, תבניות תצורה, היסטוריית שינויים. את התחזוקה אתם מבצעים בעצמכם — אנחנו מתחברים רק למשימות חדשות.

מקרה בוחן: איך צמצמנו השהיות שאילתות במסד עמוס

מתאר הייצור — שירות עם מסד על PostgreSQL, שבו עומס אחיד פעם ביממה הופך לשיא קצר אך קשה. לפני העבודות, השהיות הזנב של שאילתות בשיא הזה חצו את הסף היעד, והזיכרון התושב של התהליך גדל במדרגות ולא חזר לערכו ההתחלתי אחרי ירידת התעבורה.

מדידות נלקחו במתאר הייצור, בלי לעצור את השירות: התפלגות השהיות לפי אחוזונים, צריכת זיכרון, תדירות החטאות בתרגום כתובות ונפח מבני שירות תחת דפים משותפים. בנפרד תועד פרופיל דווקא תחת עומס שיא — שם הדעיכה בלטה ביותר, ובשעות שקטות כמעט לא נראתה.

מבנים משותפים הועברו לדפים גדולים, והוקצה מאגר מראש בעת הפעלת התהליך, כדי שלא יתפורר במהלך העבודה. לנוהל ההפעלה הוספה בדיקת המאגר הזמין, ולניטור — אות על התכלותו: בלי זה הרווח נעלם אחרי האתחול הראשון של הצומת.

השהיות הזנב בשיא הצטמצמו בערך פי שלושה, ושיעור השאילתות שחרגו מהסף היעד ירד מ-4% ל-0.3%. התהליך הפסיק לחרוג מהנפח שהוקצה מראש, השיא עובר בלי גידול מדורג של זיכרון ובלי הפעלות מחדש לא מתוכננות.

המדידות חזרו על אותה עומס ועם אותו פרופיל שאילתות, לכן המספרים ברי השוואה, ולא נשלפו מחלון נוח. החזרה לאחור כאן — עריכת תצורה והפעלה מחדש, לכן אפשר קודם להריץ סימולציה כזו על עותק של המתאר שלכם ולהשוות למדידות הנוכחיות.

מה לעשות אם דפים גדולים כבר מוגדרים חלקית?

מתאר מוגדר חלקית הוא מצב שבו דפים גדולים כבר הוקצו, אבל אינם מתואמים ביניהם: חלק מהפרמטרים הוגדרו ידנית במערכת פעילה וחיים עד האתחול הבא, חלק מתוארים בפרופיל הטוען, וגודל המאגר נתון בהגדרות המסד.

בדיקת המצב הנוכחי מתחילה מ-/proc/meminfo: משווים את הנפח הכולל, המוקצה והפנוי של דפים — הפער ביניהם מיד מראה איזו הקצאה עומדת ללא שימוש ולא מגיעה לתהליך.

המקור הטיפוסי השני — התנגשות פרמטרים. המסד עובר לדפים גדולים רק אם הנפח עבור המאגרים שלו מכוסה במלואו בדפים שהוקצו והוסר המגבלה על זיכרון חסום של התהליך; אם ההתאמה חלקית, השירות או עולה בשקט על דפים בסיסיים, או לא עולה כלל.

מלכודת נפרדת — דפים גדולים שקופים של הליבה: תחת עומס הם משנים את הפריסה, והקצאה ידנית מאבדת משמעות, לכן מכבים אותם במפורש.

בהמשך עושים סדר: משאירים מקור אמת אחד לפרמטרי האתחול ולמגבלות המערכת, מסירים כיוונים ידניים מההפעלה האוטומטית, ומביאים את נפח ההקצאה לגודל המאגרים עם מרווח לרפליקות ולתהליכי שירות.

מעבר בלי השבתה נבנה לסירוגין: קודם מגדירים ובודקים על רפליקה, מעבירים את תפקיד הצומת הראשי, ואז חוזרים על אותו דבר על הראשי הקודם. השלב עם אתחול מחדש נשאר, אבל זמינות האפליקציה אינה נפסקת.

אחרי המיתוג חוזרים לבדיקה: בודקים את שיעור הדפים הגדולים שבשימוש בפועל ואת כשלי ההקצאה ביומן הליבה. אם מונה הכשלים גדל, הנפח או המגבלות נבחרו לא נכון, ויש לחזור על העריכה עד לתוצאה יציבה.

ספקות טכניים: עונים לשאלות נפוצות

הטמעת Huge Pages משנה את אופן הקצאת הזיכרון ברמת הליבה: מסד הנתונים מקבל דפים בגודל גדול במקום אוסף דפים קטנים. לכן ספקות טכניים מתעוררים לפני הבעיות — מהנדס רוצה להבין היכן גבול הסיכון: מה יקרה לרפליקות ולאשכול, כמה מהר לחזור לאחור אם פרופיל העומס יתברר כאחר.

העריכה מקומית לצומת: העברת שינויים בין הצומת הראשי לרפליקה עוברת בזרם נפרד ואינה תלויה בגודל הדפים. להלן — מה שבדרך כלל עולה באישור.

האם ההתנהגות של הצומת הראשי והרפליקות לא תתפצל?

לא, אם גודל הדפים מוגדר זהה בכל הצמתים. תשומת לב מיוחדת דורש הזיכרון המשותף של התהליך: הוא מוקצה כבלוק שלם, ובגודל דפים שונה המקטע המשותף ברפליקה לא יעלה. לכן ההגדרה נפרסת על האשכול בחלון אחד, ולא צומת-צומת — אז ההשפעה על שכפול היא אפסית.

איך נראית החזרה לאחור אם משהו ישתבש?

החזרה לאחור היא דו-שלבית. פרמטר הליבה חוזר לערכו הקודם ומיושם אחרי אתחול מחדש של הצומת, והגדרות המסד עצמו — לקו הבסיס שנלקח לפני ההטמעה. שתי התצורות מוחזקות אצלנו כעובדות ומאומתות, לכן החזרה אינה דורשת בנייה מחדש: זו פעולה מנוהלת בחלון מוכר.

אילו סיכוני הטמעה כדאי לקחת בחשבון מראש?

העיקרי — מחסור בזיכרון פיזי רציף: אם ההקצאה לא נעשתה בעת ההפעלה, לא ניתן יהיה להקצות חלק מהדפים והצומת יחזור למצב רגיל. השני — טעות בחישוב ההקצאה, כשהיא דוחקת תהליכים אחרים.

שניהם מוסרים במדידה על עותק של צומת הייצור תחת עומס אמיתי: בודקים השהיית שאילתות, שיעור הזמן בקריאות מערכת ויציבות המאגרים. פריסה למתאר הייצור מתבצעת רק אחרי זה ועם תוכנית חזרה לאחור ביד.

נדבר על המשימה: מה נדייק ואיך נתחיל לעבוד

התחלה — זו שיחה עניינית אחת: אתם מתארים את המתאר והמשימה, ואנחנו חוזרים עם תוכנית עבודה והערכה. כדי לא לברר נתונים תוך כדי, מתעדים אותם מיד — מזה תלוי אילו הגדרות זיכרון בכלל יש טעם לגעת בהן.

  • פרופיל עומס: מה שולט — טרנזקציות קצרות, שליפות אנליטיות או גישות למטמון, ובאילו שיאים נראית הדעיכה;
  • מצב הזיכרון של המארח: האם מצב huge pages שקוף מופעל, אילו פרמטרי ליבה מוגדרים, איך המשאבים מחולקים בין התפקידים;
  • סכימת המתאר: היכן ממוקמים PostgreSQL, Oracle ו-Redis, האם יש רפליקות והפרדה בין קריאה לכתיבה;
  • חלונות תחזוקה: מתי מותרת הפעלה מחדש של שירות המסד ולכמה זמן אפשר להוציא את הצומת מפעילות;
  • דרישות לחזרה לאחור: כמה מהר צריך להחזיר את הפרמטרים הקודמים אם המדדים יורדים;
  • גישות: סביבת בדיקה קרובה לפרודקשן ונתוני ניטור שמהם רואים את התפלגות הזיכרון בפועל;
  • קריטריון הצלחה: מה נחשב לתוצאה — צמצום השהיית שאילתות, גידול בהחטאות מטמון או יציבות בשיא.

בתמורה אתם מקבלים תוכנית עבודה: ממה מתחילים, מה בודקים בסביבת הבדיקה, לפי אילו מדדים מקבלים את התוצאה ובאילו ערכים מחזירים את השינויים לאחור. סדר ההתחלה פשוט — מתאמים את נתוני המתאר, קובעים חלון ומגיעים עם הערכה. תארו את המשימה, ואנחנו נחזור עם תוכנית קונקרטית.