كيفية الجلب المسبق لمعرفات تسلسل Oracle في بيئة موزعة

StackOverflow https://stackoverflow.com/questions/43808

  •  09-06-2019
  •  | 
  •  

سؤال

لدي تطبيق Java موزع يعمل على 5 خوادم تطبيقات.تستخدم جميع الخوادم نفس قاعدة بيانات Oracle 9i التي تعمل على الجهاز السادس.

يحتاج التطبيق إلى الجلب المسبق لمجموعة مكونة من 100 معرف من التسلسل.من السهل نسبيًا القيام بذلك في بيئة ذات ترابط واحد وغير موزعة، يمكنك فقط إصدار هذه الاستعلامات:

select seq.nextval from dual;
alter sequence seq increment by 100;
select seq.nextval from dual;

يقوم التحديد الأول بجلب معرف التسلسل الأول الذي يمكن للتطبيق استخدامه، بينما يقوم التحديد الثاني بإرجاع آخر معرف يمكن استخدامه.

تصبح الأمور أكثر إثارة للاهتمام في بيئة متعددة الخيوط.لا يمكنك التأكد من أن مؤشر الترابط الآخر قبل التحديد الثاني لا يزيد التسلسل بمقدار 100 مرة أخرى.يمكن حل هذه المشكلة عن طريق مزامنة الوصول على جانب Java - فأنت تسمح لسلسلة رسائل واحدة فقط بالبدء في جلب المعرفات في وقت واحد.

يصبح الموقف صعبًا حقًا عندما لا تتمكن من المزامنة لأن أجزاء من التطبيق لا تعمل على نفس JVM، ولا حتى على نفس الجهاز الفعلي.لقد وجدت بعض المراجع في المنتديات التي تفيد بأن الآخرين لديهم مشاكل في حل هذه المشكلة أيضًا، ولكن لم تكن أي من الإجابات فعالة حقًا ناهيك عن كونها معقولة.

هل يمكن للمجتمع أن يقدم حلاً لهذه المشكلة؟

بعض مزيد من المعلومات:

  • لا أستطيع حقًا اللعب بمستويات عزل المعاملات.أستخدم JPA وسيؤثر التغيير على التطبيق بأكمله، وليس فقط على استعلامات الجلب المسبق وهذا غير مقبول بالنسبة لي.
  • في PostgreSQL يمكنني القيام بما يلي:

    حدد setval('seq', nextval('seq') + n - 1 )

  • يعمل الحل الذي قدمه ماثيو عندما يمكنك استخدام قيمة زيادة ثابتة (وهو أمر مقبول تمامًا في حالتي).ومع ذلك، هل يوجد حل عندما لا ترغب في تثبيت حجم الزيادة، ولكنك تريد تعديلها ديناميكيًا؟

هل كانت مفيدة؟

المحلول

لماذا لا يكون التسلسل زيادة بمقدار 100 طوال الوقت؟يمنحك كل "nextval" 100 رقم تسلسلي للعمل معه

SQL> create sequence so_test start with 100 increment by 100 nocache;

Sequence created.

SQL> select so_test.nextval - 99 as first_seq, so_test.currval as last_seq from dual;

 FIRST_SEQ   LAST_SEQ
---------- ----------
         1        100

SQL> /

 FIRST_SEQ   LAST_SEQ
---------- ----------
       101        200

SQL> /

 FIRST_SEQ   LAST_SEQ
---------- ----------
       201        300

SQL> 

ملحوظة على مثالك..احذروا الـDDL..وسوف تنتج التزاما ضمنيا

مثال على الالتزام الذي أنتجته DDL

SQL> select * from xx;

no rows selected

SQL> insert into xx values ('x');

1 row created.

SQL> alter sequence so_test increment by 100;

Sequence altered.

SQL> rollback;

Rollback complete.

SQL> select * from xx;

Y
-----
x

SQL> 

نصائح أخرى

لماذا تحتاج إلى جلب معرفات التسلسل في المقام الأول؟في معظم الحالات، يمكنك إدراجه في جدول وإرجاع المعرف.

insert into t (my_pk, my_data) values (mysequence.nextval, :the_data)
returning my_pk into :the_pk;

يبدو أنك تحاول تحسين المعالجة مسبقًا.

إذا كنت بحاجة حقًا إلى جلب المعرفات مسبقًا، فما عليك سوى استدعاء التسلسل 100 مرة.بيت القصيد من التسلسل هو أنه يدير الترقيم.ليس من المفترض أن تفترض أنه يمكنك الحصول على 100 رقم متتالي.

ماثيو لديه النهج الصحيح هنا.في رأيي، من غير المعتاد أن يقوم أحد التطبيقات بإعادة تعيين القيمة الحالية للتسلسل بعد كل استخدام.أكثر تقليدية بكثير لضبط حجم الزيادة على كل ما تحتاجه مقدمًا.

كما أن هذه الطريقة أكثر أداءً.يعد تحديد nextval من التسلسل عملية محسنة للغاية في Oracle، في حين أن تشغيل ddl لتغيير التسلسل يكون أكثر تكلفة بكثير.

أعتقد أن هذا لا يجيب حقًا على النقطة الأخيرة في سؤالك المعدل ...

لأنه عندما لا ترغب في زيادة حجم ثابت، فإن التسلسلات ليست حقًا ما تبحث عنه، كل ما يضمنونه حقًا هو أنك ستحصل على رقم فريد دائمًا أكبر من الرقم الأخير الذي حصلت عليه.هناك دائمًا احتمال أن ينتهي بك الأمر بوجود فجوات، ولا يمكنك حقًا ضبط مقدار الزيادة بسرعة بأمان أو بفعالية.

لا أستطيع حقًا التفكير في أي حالة اضطررت فيها إلى القيام بهذا النوع من الأشياء، ولكن من المحتمل أن تكون الطريقة الأسهل هي تخزين الرقم "الحالي" في مكان ما وتحديثه حسب حاجتك إليه.

شيء من هذا القبيل.

drop table t_so_test;

create table t_so_test (curr_num number(10));

insert into t_so_test values (1);
create or replace procedure p_get_next_seq (inc IN NUMBER, v_next_seq OUT NUMBER) As
BEGIN
  update t_so_test set curr_num = curr_num + inc RETURNING curr_num into v_next_seq;
END;
/


SQL> var p number;
SQL> execute p_get_next_seq(100,:p);

PL/SQL procedure successfully completed.

SQL> print p;

         P
----------
       101

SQL> execute p_get_next_seq(10,:p);     

PL/SQL procedure successfully completed.

SQL> print p;

         P
----------
       111

SQL> execute p_get_next_seq(1000,:p);

PL/SQL procedure successfully completed.

SQL> print p;

         P
----------
      1111

SQL> 
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top