ניתוח מעמיק של דחיית ביצועי MySQL 8.0

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

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

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

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

הסיבות העמוקות לירידת ביצועי MySQL

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

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

לדוגמה, התמונה הבאה מראה את ביצועי פעולת השרשור הפשוטה, עם MySQL 8.0.40 מראה ירידה בביצועים בהשוואה לגרסה 8.0.27 של MySQL:

Figure 1. Significant decline in join performance in MySQL 8.0.40.

התמונה הבאה מראה את בדיקת ביצועי הכנסת מצטברת בתנאי זרימה יחידה, עם הירידה בביצועים של MySQL 8.0.40 בהשוואה לגרסה 5.7.44:

Figure 2. Significant decline in bulk insert performance in MySQL 8.0.40.

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

באשר לניתוח שורש גורם לירידת הביצועים ב־MySQL על רמת הקוד, הנה הפונקציה PT_insert_values_list::contextualize ב־MySQL 8.0:

C++

 

הפונקציה המתאימה PT_insert_values_list::contextualize ב־MySQL 5.7 נראית כך:

C++

 

מהשוואת הקוד, נראה שבMySQL 8.0 יש קוד יותר אלגנטי, שמראה התקדמות.

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

Markdown

 

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

Plain Text

 

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

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

במהלך השדרוג ל-MySQL 8.0, המשתמשים ראו ירידה משמעותית בביצועים של הכנסת המון, ואחת הסיבות העיקריות הייתה השינוי המשמעותי במבני נתונים הבסיסיים.

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

בואו נבחן את פעולת ה-execute היסודית של העקיבה MTR ב-MySQL 5.7.44:

C++

 

בואו נבחן את פעולת ה־execute העיקרית של MTR commit ב־MySQL 8.0.40:

C++

 

בהשוואה, ברור שב־MySQL 8.0.40, פעולת ה־execute ב־MTR commit הפכה הרבה יותר מורכבת, עם יותר שלבים מעורפלים. פיקוח זהו אחד הסיבות העיקריות לירידה בביצועים של כתיבה בנמוכה-צידיות.

במיוחד, הפעולות m_impl->m_log.for_each_block(write_log) ו־log_wait_for_space_in_log_recent_closed(*log_sys, handle.start_lsn) נושאות עליות משמעותיות. שינויים אלו נעשו כדי לשפר ביצועי צריבה בגבוה-צידיות, אך הם הגיעו על חשבון ביצועי צריבה בנמוכה-צידיות.

העדיפות של הרישום שוב עבודת צריבה במצב גבוה-צידיות מובילה לביצועים רעים עבור עומסי עבודה בנמוכה-צידיות. אמנם ההכנסה של innodb_log_writer_threads הייתה כדי להקטין את בעיות ביצועי צריבה בנמוכה-צידיות, אך זה לא משפיע על ביצוע הפונקציות הנ"ל. מאז שהפעולות הללו הפכו יותר מורכבות ודורשות MTR commits תדירים, הביצועים ירדו באופן משמעותי.

בואו נסתכל על ההשפעה של יכולת ההוספה/הסרה המיידית על הביצועים. להלן הפונקציה rec_init_offsets_comp_ordinary ב־MySQL 5.7:

C++

 

הפונקציה rec_init_offsets_comp_ordinary ב־MySQL 8.0.40 היא כדלקמן:

C++

 

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

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

C++

 

לפי הבדיקות שלנו, ההצהרה ib::fatal מפריעה באופטימיזציית ההרחבה. לפונקציות בהן נגש בתדידות, מומלץ להימנע מהצהרות שמפריעות באופטימיזציית ההרחבה.

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

C++

 

הפונקציה row_sel_field_store_in_mysql_format בסופו של דבר קוראת ל־row_sel_field_store_in_mysql_format_func.

C++

 

הפונקציה row_sel_field_store_in_mysql_format_func אינה יכולה להיות מוכנסת לתוך שורה עקב ההופכות ib::fatal.

C++

 

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

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

GitHub Flavored Markdown

 

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

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

פתרונות להקלת ירידת הביצועים של MySQL

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

לצורך ירידת ביצועי הכנסת עכברוש, הגרסה הפתוחה שלנו [2] מחליפה את ה- deque הרשמי עם יישום רשימה משופר. זה מטרתו בעיקר לטפל בבעיות יעילות זיכרון ויכול לטפל חלקית בירידת ביצועים. על ידי שילוב שיפור PGO עם הגרסה הפתוחה שלנו, ביצועי הכנסת עכברוש יכולים לקרב לאלו של MySQL 5.7.

Figure 3. Optimized MySQL 8.0.40 with PGO performs roughly on par with version 5.7.

משתמשים יכולים גם לנצל תהליכי רקמה מרובים עבור עיבוד עכברוש מקביל, תוך תמיכה מלאה בקידום ה-Concurrency המשופר של רשומת ה- redo, שיכול לשפר באופן משמעותי את ביצועי הכנסת העכברוש.

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

לגבי ביצועי קריאה, במיוחד ביצועי join, עשינו שיפורים משמעותיים, כולל תיקון בעיות inline וביצוע שיפורים נוספים. עם הוספת PGO, ביצועי join יכולים להיגבר במעל 30% בהשוואה לגרסה הרשמית.

Figure 4. Using PGO, along with our optimizations, can lead to significant improvements in join performance.

אנו נמשיך להשקיע זמן באופטימיזציה של ביצועים בנמוך Concurrency. התהליך זה ארוך אך מעורב באזורים רבים הזקוקים לשיפור.

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

הפניות

[1] בין וואנג (2024). אומנות פתרון בהנדסת תוכנה: איך לשפר את MySQL.

[2] משופר עבור MySQL · GitHub

Source:
https://dzone.com/articles/mysql-80-performance-degradation-analysis