如何在分布式环境中预取Oracle序列ID
我有一个在5个应用程序服务器上运行的分布式Java应用程序。所有服务器都使用在第六台计算机上运行的相同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上,我可以执行以下操作:选择setval('seq',nextval('seq')+ n-1)
- 当我们可以使用固定的增量值(在我的情况下完全可以接受)时,Matthew的解决方案起作用。但是,当我们不想固定增量的大小而是想要动态调整它时,是否有解决方案?
解决方案
回答
为什么不总是将序列始终增加100?每个"下一个"给我们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中,从序列中选择nextval是高度优化的操作,而运行ddl来更改序列则要昂贵得多。
我想这并不能真正回答我们所编辑问题中的最后一点...
回答
为什么首先需要获取序列ID?在大多数情况下,我们将插入表中并返回ID。
insert into t (my_pk, my_data) values (mysequence.nextval, :the_data) returning my_pk into :the_pk;
听起来我们正在尝试预先优化处理。
如果我们确实需要预提取ID,则只需调用序列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>