לדלג לתוכן

איך Connection Pooling חוסך זמן יקר בכל בקשה למסד נתונים

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

למה חיבור חדש למסד נתונים כל כך יקר

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

הרעיון - להחזיק חיבורים מוכנים מראש

Connection pooling פותר את זה בעזרת רעיון פשוט - במקום לפתוח חיבור חדש בכל בקשה ולסגור אותו בסוף, האפליקציה מחזיקה בריכה (pool) של חיבורים שכבר פתוחים ומאומתים מראש, מוכנים לשימוש. כשמגיעה בקשה שצריכה לגשת למסד נתונים, האפליקציה פשוט "שואלת" חיבור פנוי מהבריכה, משתמשת בו לשאילתה, ומחזירה אותו בחזרה לבריכה בסיום - לא סוגרת אותו, רק משחררת אותו לשימוש הבא.

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

מה קורה כשכל החיבורים בבריכה תפוסים

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

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

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

למה זה נראה כמו פרט תשתיתי אבל הוא לא

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

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

יש לכם API שאיטי בגלל חיבורי מסד נתונים? שאלו בדיסקורד.

הצטרפו לקהילה בדיסקורד

לסיכום

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