6

5 つのアプリケーション サーバーで実行されている分散 Java アプリケーションがあります。サーバーはすべて、6 台目のマシンで実行されている同じ Oracle 9i データベースを使用しています。

アプリケーションは、シーケンスから 100 個の ID のバッチをプリフェッチする必要があります。シングルスレッドの非分散環境では、次のクエリを発行するだけで比較的簡単に実行できます。

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

最初の select は、アプリケーションが使用できる最初のシーケンス ID をフェッチし、2 番目の select は、使用できる最後の ID を返します。

マルチスレッド環境では、物事がさらに面白くなります。2 番目の select の前に、別のスレッドがシーケンスを 100 ずつ増加させないことを確認することはできません。この問題は、Java 側でアクセスを同期することで解決できます。一度に 1 つのスレッドのみが ID のフェッチを開始できるようにします。

アプリケーションの一部が同じ JVM 上で実行されず、同じ物理マシン上でさえ実行されないために同期できない場合、状況は非常に困難になります。フォーラムで、他の人もこの問題を解決するのに問題があるといういくつかの参照を見つけましたが、合理的であることは言うまでもなく、実際に機能する答えはありません。

コミュニティはこの問題の解決策を提供できますか?

いくつかの詳細情報:

  • トランザクションの分離レベルを実際に操作することはできません。私は JPA を使用していますが、変更はプリフェッチ クエリだけでなく、アプリケーション全体に影響を与えるため、私には受け入れられません。
  • PostgreSQL では、次のことができます。 SELECT setval('seq', NEXTVAL('seq') + n - 1)

  • マシューによる解決策は、固定の増分値を使用できる場合に機能します (私の場合は完全に受け入れられます)。ただし、インクリメントのサイズを固定したくないが、動的に調整したい場合の解決策はありますか?

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

固定サイズの増分が必要ない場合、シーケンスは実際には目的ではありません。最後に取得した番号よりも常に大きい一意の番号を取得することが保証されます。ギャップが発生する可能性は常にあり、その場で増分量を安全または効果的に調整することはできません。

このようなことをしなければならなかったケースは考えられませんが、おそらく最も簡単な方法は、「現在の」番号をどこかに保存し、必要に応じて更新することです。

このようなもの。

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 に答える
1

マシューはここで正しいアプローチをしています。私の意見では、アプリケーションがシーケンスを使用するたびに現在の値をリセットすることは非常に珍しいことです。増分サイズを事前に必要なものに設定する方がはるかに一般的です。

また、この方法ははるかにパフォーマンスが高くなります。シーケンスから nextval を選択することは、Oracle では高度に最適化された操作ですが、ddl を実行してシーケンスを変更すると、はるかにコストがかかります。

編集した質問の最後のポイントには実際には答えていないと思います...

于 2008-09-04T15:15:01.920 に答える