У меня есть распределенное приложение 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, даже на одной физической машине. Я нашел на форумах ссылки на то, что у других тоже есть проблемы с решением этой проблемы, но ни один из ответов на самом деле не работает, не говоря уже о том, что они разумны.
Может ли сообщество предложить решение этой проблемы?
Дополнительная информация:
В PostgreSQL я мог делать следующее:
SELECT 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>
У Мэтью здесь правильный подход. На мой взгляд, для приложения очень необычно сбрасывать текущее значение последовательности после каждого использования. Гораздо более условно заранее установить размер приращения, который вам нужен.
Кроме того, этот способ намного эффективнее. Выбор следующего значения из последовательности - это высокооптимизированная операция в Oracle, тогда как запуск ddl для изменения последовательности намного дороже.
Думаю, это не совсем ответ на последний пункт вашего отредактированного вопроса ...
Зачем вообще нужно получать идентификаторы последовательностей? В большинстве случаев вы вставляете в таблицу и возвращаете идентификатор.
insert into t (my_pk, my_data) values (mysequence.nextval, :the_data)
returning my_pk into :the_pk;
Похоже, вы пытаетесь предварительно оптимизировать обработку.
Если вам ДЕЙСТВИТЕЛЬНО нужно предварительно получить идентификаторы, просто вызовите последовательность 100 раз. Вся суть последовательности в том, что она управляет нумерацией. Вы не должны предполагать, что можете получить 100 последовательных чисел.
Поскольку, когда вам не нужно приращение фиксированного размера, последовательности на самом деле не то, что вам нужно, все, что они действительно гарантируют, - это то, что вы получите уникальный номер, всегда больше, чем последний, который у вас есть. Всегда существует вероятность того, что у вас появятся пробелы, и вы не сможете настроить величину приращения на лету безопасно или эффективно.
Я не могу вспомнить ни одного случая, когда мне приходилось бы делать такие вещи, но, вероятно, самый простой способ - просто сохранить где-нибудь «текущий» номер и обновлять его по мере необходимости.
Что-то вроде этого.
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>