שאלה עקרונית ב COM עם MTS

שאלה עקרונית ב COM עם MTS

אני מעוניין לבצע הרבה שאילתות insert למסד הנתונים בבת אחת, ולעטוף הכל בטרנזקציה. סביבת הפיתוח שלי היא VB6, אבל השאלה תקפה גם ל VCPP6, למיטב הבנתי. לצורך העניין, נניח שאני רוצה להוסיף 1000 לקוחות חדשים בבת אחת. לשם כך עלי להשתמש באובייקט CCustomer . לפי מה שקראתי במאמרים על MTS ו COM או +COM, אני צריך ליצור בכל פעם instance של האובייקט CCustomer ולקרוא למתודה Insert, שנראית בערך כך:
Public Sub Insert(ByVal lCustId As Long, ByVal sFirstName As String, ByVal sLastName As String) Dim conTemp As ADODB.Connection On Error GoTo EH Set conTemp = New ADODB.Connection With conTemp .ConnectionString = "..." .Open .Execute "INSERT INTO CUSTOMERS ..." .Close End With Set conTemp = Nothing GetObjectContext.SetComplete Exit Sub EH: GetObjectContext.SetAbort If Not conTemp Is Nothing Then If conTemp.State <> adStateClosed Then conTemp.Close End If Set conTemp = Nothing End If Err.Raise ... End Sub​
ומה השאלה? בצורה הזו, אם אני מוסיף 1000 לקוחות, אז אני 1000 פעמים פותח וסוגר connection ל DB , כאשר במקום זאת הייתי יכול לפתוח רק connection אחד, ולהריץ לולאה של פקודות insert . אז מה נכון או מה עדיף? אני עדיין מעוניין לעבוד עם MTS (או +COM ), אבל אני לא רוצה שהשרת "יסבול" יותר מדי. אני יודע שהשרת עושה connection pooling, אבל זה נראה לי עדיין מיותר במקרה שכזה. אגב, אני מכיר את השיטה של יצירת טרנזקציה ע"י המתודות BeginTrans וכו' של ה connection, אבל זה לא ממש MTS . אז מה דעתכם? האם זוהי השיטה היחידה? איך אתם עובדים עם MTS כאשר אתם מעוניינים "לשפוך" פנימה הרבה נתונים בבת אחת? תודה מראש לכל העונים והעוזרים, רון
 

voguemaster

New member
לעבוד ב-PERL.... ../images/Emo3.gif

סתם סתם... אין לי ממש פיתרון עבורך.
 

כאגאי

New member
סליחה על הבורות, אבל למה אתה לא

יכול להשאיר את ה CONNECTION חי, כמשתנה גלובאלי, או פרטי באובייקט שלך, ולהשתמש בו כל הזמן? כך תוכל לממש את זה כפי שאמרת ע"י לולאות? פתרון אחר, זה אולי ע"י STORED PROCEDURE שיוגדרו במסד נתונים שלך (עדיין תצטרך לשמור על ה CONNECTION אבל אפשר אולי לשלוח 2 אנשים כל פעם, או לקבל את הנתונים מטבלה אחרת (בקוד במסד נתונים)).
 
תגובה

קודם כל, תודה על תגובתך. לענייננו: במאמרים שקראתי על עבודה מול DB במסגרת של +COM , יש הנחיה גורפת של לפתוח connection , להוסיף רשומה, לסגור אותו, ובסוף לבצע SetComplete (או SetAbort אם משהו השתבש). יתכן שזו מגבלה של העבודה במסגרת של +COM , וזה למעשה מה שניסיתי לברר כאן. כל הרעיון של להשאיר את ה connection חי ולבצע הרבה insertים, לא זוכה לאהדה רבה במסגרת העבודה עם +COM , ולא מספיק ברור לי למה. אני בהחלט מבין את הרעיון של לשלוח יותר מאיש אחד בכל פעם (נניח 2 לקוחות או אפילו 4 או 8), אבל זה לא עונה לי על השאלה המקורית. הדבר היחיד שכרגע עולה לי בראש זה לבצע Serialize לנתונים, לשלוח אותם כ string לאיזה SP , ושם לבצע Deserialize (למעשה זה יכול להיות Extended SP ). שוב, תודה מראש לכל תגובה ולכל התייחסות, רון
 

vinney

Well-known member
BULK INSERT

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

תודה על תגובתך. אני עובד מול MSSQL2000. לפעמים מול MySQL, אבל בינתיים לא מול אורקל. אבל בכ"ז אבדוק את הנושא הזה במסדים הנ"ל, אולי משם תבוא הישועה. ואגב, ה DataEnvironment זה על הפנים לתכנות שאינו 2 שכבות בלבד, ולכן אני לא משתמש בזה. לדעתי הוא גורם ליותר באגים מאשר לנוחות. אבל אני כן משתמש בו כדי להפיק משפטי SQL במהירות... ושוב תודה לכל מי שעוזר ומתייחס, רון
 
תגובה נוספת

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

כאגאי

New member
אהמ, אז אתה יכול לקרוא

לפונקציה ()SQLSetConnectOption בשביל להיות ב autocommit mode, כך הוא לא יכניס כל פעולה אחת, אלא מחכה עד שיש לו כמות מסויימת, ואז עושה הכל בבת אחת. מ MSDN:
Using Transactions When you execute multiple Insert, Delete, or Update statements, performance can be enhanced by turning autocommit mode off. Do this by calling SQLSetConnectOption() on the connection handle (HDBC). Performance is enhanced when not in the autocommit mode because, in the autocommit mode, the driver must ensure that each individual Insert, Delete, or Update statement is flushed to the safe store (usually disk). If these statements are grouped in a transaction, the driver batches the disk writes and does them only once at commit time. Because disk I/O takes a relatively long time, turning autocommit mode off enhances performance​
 

vinney

Well-known member
שוב

אני לא יודע איך הגעת לקובץ/סטרינג. אני לא מכיר את מסדי הנתונים שאתה עובד איתם, אני יודע שבאוראקל מה שאפשר לעשות זה מערך נתונים לכל עמודה של INSERT (יוצא לך מספר מערכים שונים, בגודל זהה של מס' שורות להכנסה, כל מערך לשדה אחר בשורה המוכנסת), ואז להעביר את המערך כפרמטר ל INSERT (אתה יודע איך משתמשים בפרמטרים בSQL, נכון? יש את זה בכל מסד נתונים). מבחינתך זה יהיה שקוף איך הדרייבר יבצע את הפעולה וכמה כתיבות לדיסק בפועל הוא יעשה, והוא יעשה את זה משיקולי performance שלו. לדעתי זה הפתרון הכי יעיל.
 

ארזירון

New member
לעניות דעתי...

אני אתחיל בהבהרות למי שרוצה קצת רקע: +COM או MTS בשמו הישן, זה מערכת של ניהול אפליקציות בערך. כלומר אין (או יותר נכון לא אמורה להיות) התייחסות אחת למשתמש אחד, אלא התייחסות אחת למערכת אחת. למי שהעיר לגבי משתנה גלובלי, פה הבעיה טמונה, כיון שאין משתנה גלובלי למשתמש, אלא למערכת מרובת משתמשים, ואז משתמש אחד יכול ליצור בעיות עבור משתמש אחר באותה מערכת. כיון שהמערכת הזו היא מבוססת אפליקציה, כל פעולה קוראת בתוך טרנזאקציה אטומית, כדי למנוע "הפרעות" של משתמש אחד למשתמש אחר, כאשר כל הפעולות הן במצב של auto commit במקרה של מחסור בשגיאות. אם יש שגיאה לא מטופלת אז מתרחש abort באופן אוטומטי. ההערה לגבי ניטרול המנגנון האוטומטי היא נכונה בהחלט, כיון שזה ישפר את יעילות המערכת אם היא לא בודקת אם צריך או לא לבצע commit. bulk insert זה פעולה שדורשת קובץ או בגרסאות היותר מתקדמות stream של נתונים. אם כותבים בסביבת dot net אפשר לכתוב את המחרוזת אל תוך stream (system.io.memorystream למשל), אולם יהיה יותר פשוט לייצר קובץ לדעתי אם אתה משתמש ב-VS6 על כליו השונים. והדרך להכנסת הרבה נתונים בבת אחת של בסיס נתונים לא אמורה להיות אחד אחד. כלומר אתה לא תקרא לפונקציה Insert של האובייקט שלך על כל נתון שאתה רוצה להכניס. מבחינתי זה שקול לעדכון בודד של שדה על כל עדכון של רשומה במקום לעדכן את כל השדות בפעם אחת עם update. במקרה של הכנסת נתונים גדולה, יש לך פונקציה שמעבדת נתונים על מנת להחליט מה נכנס ומה לא. הייתי ממליץ שהיא תיצור את הקובץ שישמש ל-BI למשל. דרך אחרת יכולה להיות יצירת טבלה זמנית ברמה של session (ב-SQL אומר עם # בתחילת שם הטבלה) הכנסת הנתונים לשם כדי לחסוך בדיקות אחרות שאולי מתבצעות על כל insert כגון trigers ו-referntial integrity, ואז עם insert into ... select לבצע את ההכנסה עצמה. הערות מסביב: ישנם פעולות שמוגרות כלא טרזאקטיביות, למשל bulk insert. כלומר שבמקרה שאתה שוקל לעבור למצב כזה, קח בחשבון שהטרזאקציה שלך לא קיימת מבחינת בסיס הנתונים. אני לא בטוח לגבי +COM, אבל מבחינת SQL מתבצע insert אחד שלא נרשם בלוג ולכן במקרה של קריסה במהלך הפעולה הבסיס נתונים לא תקין. את ה-connection שלך אתה אמור לסגור כל פעם, ולפתוח אותו מחדש ואל תדאג לשרת, הוא מתמודד. יש מקרי קצה שתלויים מאוד בעומסים (של כמות משתמשים לעומת כמות פעולות לכל משתמש) שבהם יכול להיות שאם באמת תשמור את ה-connection בעצמך פתוח באופן גלובאלי (באמצעות Shared Property) אז תקבל ביצועים טובים יותר. חשוב לזכור שאובייקטים ב-+COM אמורים להיות אטומיים. כלומר כל פעולה עומדת באופן עצמאי, ולא תלויה במאפיינים או קריאות קודמות. למשל אין תהליך login כזה או אחר שבו אתה שומר את שם המשתמש והסיסמא. הנתון אמור להיות מועבר בכל קריאה, והאובייקט לא נשמר חי אצל הלקוח, אלא נוצר - מבצע פעולה אחת - מת. לא נוצר בתחילת אפליקצית תחנת הקצה ומת כאשר היא נסגרת. מאמר טוב לקרוא בנושא מדבר על Connection Pooling ו-Session Pooling, ונמצא כאן.
 

ארזירון

New member
עוד משהו אחד

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

תודה על התגובה המפורטת. כמה התייחסויות: "דרך אחרת יכולה להיות יצירת טבלה זמנית..." -אבל אז עדיין אצטרך לבצע הרבה Insertים, גם אם לאחר פונקציה שתסנן נתונים לא מתאימים וכד', לא? (אני מסכים איתך בהחלט שמבחינת triggers ושאר ירקות, זה יעבוד יותר מהר באופן כללי, אבל עדיין זה נראה לי כמו הרבה מאוד Insertים). לגבי הרעיון של שמירת connection פתוח ושיתופו עם הרבה אובייקטים: -השאלה המהותית היא האם במקרה של RollBack (כלומר אם אחד מהאובייקטים ביצע SetAbort, למשל) ה MTS אכן יצליח לשחזר הכל למרות שהפעולות נעשו כולן דרך אותו connection. לא הצלחתי למצוא מספיק תיעוד לעניין הזה (בעיקר משום שברוב המאמרים ההתייחסות היא לאטומיות, כלומר, כפי שציינת: פותחים קישור, מבצעים פעולה אחת, וסוגרים את הקישור). ועוד באותו נושא של Bulk Insert, אני חושב שיש איזה property של ADO אשר מציין שיש ליצור ב SQLServer רישום log לכל רשומה שנכנסת, ולא רק לפעולה באופן כללי. כך שאולי יש אפשרות ליצור טרנזקציה כבר ברמת ה SQLServer. ושוב תודה על ההיענות והנכונות לעזור, גם לך, ארז, וגם לעונים עד עתה (כאגאי, vinney וגם חובב טניס מפורום בסיסי נתונים)
 
למעלה