Как выполнить предварительную выборку идентификаторов последовательности Oracle в распределенной среде

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

  •  09-06-2019
  •  | 
  •  

Вопрос

У меня есть распределенное 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)

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

Это было полезно?

Решение

Почему бы просто не увеличивать последовательность на 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