6

我有一个在 5 个应用服务器上运行的分布式 Java 应用程序。这些服务器都使用在第 6 台机器上运行的相同 Oracle 9i 数据库。

应用程序需要从一个序列中预取一批 100 个 ID。在单线程、非分布式环境中执行此操作相对容易,您只需发出以下查询:

SELECT seq.nextval FROM dual;
ALTER SEQUENCE seq INCREMENT BY 100;
SELECT seq.nextval FROM dual;

第一个选择获取应用程序可以使用的第一个序列 ID,第二个选择返回最后一个可以使用的序列 ID。

在多线程环境中,事情变得更加有趣。您不能确定在第二次选择之前,另一个线程不会再次将序列增加 100。这个问题可以通过在 Java 端同步访问来解决——一次只让一个线程开始获取 ID。

当您无法同步时,情况会变得非常困难,因为应用程序的某些部分不在同一个 JVM 上运行,甚至不在同一个物理机器上。我在论坛上发现了一些参考资料,其他人也有解决这个问题的问题,但没有一个答案真的有效,更不用说是合理的了。

社区可以为这个问题提供解决方案吗?

更多信息:

  • 我真的不能玩事务隔离级别。我使用 JPA,更改会影响整个应用程序,而不仅仅是预取查询,这对我来说是不可接受的。
  • 在 PostgreSQL 上,我可以执行以下操作: SELECT setval('seq', NEXTVAL('seq') + n - 1)

  • 当您可以使用固定的增量值(在我的情况下这是完全可以接受的)时,Matthew 的解决方案有效。但是,当您不想固定增量的大小,而是想动态调整它时,是否有解决方案?

4

4 回答 4

11

为什么不让序列一直递增 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> 
于 2008-09-04T14:10:39.940 回答
3

为什么首先需要获取序列 ID?在大多数情况下,您会插入到表中并返回 ID。

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

听起来您正在尝试预先优化处理。

如果您真的需要预取 ID,那么只需调用该序列 100 次。序列的全部意义在于它管理编号。你不应该假设你可以得到 100 个连续的数字。

于 2008-09-04T18:49:29.970 回答
1

马修在这里有正确的方法。在我看来,应用程序在每次使用后重置序列的当前值是非常不寻常的。将增量大小设置为您预先需要的任何内容都更为传统。

此外,这种方式的性能要高得多。从序列中选择 nextval 是 Oracle 中高度优化的操作,而运行 ddl 来更改序列的成本要高得多。

我想这并不能真正回答您编辑的问题中的最后一点......

于 2008-09-04T15:15:01.920 回答
1

因为当您不想要固定大小的增量时,序列并不是您真正想要的,它们真正保证的是您将获得一个始终大于您获得的最后一个数字的唯一数字。总是有可能最终出现差距,并且您无法真正安全或有效地即时调整增量量。

我真的想不出我必须做这种事情的任何情况,但可能最简单的方法是将“当前”数字存储在某处并根据需要进行更新。

像这样的东西。

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> 
于 2008-09-05T00:09:38.767 回答