אופטימיזציה של חיבור
חיבורים למסדי נתונים הם משאבים יקרים. כל חיבור צורך זיכרון שרת, דורש עומס אימות וסופר במגבלות השרת. ליישומי בינה מלאכותית שמבצעים שאילתות וקטוריות תכופות, ניהול חיבור יעיל הוא חיוני להשגת תפוקה גבוהה מבלי למצות משאבים.
הערה
דוגמאות קוד ביחידה זו מדגימות דפוסי ניהול חיבורים עבור פייתון (psycopg) ו-.NET (Npgsql). ספריות אלו מתעדכנות לעיתים קרובות. בקרו בתיעוד של psycopgובתיעוד Npgsql לפרטים עדכניים על ה-API.
עומס חיבור
יצירת חיבור PostgreSQL חדש כוללת מספר שלבים, שכל אחד מהם מוסיף השהיה:
- לחיצת יד של TCP: הקמת חיבור הרשת (בדרך כלל 1-3 נסיעות הלוך-חזור)
- משא ומתן TLS: הצפנת החיבור (נדרש למסד הנתונים של Azure עבור PostgreSQL)
- אימות: אימות אישורים (החלפת סיסמאות או טוקנים)
- הקצאת תהליכי שרת: PostgreSQL יוצר תהליך backend לכל חיבור
- אתחול הסשן: הגדרת פרמטרי סשן ותצורות טעינה
רצף זה לוקח 50-200 מילישניות, תלוי בהשהיית הרשת ובעומס השרת. עבור מנוע המלצה שמטפל באלפי בקשות בשנייה, יצירת חיבורים חדשים לכל בקשה תדרוך יותר זמן בהגדרת החיבור מאשר בביצוע השאילתות בפועל.
Azure Database for PostgreSQL מגביל חיבורים מקבילים לפי שכבת המחשוב. Burstable B1ms מאפשר 50 חיבורים, General Purpose 2 vCores מאפשר 859 חיבורים, General Purpose 4 vCores מאפשר 1,718 חיבורים, Memory Optimized 4 vCores מאפשר 3,437 חיבורים, ו-Memory Optimized 16 vCores מאפשר 5,000 חיבורים. חריגה מהגבולות הללו גורמת לכשלים בחיבור. יישומים שיוצרים חיבורים לפי בקשה יכולים להגיע במהירות למגבלות אלו במהלך קפיצות תנועה.
איגוד חיבורים עם PgBouncer
PgBouncer הוא מאגר חיבורים קל משקל שנמצא בין האפליקציה שלך ל-PostgreSQL. הוא שומר מאגר של חיבורי מסד נתונים ומולטיפלקס חיבורי לקוחות ביניהם, מה שמפחית משמעותית את מספר החיבורים הנדרשים בפועל.
Azure Database for PostgreSQL כולל תמיכה מובנית ב-PgBouncer בשכבות חישוב כלליות ו-Memory Optimized. רמת Burstable לא תומכת בתכונה הזו. הפעל PgBouncer דרך פורטל Azure או CLI. לאחר הפעלה, התחבר דרך פורט 6432 (פורט PgBouncer) במקום 5432 (פורט PostgreSQL ישיר). מחרוזת החיבור של PgBouncer משתמשת postgresql://user:password@myserver.postgres.database.azure.com:6432/mydbב-.
az postgres flexible-server parameter set \
--resource-group myResourceGroup \
--server-name myserver \
--name pgbouncer.enabled \
--value true
PgBouncer תומך בשלושה מצבי איגוד, שלכל אחד מהם יתרונות שונים. מצב סשן פירושו שלקוח מחזיק חיבור שרת לאורך כל הסשן (עד לניתוק). מצב זה תומך בכל תכונות PostgreSQL אך מספק הפחתת חיבור מינימלית. מצב עסקה פירושו שלקוח מחזיק חיבור שרת רק במהלך העסקה. בין העסקאות, החיבור חוזר לבריכה. מצב זה עובד היטב ברוב היישומים ומפחית משמעותית את דרישות החיבור. מצב משפט אומר שלקוח מקבל חיבור רק עבור פקודות בודדות. מצב זה מספק הפחתת חיבור מרבית אך אינו תומך בעסקאות מרובות דוחות. בעומסי עבודה בחיפוש וקטורי, מצב עסקה הוא בדרך כלל הבחירה הטובה ביותר.
PgBouncer חושף מספר פרמטרים השולטים בהתנהגות הבריכה, מגבלות החיבור וטיפול ב-timeout. בעומסי עבודה בחיפוש וקטורי עם דפוסי תנועה מתפרצים, כוונון פרמטרים אלו מסייע לאזן בין זמינות החיבור לצריכת המשאבים. להגדיר pgbouncer.default_pool_size (20-50 בהתאם לצרכי החפיפות), pgbouncer.max_client_conn (5000+ ליישומים עם תנועה גבוהה), pgbouncer.pool_mode (עסקאות), ו pgbouncer.query_wait_timeout -(30-120 שניות).
מצב העסקה מחזיר חיבורים לבריכה לאחר שכל עסקה מתחייבת או מתגלגלת אחורה. דבר זה משפיע על מספר תכונות PostgreSQL. משתני הסשן מתאפסים בין העסקאות, כך שההגדרות שמופעלות עם SET לא נשארות בין העסקאות. השתמש SET LOCAL בתוך עסקאות או הגדר ברירת מחדל בצד השרת. הצהרות מוכנות עשויות לא לעבוד כי הצהרות מוכנות בשם קשורות לקשרים. במצב עסקה, הצהרה מוכנה שנוצרה בעסקה אחת עשויה שלא להיות זמינה בעסקה הבאה אם יוקצה חיבור שונה. LISTEN/NOTIFY לא עובד כי תכונות אלו דורשות חיבורים מתמשכים ואינן תואמות לבריכת עסקאות. ביישומי חיפוש וקטורי, מגבלות אלו לעיתים נדירות בעייתיות מכיוון ששאילתות הן בדרך כלל בחירות פשוטות ללא מצב ספציפי לסשן.
איגוד חיבורים ברמת היישום
בנוסף ל-PgBouncer (או במקום), האפליקציה שלך יכולה לנהל בריכות חיבורים ישירות. זה מספק שליטה מדויקת יותר על מחזור חיי החיבור ומשתלב עם מסגרות יישומים.
החבילה psycopg_pool מספקת איגוד חיבורים ל-psycopg. בריכות ברמת האפליקציה נותנות לך שליטה על מחזור חיי החיבור, התנהגות פסק זמן במצב סרק ובדיקת בריאות. הם גם משתלבים באופן טבעי עם טיפול השגיאות והתיעוד של האפליקציה שלך. בשילוב עם PgBouncer, מאגרי היישומים מטפלים בניהול חיבורים מקומיים בעוד ש-PgBouncer מטפל בריבוב בצד השרת.
with pool.connection() מנהל ההקשר מחזיר אוטומטית את החיבור למאגר כאשר הבלוק יוצא, גם אם מתרחשת חריגה.
from psycopg_pool import ConnectionPool
# Create a connection pool
pool = ConnectionPool(
conninfo="postgresql://user:password@myserver.postgres.database.azure.com:6432/mydb",
min_size=5, # Minimum connections to maintain
max_size=20, # Maximum connections allowed
max_idle=300, # Close idle connections after 5 minutes
max_lifetime=3600 # Recycle connections after 1 hour
)
# Use connections from the pool
def search_similar_products(query_embedding, limit=10):
with pool.connection() as conn:
with conn.cursor() as cur:
cur.execute("""
SELECT id, name, embedding <=> %s AS distance
FROM products
ORDER BY embedding <=> %s
LIMIT %s
""", (query_embedding, query_embedding, limit))
return cur.fetchall()
Npgsql כוללת איגוד חיבורים מובנה שמופעל כברירת מחדל, כך שאין צורך בחבילה נפרדת. המאגר מנהל אוטומטית את יצירת הקשר, השימוש החוזר וההשלכה בהתבסס על פרמטרים שאתה מציין במחרוזת החיבור. כל מחרוזת חיבור ייחודית שומרת על מאגר משלה, כך שמחרוזות חיבור עקביות לאורך כל האפליקציה שלך מבטיחות ניצול יעיל של המאגר. כשאתה מתקשר conn.Close() או שהחיבור מתבטל, הוא חוזר לבריכה במקום להיהרס. הגדר איגוד דרך פרמטרים של מחרוזות חיבור כמו Minimum Pool Size=5;Maximum Pool Size=20;Connection Idle Lifetime=300;Connection Lifetime=3600.
גודל המאגר משפיע הן על הביצועים והן על צריכת המשאבים. הגדרת המאגר קטן מדי גורמת לבקשות להמתין לחיבורים זמינים, מה שמעלה את ההשהייה. הגדרה גדולה מדי מבזבזת זיכרון ועלולה להעמיס על שרת מסד הנתונים. הגודל הנכון תלוי בדפוסי התעבורה שלך, משך השאילתה, ובמספר מופעי היישומים שמשתפים את מסד הנתונים. שמור על גודל מינימלי גדול מספיק כדי להתמודד עם תנועה בסיסית בלי לחכות. הגבלת גודל המקסימלי במה שמסד הנתונים יכול להתמודד חלקי מספר מופעי היישום. אם יש לך 10 מופעי יישום והמסד נתונים שלך תומך ב-1,000 חיבורים, הגדר את המקסימום ל-100 לכל מופע (משאיר את ה-headroom). מחזר חיבורים מעת לעת (כל 30-60 דקות) כדי לשמור על בריאות, כי חיבורים ארוכי טווח עלולים להצטבר דליפות זיכרון או להחזיק תוכניות מיושנות במטמון.
ניהול סשנים עבור עומסי עבודה בינה מלאכותית
חלק מהשאילתות הווקטוריות נהנות מהגדרות ברמת סשן שמקצות יותר משאבים לשאילתה ממה שברירת המחדל של השרת מאפשרת.
שאילתות דמיון וקטורי שממיין מערכי תוצאות גדולים נהנות מהגברת work_mem. הגדר אותו לסשנים או עסקאות ספציפיות באמצעות SET LOCAL work_mem = '256MB'.
SET LOCAL חל רק בתוך העסקה הנוכחית. כאשר העסקה מסתיימת, ההגדרה חוזרת לברירת המחדל, שהיא בטוחה לחיבורים משותפים.
כוון hnsw.ef_search או ivfflat.probes עבור שאילתות עם דרישות דיוק שונות. משתמשים SET LOCAL hnsw.ef_search = 200 בשאריות גבוהות יותר בשאילתות שבהן הדיוק קריטי, או SET LOCAL hnsw.ef_search = 20 לשאילתות מהירות יותר שבהן תוצאות משוערות מקובלות מתקבלות. התבנית הזו מאפשרת לך לאזן בין דיוק למהירות לפי מקרה השימוש הספציפי מבלי להשפיע על שאילתות אחרות.
דפוסי שימוש יעילים ב-SDK
מעבר לניהול חיבורים, האופן שבו אתה מבנה אינטראקציות עם מסדי נתונים משפיע על הביצועים.
נסיעות הלוך ושוב ברשת מוסיפות השהיה לכל פעולת מסד נתונים. כשצריך מספר פריטי נתונים, שליחתם בשאילתה אחת מבטלת את העומס לכל שאילתה של העברת רשת, ניתוח שאילתות וסיריאליזציה של תוצאות. עבור יישומי בינה מלאכותית שאוספים הטבעות עבור מספר פריטים, האצווה יכולה להפחית את ההשהיה הכוללת ממאות מילישניות לספרות בודדות. במקום לבצע מספר נסיעות הלוך-חזור עם שאילתות בודדות, השתמש בשאילתה אחת והעבר WHERE id = ANY(%s) רשימת מזהים.
לטעינת מספר רב של וקטורים, פקודת PostgreSQL COPY מהירה בהרבה מהפקודות בודדות INSERT .
COPY מזרימה נתונים ישירות לטבלה בפורמט בינארי או טקסט, תוך עקיפת העומס של ניתוח פקודות SQL בודדות. בעת טעינת הטמעת נתונים מצינורות עיבוד אצווה או הגירות נתונים ראשוניות, COPY יכולה לקצר את זמני הטעינה משעות לדקות.
COPY ניתן לטעון מאות אלפי שורות בשנייה, בעוד שאינסרטים בודדים מוגבלים לאלפי שורות בשנייה.
כאשר היישום שלך יכול למקביל את העבודה, פעולות מסד הנתונים האסינכרוניות משפרות את קצב התפוקה על ידי ביצוע מספר שאילתות בו-זמנית מבלי לחסום תהליכים. תבנית זו חשובה לעומסי עבודה של בינה מלאכותית שצריכים לחפש במספר אוספי וקטורים בו-זמנית או לשלב חיפוש וקטורי עם שליפת נתונים אחרים. בריכות אסינכרון מנהלות חיבורים ביעילות בין פעולות מקבילות תוך שמירה על מגבלות גודל המאגר. השתמש AsyncConnectionPool ב-psycopg_pool ובביצוע asyncio.gather מספר חיפושים במקביל.
עמידות לחיבור
בעיות רשת, הפעלה מחדש של שרת והחלפות עלולות להפריע לחיבורי מסד נתונים. יישומים חזקים מתמודדים עם זה בחן.
כשלים חולפים כמו נקודות רשת, איפוס חיבור ואי-זמינות קצרה של שרת במהלך תחזוקה הם בלתי נמנעים בסביבות ענן. הטמעת לוגיקת ניסיון חוזר עם חזרה אקספוננציאלית עוזרת לאפליקציה שלך להתאושש בצורה אלגנטית מהבעיות הזמניות האלה מבלי להעמיס על השרת בניסיונות ניסיון חוזרים מיידיים. הוסיפו ג'יטר אקראי כדי למנוע ממספר מופעי יישום לנסות שוב בו-זמנית. תפוס OperationalError חריגים, חשב זמן המתנה כ (2 ** attempt) + random.uniform(0, 1)-, ונסה שוב עד למספר מקסימלי של ניסיונות.
פסקי זמן מונעים מהאפליקציה שלך להמתין ללא הגבלת זמן כאשר מסד הנתונים איטי או בלתי נגיש. פסקי זמן חיבור מגבילים את זמן ההמתנה בעת הקמת חיבורים חדשים, בעוד שזמני פקודות מגבילים את זמן ביצוע השאילתה. עבור יישומי חיפוש וקטורי, בחרו טיימ-אאוטים שמתאימים לשאילתות הלגיטימיות האיטיות ביותר שלכם תוך כישלון מהיר בשאילתות שעולות על השהייה המקובלת. הגדר פסק זמן במחרוזת החיבור שלך באמצעות פרמטרים כמו connect_timeout=10 ו options=-c statement_timeout=30000-. בשאילתות וקטוריות, קבע זמני פקודה שיתאימו לשאילתות האיטיות והמקובלות שלך. פסק זמן של 30 שניות סביר בחיפושי וקטור מורכבים; יישומים אינטראקטיביים עשויים להשתמש בערכים נמוכים יותר.
כאשר כל חיבורי הבריכה בשימוש ובקשות חדשות מגיעות, המאגר חייב או לתור בקשות (מה שמוסיף השהיה) או לדחות אותן מיד. אף אחת מהאפשרויות אינה אידיאלית, ולכן ניטור ניצול הבריכות עוזר לך להתרחב לפני שהעייפות הופכת לתדירה. כאשר מתרחשת תשייה, החזרת הודעת שגיאה ברורה עוזרת ללקוחות ליישם את לוגיקת הניסיון מחדש שלהם במקום להסתיים באופן בלתי צפוי. טפל PoolTimeout בחריגים על ידי החזרת שגיאה אלגנטית כמו {"error": "Service temporarily busy, please retry"}. עקוב אחרי ניצול הבריכות וגדל אם התשה מתרחשת לעיתים קרובות.