Как предварительно выбрать идентификаторы последовательности Oracle в распределенной среде - PullRequest
6 голосов
/ 04 сентября 2008

У меня есть распределенное приложение Java, работающее на 5 серверах приложений. Все серверы используют одну и ту же базу данных Oracle 9i, запущенную на 6-м компьютере.

Приложение должно предварительно выбрать пакет из 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)

  • Решение от Мэтью работает, когда вы можете использовать фиксированное значение приращения (что вполне приемлемо в моем случае). Однако есть ли решение, когда вы не хотите фиксировать размер приращения, но хотите настроить его динамически?

Ответы [ 4 ]

11 голосов
/ 04 сентября 2008

Почему бы просто не иметь последовательность с шагом в 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> 
3 голосов
/ 04 сентября 2008

Зачем вам нужно получать идентификаторы последовательности в первую очередь? В большинстве случаев вы вставляете в таблицу и возвращаете идентификатор.

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

Похоже, вы пытаетесь предварительно оптимизировать обработку.

Если вам ДЕЙСТВИТЕЛЬНО нужно предварительно выбрать идентификаторы, просто вызовите последовательность 100 раз. Весь смысл последовательности состоит в том, что она управляет нумерацией. Вы не должны предполагать, что вы можете получить 100 последовательных чисел.

1 голос
/ 05 сентября 2008

Если вы не хотите фиксированного приращения размера, последовательности не совсем то, что вам нужно, все, что они действительно гарантируют, это то, что вы будете получать уникальный номер всегда больше, чем последний, который вы получили. Всегда есть вероятность, что у вас будут пробелы, и вы не сможете реально и безопасно отрегулировать величину прироста на лету.

Я не могу вспомнить ни одного случая, когда мне приходилось делать подобные вещи, но, вероятно, самый простой способ - просто сохранить где-нибудь "текущий" номер и обновить его по мере необходимости.

Как то так.

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> 
1 голос
/ 04 сентября 2008

Мэтью имеет правильный подход здесь. На мой взгляд, очень необычно для приложения сбрасывать текущее значение последовательности после каждого использования. Гораздо более привычно устанавливать размер приращения в соответствии с тем, что вам нужно заранее.

Кроме того, этот способ намного эффективнее. Выбор nextval из последовательности является высокооптимизированной операцией в Oracle, тогда как запуск ddl для изменения последовательности намного дороже.

Полагаю, это не отвечает последнему пункту вашего отредактированного вопроса ...

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...