تصف هذه المقالة طريقة معالجة الدُفعات السطحية للبيانات الضخمة. شاركه للرجوع إليه ، على النحو التالي:
معالجة الدُفعات السباتية هي في الواقع غير مرغوب فيها من منظور الأداء وتضيع الكثير من الذاكرة. من آليتها ، يقوم السبات أولاً بفحص البيانات التي تلبي الشروط ، ويضعها في الذاكرة ، ثم تقوم بالعمليات. الأداء غير مرض للغاية في الاستخدام الفعلي. في استخدامي الفعلي ، يتم إدراج بيانات حل التحسين الثالث التالي: 100000 قطعة من البيانات في قاعدة البيانات ، والتي تستغرق حوالي 30 دقيقة. هاها ، باهتة. (قمت بإدراج 1000،000 قطعة من البيانات في 10 دقائق (الحقول صغيرة نسبيًا))
هناك ثلاث طرق للتعامل معها لحل مشاكل الأداء:
1: تجاوز واجهة برمجة تطبيقات السبات واستخدام واجهة برمجة تطبيقات JDBC مباشرة. هذه الطريقة لها أداء أفضل. إنه أيضًا الأسرع.
2: استخدام الإجراءات المخزنة.
3: استخدم واجهة برمجة تطبيقات Hibernate لإجراء معالجة الدُفعات العادية. يمكن أن تكون هناك تغييرات ، وسيتغير التغيير. عندما نجد مبلغًا معينًا ، يمكننا حذف البيانات في الوقت المناسب بعد الانتهاء من العملية ، session.flush () ؛ Session.Evict (مجموعة كائنات XX) ؛ هذا يمكن أن يوفر أيضا بعض خسائر الأداء. يجب استخدام "كمية معينة" كمرجع كمي على أساس الظروف الفعلية. بشكل عام حوالي 30-60 ، ولكن التأثير لا يزال غير مثالي.
1: تجاوز واجهة برمجة تطبيقات السبات والقيام بذلك مباشرة من خلال واجهة برمجة تطبيقات JDBC. هذه الطريقة لها أداء أفضل وأسرع. (المثال هو عملية تحديث)
المعاملة tx = session.begintransaction () ؛ // لاحظ أنك تستخدم اتصال حدود المعاملات السطحية connt conn = session.connection () ؛ reparedStatement STMT = conn.preparedStatement ("تحديث العميل كـ C C.Sarlary = C.Sarlary+1 حيث c.sarlary> 1000") ؛ stmt.excuteupdate () ؛ tx.Commit () ؛ // لاحظ أنك تستخدم حدود معاملات السبات في هذا التطبيق ، يستخدم واجهة برمجة التطبيقات التي تستدعي JDBC مباشرة للوصول إلى قاعدة البيانات ، وهي فعالة للغاية. تجنب مشاكل الأداء الناجمة عن السبات أولاً في الاستعلام والتحميل في الذاكرة ، ثم إجراء العمليات.
2: استخدام الإجراءات المخزنة. ومع ذلك ، لا ينصح بهذه الطريقة بسبب راحة قابلية النقل ونشر البرنامج. (المثال هو عملية تحديث)
إذا كانت قاعدة البيانات الأساسية (مثل Oracle) تدعم الإجراءات المخزنة ، فيمكن أيضًا إجراء تحديثات الدُفعات من خلال الإجراءات المخزنة. يتم تشغيل الإجراءات المخزنة مباشرة في قاعدة البيانات ، بشكل أسرع. في قاعدة بيانات Oracle ، يمكن تعريف الإجراء المخزن المسمى BatchupDateCustomer () ، والرمز كما يلي:
نسخة التعليمات البرمجية كما يلي: إنشاء أو استبدال الإجراء BatchupDateCustomer (p_age في الرقم) كما تبدأ تحديث العملاء تعيين العمر = العمر+1 حيث العمر> p_age ؛ النهاية ؛
يحتوي الإجراء المخزن أعلاه على معلمة p_age ، والتي تمثل عمر العميل. يمكن للتطبيق استدعاء الإجراء المخزن بالطرق التالية:
tx = session.begintransaction () ؛ connection con = session.connection () ؛ string procedure = "{call batchupdatecustomer (؟)}" // قم بتعيين المعلمة العمرية على 0cstmt.executeupdate () ؛ tx.commit () ؛كما يتضح من البرنامج أعلاه ، يجب أن يتجاوز التطبيق أيضًا واجهة برمجة تطبيقات السبات والاتصال مباشرة بالإجراءات المخزنة من خلال واجهة برمجة تطبيقات JDBC.
3: استخدم واجهة برمجة تطبيقات Hibernate لإجراء معالجة الدُفعات العادية. يمكن أن تكون هناك تغييرات ، وسيتغير التغيير. عندما نجد مبلغًا معينًا ، يمكننا حذف البيانات في الوقت المناسب بعد الانتهاء من العملية ، session.flush () ؛ Session.Evict (مجموعة كائنات XX) ؛ هذا يمكن أن يوفر أيضا بعض خسائر الأداء. يجب أن يكون هذا "كمية معينة" مرجعًا كميًا يعتمد على الظروف الفعلية ...
(المثال هو عملية حفظ)
منطق العمل هو: نريد إدراج 10 0000 قطعة من البيانات في قاعدة البيانات
tx = session.begintransactaction () ؛ for (int i = 0 ؛ i <100000 ؛ i ++) {custome custom = new customer () ؛ custom.setName ("user"+i) ؛ session.save (custom) ؛ if (i ٪ 50 == 0)هذا سيبقي النظام في نطاق مستقر ...
أثناء عملية تطوير المشروع ، بسبب متطلبات المشروع ، نحتاج غالبًا إلى إدراج كميات كبيرة من البيانات في قاعدة البيانات. هناك عشرات الآلاف ، عشرات الآلاف ، عشرات الملايين ، وحتى عشرات الملايين منهم. إذا كنت تستخدم السبات لإدخال بيانات من هذا المستوى من الحجم ، فقد يحدث استثناء. الاستثناء الشائع هو OutofMemoryError (استثناء فيضل الذاكرة).
أولاً ، دعنا نراجع بإيجاز آلية عملية إدخال السبات. يحتاج السبات إلى الحفاظ على ذاكرة التخزين المؤقت الداخلية. عندما نقوم بإجراء عملية إدراج ، سنضع جميع الكائنات للعمل في ذاكرة التخزين المؤقت الداخلية الخاصة بنا للإدارة.
عندما يتعلق الأمر بذاكرة التخزين المؤقت لـ Hibernate ، فإن السبات له نظريات ذاكرة التخزين المؤقت الداخلية وذاكرة التخزين المؤقت الثانوية. نظرًا لأن السبات يحتوي على آليات إدارة مختلفة لهذين التخزين المؤقت ، يمكننا تكوين حجمها فيما يتعلق بتذاكر ذاكرة التخزين المؤقت الثانوية ، بينما بالنسبة للذاكرة التخزين المؤقت الداخلية ، تعتمد السبات على موقف "تدفق المقود" ، ولا يوجد حد على قدرته. الآن تم العثور على جوهر المشكلة. عندما نقوم بإدراج بيانات ضخمة ، سيتم تضمين العديد من الكائنات في ذاكرة التخزين المؤقت الداخلية (يتم تخزين ذاكرة التخزين المؤقت الداخلية في الذاكرة) ، بحيث يتم تناول ذاكرة النظام الخاصة بك شيئًا فشيئًا. إذا كان النظام "مقليًا" أخيرًا ، فهو معقول.
دعونا نفكر في كيفية التعامل مع هذه المشكلة بشكل أفضل؟ يجب التعامل مع بعض ظروف التطوير باستخدام السبات ، وبالطبع بعض المشاريع أكثر مرونة ويمكنك العثور على طرق أخرى.
هنا أوصي بطريقتين:
(1): تحسين السبات ، واستخدم طريقة الإدراج المجزأة لمسح ذاكرة التخزين المؤقت في الوقت المناسب على البرنامج.
(2): تجاوز واجهة برمجة تطبيقات السبات وإدراج الدُفعات مباشرة من خلال واجهة برمجة تطبيقات JDBC. هذه الطريقة لديها أفضل أداء وأسرع.
بالنسبة للطريقة 1 أعلاه ، فإن الفكرة الأساسية هي: تحسين Hibernate ، قم بتعيين المعلمة hibernate.jdbc.batch_size في ملف التكوين لتحديد عدد SQL المقدم في كل مرة ؛ يستخدم البرنامج طريقة مسح ذاكرة التخزين المؤقت في الوقت المناسب في الإدراج المجزأة (تنفذ الجلسة من الكتابة غير المتزامنة ، والتي تسمح لـ Hibernate بكتابة العمليات الصريحة) ، أي مسحها من ذاكرة التخزين المؤقت الداخلية في الوقت المناسب بعد إدخال كمية معينة من البيانات ، وتحديد الذاكرة المحتلة.
لتعيين المعلمة hibernate.jdbc.batch_size ، يمكنك الرجوع إلى التكوين التالي.
<Hibernate-Configuration> <Session-Factory> ... <property name = "hibernate.jdbc.batch_size"> 50 </property> ... <Session-Factory> <hibernate-configuration>
سبب تكوين المعلمة hibernate.jdbc.batch_size هو قراءة قاعدة البيانات بأقل قدر ممكن. كلما زادت قيمة المعلمة hibernate.jdbc.batch_size ، كلما قلت الأوقات التي تقرأ فيها قاعدة البيانات ، وأسرع السرعة. من التكوين أعلاه ، يمكن ملاحظة أن السبات ينتظر حتى يتراكم البرنامج 50 SQL قبل إرساله على دفعات.
يعتقد المؤلف أيضًا أن قيمة المعلمة hibernate.jdbc.batch_size قد لا يتم تعيينها بأكبر قدر ممكن ، ويبقى مناقشتها من منظور الأداء. هذا يتطلب النظر في الموقف الفعلي ووضعه حسب الاقتضاء. عمومًا ، يمكن أن يلتزم تحديد 30 أو 50 بالاحتياجات.
من حيث تنفيذ البرنامج ، يأخذ المؤلف إدخال 10000 قطعة من البيانات كمثال ،
جلسة الجلسة = hibernateutil.currentsession () ؛ transatcion tx = session.begintransaction () ؛ لـ (int i = 0 ؛ i <10000 ؛ i ++) {student st = new student () ؛ st.SetName ("feifei") ؛ setting.save (st) ؛ if (i ٪ 50 == 0) // استخدم كل بيانات 50 كبيانات للمعالجة {session. // الحفاظ على متزامن مع Data Data Session.clear () ؛ // قم بمسح جميع البيانات المخبأة داخليًا وأطلق الذاكرة المحتلة في الوقت}} tx.commit () ؛ ...تحت مقياس بيانات معين ، يمكن لهذا النهج الحفاظ على موارد ذاكرة النظام في نطاق مستقر نسبيًا.
ملاحظة: ذاكرة التخزين المؤقت من المستوى الثاني المذكورة سابقًا ضرورية بالنسبة لي أن أذكرها هنا. إذا تم تمكين ذاكرة التخزين المؤقت الثانوية ، من أجل الحفاظ على ذاكرة التخزين المؤقت الثانوية ، فسيقوم Hibernate بشحن البيانات المقابلة إلى ذاكرة التخزين المؤقت الثانوية عندما نقوم بإدراج العمليات وتحديثها وحذفها. سيكون هناك خسارة كبيرة في الأداء ، لذلك يوصي المؤلف بتخزين ذاكرة التخزين المؤقت من المستوى 2 في معالجة الدُفعات.
بالنسبة للطريقة 2 ، يتم استخدام معالجة دفعة JDBC التقليدية ويتم استخدام API JDBC لمعالجتها.
يرجى الرجوع إلى معالجة الدُفعات Java والتنفيذ الذاتي SQL.
عند النظر إلى الكود أعلاه ، هل تشعر دائمًا أن هناك شيئًا ما غير مناسب؟ نعم ، ألم تلاحظ ذلك! لا يزال هذا هو البرمجة التقليدية لـ JDBC ، دون نكهة السبات.
يمكن تعديل الرمز أعلاه إلى ما يلي:
المعاملة tx = session.begintransaction () ؛ // استخدم اتصال معالجة المعاملات السطحية conn = session.connection () ؛ إعداد STMT = CONN.PREPARESTATEMENT ("أدخل في قيم (الاسم) (الاسم) (؟)") ؛ لـ (int j = 0 ؛ j ++ ؛ j <200) {for (int i = 0 ؛ i ++ ؛ j <50) {stmt.SetString (1 ، "feifei") ؛}} stmt.executeupdate () ؛ tx.commit () ؛ // استخدم حدود معالجة معاملات السبات ...هذا التغيير سيكون له نكهة السبات. بعد الاختبار ، يستخدم المؤلف واجهة برمجة تطبيقات JDBC لمعالجة الدُفعات ، والتي تتجاوز الأداء 10 أضعاف ما يقرب من 10 مرات من استخدام API Hibernate. هذا هو بلا شك الأداء السائد لـ JDBC.
في تحديث الدُفعات وحذف Hibernate2 ، لعمليات تحديث الدُفعات ، يكتشف Hibernate البيانات التي تلبي المتطلبات ثم تنفيذ عملية التحديث. وينطبق الشيء نفسه على حذف الدُفعات. اكتشف أولاً البيانات التي تلبي الشروط ، ثم قم بتنفيذ عملية الحذف.
هذا له عيوب رئيسية:
(1): يأخذ الكثير من الذاكرة.
(2): عند معالجة البيانات الضخمة ، يعد تنفيذ بيان التحديث/الحذف مبلغًا ضخمًا ، ويمكن لبيان التحديث/الحذف تشغيل كائن واحد فقط. من المتصور أن يكون أداء قاعدة البيانات منخفضًا إذا تم تشغيله بشكل متكرر.
بعد إصدار Hibernate3 ، تم تقديم تحديث/حذف بالجملة لعمليات تحديث الدُفعات/الحذف. المبدأ هو إكمال عمليات التحديث/الحذف الدُفعات من خلال عبارة HQL ، والتي تشبه إلى حد كبير عمليات تحديث/حذف الدُفعات من JDBC. من حيث الأداء ، هناك تحسن كبير على تحديثات الدُفعات/حذف Hibernate2.
المعاملة tx = session.beginsession () ؛ String HQL = "Delete Student" ؛ Query Query = Session.Createquery (HQL) ؛ int size = query.executeupdate () ؛ tx.Commit () ؛ ... ...
تقوم وحدة التحكم بإخراج بيان حذف واحد فقط: حذف من T_Student. تنفيذ البيان أقل ، والأداء هو نفسه تقريبًا باستخدام JDBC. إنها طريقة جيدة لتحسين الأداء. بالطبع ، من أجل الحصول على أداء أفضل ، يوصي المؤلف بأن تحديثات الدُفعات وعمليات الحذف لا تزال تستخدم JDBC. الأساليب ونقاط المعرفة الأساسية هي في الأساس نفس طريقة إدخال الدُفعات أعلاه 2 ، لذلك لن أصفها بشكل متكرر هنا.
أنا هنا أقدم طريقة أخرى ، وهي التفكير في تحسين الأداء من جانب قاعدة البيانات وإجراءات المكالمات المخزنة على جانب برنامج السبات. الإجراءات المخزنة تعمل على جانب قاعدة البيانات ، أسرع. أخذ تحديثات الدُفعات كمثال ، يتم تقديم رمز المرجع.
أولاً ، قم بإنشاء إجراء مخزن اسمه BatchupDateStudent على جانب قاعدة البيانات:
قم بإنشاء أو استبدال إنتاج BatchupDateStudent (A in Number) Asbeginupdate Set Set Age = Age+1 حيث العمر> A ؛ النهاية ؛
رمز الاتصال كما يلي:
المعاملة tx = session.beginsession () ؛ connection conn = session.connection () ؛ String pd = "... {call batchupDateStudent (؟)}" // قم بتعيين المعلمة العمرية إلى 20tx.commit () ؛مراقبة الكود أعلاه ، فإنه يتجاوز أيضًا واجهة برمجة تطبيقات Hibernate ويستخدم API JDBC لاستدعاء الإجراءات المخزنة ، ويستخدم حدود معاملات Hibernate. لا شك أن الإجراءات المخزنة هي وسيلة جيدة لتحسين أداء معالجة الدُفعات. تعمل مباشرة مع جانب قاعدة البيانات ، وإلى حد ما نقل ضغط معالجة الدُفعات إلى قاعدة البيانات.
PostScript
تناقش هذه المقالة عمليات معالجة الدُفعات الخاصة بـ Hibernate ، ونقطة البداية هي النظر في تحسين الأداء ، وهي توفر فقط جانبًا صغيرًا من تحسين الأداء.
بغض النظر عن الطريقة التي تم تبنيها ، يجب النظر فيها بناءً على الظروف الفعلية. إن تزويد المستخدمين بنظام فعال ومستقر يلبي احتياجاتهم هو الأولوية القصوى.
آمل أن تكون هذه المقالة مفيدة لبرمجة السبات للجميع.