この節では、特殊なパフォーマンス強化手法について説明します。ここで紹介する手法は、バージョン整合性と要求のパーティション分割の 2 つです。
データベース内のデータの完全性を保護しながらパフォーマンスを改善するには、「バージョン整合性」を使用します。アプリケーションサーバーは EJB コンポーネントの複数のコピーを同時に使用できるので、同時アクセスを通じて EJB コンポーネントが破壊された状態になる可能性があります。
破壊を防ぐ標準的な方法は、特定の Bean に関連付けられたデータベース行をロックすることです。これは、2 つの同時トランザクションによって Bean がアクセスされることを防ぎ、結果的にデータを保護します。ただし、この方法は実質的にすべての EJB アクセスを直列化するため、パフォーマンスも低下させます。
バージョン整合性は、EJB データの完全性を保護するためのもう 1 つのアプローチです。バージョン整合性を使用するには、バージョン番号として使用するデータベース内の列を指定します。その場合、EJB ライフサイクルの進行は次のようになります。
最初に Bean が使用されるとき、ejbLoad() メソッドが Bean を通常どおりにロードします。これには、データベースからのバージョン番号のロードが含まれます。
ejbStore() メソッドが、データベース内のバージョン番号を、EJB コンポーネントがロードされたときの番号の値と比較してチェックします。
バージョン番号が変更されている場合、それは EJB コンポーネントへの同時アクセスがあったことを意味し、ejbStore() は ConcurrentModificationException をスローします。
そうでない場合、ejbStore() はデータを格納して通常どおりに完了します。
ejbStore() メソッドは、Bean 内のデータが変更されたかどうかに関係なく、トランザクションの最後にこの検証を実行します。
2 回目以降の Bean の使用では、ejbLoad() メソッドがその初期データ (バージョン番号を含む) を内部キャッシュからロードすることを除いて、動作は同様です。これにより、データベースへのアクセスが省略されます。ejbStore() メソッドが呼び出されると、トランザクションで正しいデータが使用されたことを保証するためにバージョン番号がチェックされます。
バージョンが一致していると、2 つのトランザクションが同じ EJB コンポーネントを同時に使用できるため、めったに変更されない EJB コンポーネントがある場合には効果的な手法です。どちらのトランザクションもデータを変更しないため、両方のトランザクションの終了時にバージョン番号は不変のままであり、トランザクションは両方とも成功します。ただし、この時点ではトランザクションの並列実行が可能です。2 つのトランザクションが同じ EJB コンポーネントを変更する場合、一方が成功し、もう一方は失敗して、新しい値を使用して再試行することができます。これは、再試行の頻度が一定以下であれば、EJB コンポーネントへのすべてのアクセスを直列化するよりも高速です (ただし、再試行操作を実行するための新しいアプリケーションロジックを準備する必要がある)。
バージョン整合性を使用するには、特定のテーブル用のデータベーススキーマに、バージョンを格納できる列を含める必要があります。その後、特定の Bean に対する配備記述子 sun-cmp-mapping.xml でそのテーブルを指定します。
<entity-mapping> <cmp-field-mapping> ... </cmp-field-mapping> <consistency> <check-version-of-accessed-instances> <column-name>OrderTable.VC_VERSION_NUMBER</column-name> </check-version-of-accessed-instances> </consistency> </entity-mapping>
加えて、指定されたテーブル内のデータが変更されたときにバージョン列を自動的に更新するためのトリガーを、データベースに対して確立する必要があります。Application Server では、そのようなトリガーはバージョン整合性を使用する必要があります。そのようなトリガーを定義することにより、EJB データを変更する外部アプリケーションが、進行中の EJB トランザクションと競合しないことも保証されます。
たとえば、次の DDL は、Order テーブルのトリガーを作成する方法を例示します。
CREATE TRIGGER OrderTrigger BEFORE UPDATE ON OrderTable FOR EACH ROW WHEN (new.VC_VERSION_NUMBER = old.VC_VERSION_NUMBER) DECLARE BEGIN :NEW.VC_VERSION_NUMBER := :OLD.VC_VERSION_NUMBER + 1; END;
「パーティション分割要求」では、要求の優先度を EJB コンポーネントに割り当てることができます。これにより、特定の EJB コンポーネントを、ほかよりも高い優先度で実行させる柔軟性が得られます。
要求優先度が割り当てられた EJB コンポーネントは、その要求 (サービス) を、割り当てられたスレッドプールの内部で実行させます。スレッドプールをその実行に割り当てることにより、EJB コンポーネントはほかの保留中の要求から独立して実行できます。要約すると、要求のパーティション分割により、サービスごとに異なるレベルの優先度が割り当てられたサービスレベルアグリーメントを満たすことが可能になります。
パーティション分割要求は、リモートインタフェースを実装するリモート EJB コンポーネントのみに適用されます。ローカル EJB コンポーネントは、その呼び出しスレッド内で実行されます (たとえば、サーブレットがローカル Bean を呼び出すと、サーブレットのスレッド上でローカル Bean 呼び出しが発生する)。
管理コンソールを使用して、EJB 実行用の追加スレッドプールを設定します。
Application Server の ORB に、追加スレッドプールの ID を追加します。
これは、domain.xml ファイルを編集することによって、または管理コンソールを使用して行うことができます。
たとえば、 priority-1 および priority-2 という名前のスレッドプールを有効にするには、<orb> 要素を次のように編集します。
<orb max-connections="1024" message-fragment-size="1024" use-thread-pool-ids="thread-pool-1,priority-1,priority-2"> |
EJB コンポーネントの配備記述子 sun-ejb-jar.xml の use-thread-pool-id 要素に、スレッドプールの ID を含めます。
たとえば、次に示す sun-ejb-jar.xml は、「TheGreeter」という名前の EJB コンポーネントの配備記述子であり、priority-2 という名前のスレッドプールに割り当てられます。
<sun-ejb-jar> <enterprise-beans> <unique-id>1</unique-id> <ejb> <ejb-name>TheGreeter</ejb-name> <jndi-name>greeter</jndi-name> <use-thread-pool-id>priority-1</use-thread-pool-id> </ejb> </enterprise-beans> </sun-ejb-jar>
Application Server を再起動します。