レベル: 中級 Mark Richards, Director and Sr. Technical Architect, Collaborative Consulting, LLC
2009年 6月 16日 連載「トランザクション・ストラテジー」では今回、スループットが高く、ユーザーの観点で並行性が高いという 2 つの要件を持つアプリケーションに、Java™ プラットフォームにおけるトランザクション・ストラテジーを実装する方法を、著者の Mark Richards が説明します。トランザクション・ストラテジーを実装するのに伴う長所と短所を理解することによって、データの完全性と整合性を高めることができ、開発プロセスの後の方になってリファクタリングの作業に苦労しなくてすむようになります。
この連載の前回までの記事で説明した、API 層トランザクション・ストラテジーとクライアント・オーケストレーション・トランザクション・ストラテジーは、標準的なビジネス・アプリケーションのほとんどに適用できる中核的なストラテジーです。これらのトランザクション・ストラテジーは単純で比較的容易に実装することができ、しかも堅牢な上、データの完全性と整合性のレベルは最高です。しかし場合によると、データベースでのトランザクションのスコープを狭めることで、スループットを高め、パフォーマンスを改善し、並行性を高めなければならないことがあります。そうしたことを実現し、しかも相変わらずデータの完全性と整合性を高く維持するためには、どうすればよいのでしょう。その答えは、高並行性トランザクション・ストラテジーを使用することです。
高並行性トランザクション・ストラテジーは API 層トランザクション・ストラテジーから派生するものです。API 層トランザクション・ストラテジーは確実で堅牢ですが、いくつかの欠点があります。例えば、必ずコール・スタックの最上位レベル (API 層) でトランザクションを開始するのは非効率です。ユーザーにとっての高スループット、データベースに対する高並行性が要求される場合には、尚更のことです。あるいは、特定のビジネス要件を禁止し、1 つのトランザクションを必要以上に長く継続すると、リソースを余分に使用することになると同時に、ロックの保持時間が長くなり、リソースの保持時間も必要以上に長くなります。
 |
この連載について
トランザクションはデータの品質、完全性、整合性を向上させてアプリケーションをより堅牢にします。しかし、Java アプリケーションに効果的なトランザクション処理を実装するのは容易な作業ではありません。トランザクション処理の実装には、設計がコーディングと同じく重要になってくるからです。この連載では、Mark Richards が読者の案内役となり、単純なアプリケーションからハイパフォーマンスのトランザクション処理に至るまでの様々な使用状況で功を奏するトランザクション・ストラテジーを設計する方法を説明します。
|
|
API 層トランザクション・ストラテジーの場合と同様、高並行性トランザクション・ストラテジーによって、クライアント層はトランザクションに関する責任から解放されます。しかしこれは、どのような論理作業単位 (LUW) に対しても、クライアント層からの呼び出しは 1 つしかできない、ということでもあります。高並行性トランザクション・ストラテジーの目標は、トランザクションの全体的なスコープを狭めることです。そうすることでリソースのロック時間を短くし、アプリケーションのスループット、並行性、パフォーマンス特性を改善するのです。
高並行性トランザクション・ストラテジーを使用することによって実現される可能性のあるメリットは、どのデータベースを使用しているか、どのようにデータベースが構成されているかによって多少変わります。一部のデータベース (Oracle や InnoDB エンジンを使用した MySQL など) は読み取りロックを保持しませんが、他のデータベース (スナップショット分離レベルを使用しない SQL Server など) は保持します。保持するロックが多ければ多いほど、それらが共有ロックであれ排他ロックであれ、データベース (つまりはアプリケーション) の並行性、パフォーマンス、スループットに影響します。
しかし、データベース内でロックをどのように取得、保持するかは、高並行性に関する話題の一部にすぎません。並行性とスループットには、ロックを解放するタイミングも関係します。どのデータベースを使用する場合でも、必要以上に長くトランザクションを保持すると、必要以上に長く共有ロックと排他ロックを保持することになります。すると、並行性が高い場合には、データベースはロックのエスカレーション・レベルを行レベルのロックからページ・レベルのロックに上げてしまい、極端な場合には、ページ・レベルのロックをテーブル・レベルのロックに上げてしまいます。ほとんどの場合、データベース・エンジンで使用される、ロック・レベルを上げるタイミングを指定するためのヒューリスティックを制御することはできません。一部のデータベース (SQL Server など) では、行レベルのロックがテーブル・レベルのロックに上がらないように、ページ・レベルのロックを無効にできるようになっています。この賭けが成功する場合もありますが、ほとんどの場合は元々望んでいたほど並行性は改善されません。
要するに、データベースに対する並行性要求の高いシナリオでは、データベースのロック (共有ロックまたは排他ロック) を保持する時間が長ければ長いほど、以下のいずれかの問題が発生する可能性が高くなります。
- データベース・コネクションが足りなくなり、アプリケーションに待ち状態が発生する。
- 共有ロックや排他ロックが原因でデッドロックが発生し、その結果、パフォーマンスが低下し、トランザクションが失敗する。
- ページ・レベルまたはテーブル・レベルのロックへのエスカレーションが起こる。
つまり、アプリケーションがデータベース内で処理を行う時間が長ければ長いほど、そのアプリケーションで対処できる並行性は低くなります。上に挙げたさまざまな問題によってアプリケーションの動作は遅くなり、その直接的な結果として全体的なスループットとパフォーマンスが低下します。そして大量のユーザー負荷を同時に処理するアプリケーションの能力も低下します。
トレードオフ
高並行性トランザクション・ストラテジーでは、トランザクションのスコープをアーキテクチャー上可能な限り下位のレベルに抑えることにより、高並行性に対する要求に対応します。こうすることで、API 層トランザクション・ストラテジーよりも速くトランザクションを完了 (コミットまたはロールバック) することができます。しかし、ヴァーサ (Vasa) 号 (訳注: ヴァーサ号は 17 世紀に建造されたスウェーデンの軍艦で、過去最高の装備を誇る船だったが、大砲を積載しすぎたため重心が高くなり、処女航海のときに出航してすぐに転覆してしまった。) という立派な船の話 (「参考文献」を参照) からも学べるように、すべてを併せ持つことはできません。人生はすべてトレードオフであり、トランザクション処理も例外ではありません。API 層トランザクション・ストラテジーと同様の堅牢なトランザクション処理を実現し、同時にピーク負荷時のスループットとユーザーの観点からの並行性とを最大にすることは、単純に言って不可能です。
では、高並行性トランザクション・ストラテジーを使用する場合には、何をあきらめるのでしょう。どのようにアプリケーションが設計されているかにもよりますが、たとえ更新を行うために読み取り操作を実行している場合であっても、トランザクションのスコープ外で読み取り操作を実行しなければならないかもしれません。すると皆さんは、「ちょっと待ってください。それはできません。そんなことをすると、最後に読み取りを行った後に変更されたデータを更新することになってしまいます。」と言うかもしれません。このような懸念を持つのはもっともですが、ここからトレードオフが始まるのです。高並行性トランザクション・ストラテジーでは、データに対する読み取りロックを保持していないため、更新操作を実行する際に古いデータを取得するという例外が発生する可能性が高くなります。しかし、ヴァーサ号の場合と同様で、すべてはどの特性がより重要であるかに尽きます。つまり、(API 層トランザクション・ストラテジーのように) 確実で完璧なトランザクション・ストラテジーが重要なのか、あるいはユーザーの観点からの並行性が高く、スループットも高いことが重要なのかということです。並行性の高い状況では、両方を同時に実現することは非常に困難であり、もし両方を実現しようとすると、そのアプリケーションではどちらに関しても中途半端なものになってしまいます。
もう 1 つのトレードオフは、トランザクション全体としての堅牢性に欠けることに関するものです。高並行性トランザクション・ストラテジーは、API 層トランザクション・ストラテジーやクライアント・オーケストレーション・トランザクション・ストラテジーよりも実装が難しく、開発やテストに時間がかかり、エラーが発生しやすくなります。皆さんは、こうしたトレードオフを考慮に入れた上で、まず現在の状況を分析し、高並行性トランザクション・ストラテジーを使用するのが適切であるかどうかを判断する必要があります。高並行性トランザクション・ストラテジーは API 層トランザクション・ストラテジーから派生したものであるため、1 つの適切な方法としては、最初は API 層トランザクション・ストラテジーで始め、(ピーク負荷時に想定される負荷よりも) 高いユーザー負荷でアプリケーションの負荷テストをしてみることです。その結果、スループットやパフォーマンスが低く、待ち時間が長く、さらにはデッドロックが発生することがわかった場合には、高並行性トランザクション・ストラテジーへの移行を始めるのです。
この記事のこれから先では、高並行性トランザクション・ストラテジーの持つ他の特性をいくつか説明し、このトランザクション・ストラテジーを実装するための 2 つの方法について説明します。
基本的な構造と特性
図 1 は、連載「トランザクション・ストラテジー」で使用してきた論理的なアプリケーション・スタックによって高並行性トランザクション・ストラテジーを表した図です。トランザクション・ロジックを含むクラスは背景を赤っぽくしてあります。
図 1. アーキテクチャーの階層とトランザクション・ロジック
API 層トランザクション・ストラテジーの特性と規則のいくつかは高並行性トランザクション・ストラテジーの場合にも当てはまりますが、すべてが当てはまるわけではありません。図 1 のクライアント層にはトランザクション・ロジックがないことに注目してください。これは、高並行性トランザクション・ストラテジーには任意のタイプのクライアントを使用できるということです (例えば Web ベースのクライアントや、デスクトップのクライアント、Web サービスのクライアント、そして JMS (Java Message Service) のクライアントなど)。また、トランザクション・ロジックがクライアント層より下位のすべての層に分散していますが、それぞれの層ではすべてのクラスにトランザクション・ロジックが含まれているわけではないことにも注目してください。API 層で開始されるトランザクションもあれば、ビジネス層で開始されたり、さらには DAO 層で開始されたりするトランザクションもあります。このように一貫性がないことが、高並行性トランザクション・ストラテジーの実装、維持、管理が難しい理由の 1 つです。
ほとんどの場合、プログラム型トランザクション・モデルを使用してトランザクションのスコープを狭める必要がありますが、場合によると宣言型トランザクション・モデルを使用することもできます。ただし一般的には、プログラム型トランザクション・モデルと宣言型トランザクション・モデルとを同じアプリケーションの中で混在させることはできません。高並行性トランザクション・ストラテジーを使用する場合には、プログラム型トランザクション・モデルを使用した方が賢明です。そうすれば、後で問題に突き当たることはありません。ただし、高並行性トランザクション・ストラテジーで宣言型トランザクション・モデルを使用できることがわかった場合には、トランザクションを開始するすべての層にある public な書き込みメソッド (挿入、更新、削除) のすべてに、トランザクション属性として REQUIRED を指定します。この属性は、トランザクションが必要なこと、そして (トランザクションがまだ存在していない場合には) この属性を持つメソッドによってトランザクションが開始されること、を示しています。
他のトランザクション・ストラテジーの場合と同様、どのコンポーネントや層でトランザクションを開始するにせよ、そのトランザクションを開始するメソッドは、トランザクションの所有者と見なされます。可能な限り、トランザクションの所有者以外はトランザクションのコミットとロールバックを実行できないようにする必要があります。
トランザクション・ストラテジーの実装
高並行性トランザクション・ストラテジーの実装に使用できる手法は主に 2 つあります。read-first (最初に読み取る) 手法では、読み取り操作をトランザクション・スコープ外にまとめ、できるだけアプリケーションの最上位層 (通常は API 層) で行うようにします。lower-level (より下位レベルで行う) 手法では、できるだけアーキテクチャー上の最下位層でトランザクションを開始するようにしますが、それでも更新操作のアトミック性を維持し、更新操作を切り離しておくことができます。
read-first (最初に読み取る) 手法
read-first 手法では、アプリケーションのロジックとワークフローをリファクタリングし (あるいは作成し)、すべての処理と読み取り操作がトランザクション・スコープ外で最初に行われるようにします。この手法では不要な共有ロックや排他ロックをなくすことができますが、自分が行った作業をコミットする前にデータが更新されてコミットされてしまうと、古いデータを取得するという例外が発生する可能性があります。その可能性に対応するために、高並行性トランザクション・ストラテジーでオブジェクト・リレーショナル・マッピング (ORM) フレームワークを使用する場合には、必ずバージョン管理を使用するようにします。
read-first 手法について説明するために、まず API 層トランザクション・ストラテジーを実装するコードから始めます。リスト 1 では、トランザクションは API 層で開始され、すべての作業単位 (すべての読み取り操作、処理、更新操作) がこのトランザクションに含まれています。
リスト 1. API 層トランザクション・ストラテジーを使用する
@TransactionAttribute(TransactionAttributeType.REQUIRED)
public void processTrade(TradeData trade) throws Exception {
try {
//first validate and insert the trade
TraderData trader = service.getTrader(trade.getTraderID());
validateTraderEntitlements(trade, trader);
verifyTraderLimits(trade, trader);
performPreTradeCompliance(trade, trader);
service.insertTrade(trade);
//now adjust the account
AcctData acct = service.getAcct(trade.getAcctId());
verifyFundsAvailability(acct, trade);
adjustBalance(acct, trade);
service.updateAcct(trade);
//post processing
performPostTradeCompliance(trade, trader);
} catch (Exception up) {
ctx.setRollbackOnly();
throw up;
}
}
|
リスト 1 では、(処理前と処理後両方の) あらゆる検証、妥当性確認、規則順守のチェックをはじめとする、すべての処理が JTA (Java Transaction API) トランザクションのスコープ内に含まれていることに注目してください。プロファイラー・ツールを使用して processTrade() メソッドを実行してみると、各メソッドを呼び出した場合の実行時間は表 1 のようになるはずです。
表 1. API 層のメソッドのプロファイル (トランザクション・スコープ)
| メソッド名 | 実行時間 (ミリ秒) |
|---|
service.getTrader() | 100 | validateTraderEntitlements() | 300 | verifyTraderLimits() | 500 | performPreTradeCompliance() | 2300 | service.insertTrade() | 200 | service.getAcct() | 100 | verifyFundsAvailability() | 600 | adjustBalance() | 100 | service.updateAcct() | 100 | performPostTradeCompliance() | 1800 |
processTrade() メソッドの継続時間は 6 秒をわずかに上回ります (6100 ミリ秒)。トランザクションはこのメソッドの先頭で開始され、メソッドの最後で終了するため、トランザクションの継続時間も 6100 ミリ秒です。どのようなデータベース・タイプと構成設定が使用されるかによりますが、(読み取り操作が実行される時から) そのトランザクションが継続される間、共有ロックと排他ロックの両方を保持することになります。さらに、processTrade() メソッドが呼び出すメソッドから実行されるすべての読み取り操作も、データベースの中でロックを保持する可能性があります。ご想像のとおり、この場合のようにデータベースの中で 6 秒間もロックを保持するようだと、これをスケーリングして高いユーザー負荷をサポートすることはできません。
リスト 1 のコードは、ユーザーの観点からの並行性の高さやスループットの高さを要求しない環境では完璧な動作をするかもしれません。残念ながら、大部分の人は、このタイプの環境でテストを行います。何百人という (おそらく世界中の) トレーダーが株取引を行う本番環境でこのコードが使用されると、ほぼ確実にシステムのパフォーマンスは低くなり、スループットが低くなり、そして (使用しているデータベースによりますが) ほぼ確実にデータベースのデッドロックが発生します。
そこで、高並行性トランザクション・ストラテジーの read-first 手法を適用し、リスト 1 のコードを修正します。リスト 1 のコードを見て最初に気付くことは、更新操作 (挿入と更新) は合計で 300 ミリ秒しかかからないということです。(ここでは、processTrade() メソッドが呼び出す他のメソッドは更新操作を行わないことを前提にしています。) 基本的な手法としては、読み取り操作、そして更新以外の処理を、トランザクション・スコープの外部で実行し、更新操作のみをトランザクション内にラップします。リスト 2 のコードは、トランザクション・スコープを狭めつつアトミック性を維持する上で必要なリファクタリングを示しています。
リスト 2. 高並行性トランザクション・ストラテジー (read-first 手法) を使用する
public void processTrade(TradeData trade) throws Exception {
UserTransaction txn = null;
try {
//first validate the trade
TraderData trader = service.getTrader(trade.getTraderID());
validateTraderEntitlements(trade, trader);
verifyTraderLimits(trade, trader);
performPreTradeCompliance(trade, trader);
//now adjust the account
AcctData acct = service.getAcct(trade.getAcctId());
verifyFundsAvailability(acct, trade);
adjustBalance(acct, trade);
performPostTradeCompliance(trade, trader);
//start the transaction and perform the updates
txn = (UserTransaction)ctx.lookup("UserTransaction");
txn.begin();
service.insertTrade(trade);
service.updateAcct(trade);
txn.commit();
} catch (Exception up) {
if (txn != null) {
try {
txn.rollback();
} catch (Exception t) {
throw up;
}
}
throw up;
}
}
|
ここで、insertTrade() メソッドと updateAcct() メソッドを processTrade() メソッドの最後に移し、プログラム型トランザクションの中にラップしていることに注目してください。こうすることで、すべての読み取り操作とそれに対応するすべての処理は、トランザクションのコンテキスト外で実行されます。その結果、トランザクションが継続される間、こうした操作や処理はデータベース内でロックを保持しません。新しいコードでのトランザクションの継続時間は 300 ミリ秒のみとなっており、リスト 1 の 6100 ミリ秒から大幅に短縮されています。繰り返しますが、目標はデータベース内で処理を行う時間を減らすことです。それによってデータベース全体としての並行性を高め、同時に接続しているユーザーによる大量の負荷をアプリケーションが処理できるようにするのです。リスト 2 のコードを使用すればデータベース内で処理を行う時間は 300 ミリ秒のみなので、(理論的には) スループットを 20 倍も高めることができます。
表 2 を見るとわかるように、トランザクション・スコープ内で実行されるコードの実行時間が 300 ミリ秒にまで減少しています。
表 2. API 層のメソッドのプロファイル (トランザクションのスコープを改善した場合)
| メソッド名 | 実行時間 (ミリ秒) |
|---|
service.insertTrade() | 200 | service.updateAcct() | 100 |
データベースの並行性の観点から見ると、これは大幅な改善ですが、read-first 手法によってリスクが発生します。つまり更新の対象となるオブジェクトに対して何もロックが保持されていないため、この LUW の間、こうしたロックされていないエンティティーを誰もが更新できてしまいます。そのため、挿入されるオブジェクトや更新されるオブジェクトが、通常は複数のユーザーによって一度に更新されることがないことを確実にしなければなりません。先ほどの株取引のシナリオでは、特定のアカウントで特定の株取引を行っているトレーダーは常に 1 人しかいない、と想定しても安全です。しかし、いつも必ずそうであるとは限らず、古いデータを取得するという例外が発生する可能性があります。
もう 1 つの注意点として、EJB (Enterprise JavaBeans) 3.0 を使用する場合には、コンテナーに対し、プログラム型トランザクション管理を使用する予定であることを宣言しておく必要があります。そのためには @TransactionManagement(TransactionManagementType.BEAN) アノテーションを使用します。このアノテーションが (メソッド・レベルではなく) クラス・レベルであることに注目してください。これはつまり、明らかに、同じクラスで宣言型トランザクション・モデルとプログラム型トランザクション・モデルとを組み合わせることはできない、ということです。どちらか一方を選択し、そのモデルのみを使用する必要があります。
lower-level (より下位レベルで行う) 手法
ここで例えば、トランザクション処理を単純化するために宣言型トランザクション・モデルを使用したいものの、ユーザーの観点での並行性が高いシナリオの間はスループットを高めたい、という場合を考えてみてください。こうした場合に、高並行性トランザクション・ストラテジーで lower-level 手法を使用します。この手法を利用すると、通常は read-first 手法の場合と同じトレードオフに直面します。つまり読み取り操作は通常、トランザクション・スコープのコンテキストの外で実行されるのです。そしてこの手法を実装するためには、ほぼ確実にコードのリファクタリングが必要になります。
ここでは、再度リスト 1 の例から始めることにします。同じメソッドの中でプログラム型トランザクションを使用する代わりに、コール・スタック内にある別の public メソッドに更新操作を移します。そして読み取り操作と処理を終えたら、その新しい更新メソッドを呼び出します。その更新メソッドはトランザクションを開始し、必要な更新メソッドを呼び出し、そしてリターンします。リスト 3 はこの手法を示しています。
リスト 3. 高並行性トランザクション・ストラテジー (lower-level 手法) を使用する
@TransactionAttribute(TransactionAttributeType.SUPPORTS)
public void processTrade(TradeData trade) throws Exception {
try {
//first validate the trade
TraderData trader = service.getTrader(trade.getTraderID());
validateTraderEntitlements(trade, trader);
verifyTraderLimits(trade, trader);
performPreTradeCompliance(trade, trader);
//now adjust the account
AcctData acct = service.getAcct(trade.getAcctId());
verifyFundsAvailability(acct, trade);
adjustBalance(acct, trade);
performPostTradeCompliance(trade, trader);
//Now perform the updates
processTradeUpdates(trade, acct);
} catch (Exception up) {
throw up;
}
}
@TransactionAttribute(TransactionAttributeType.REQUIRED)
public void processTradeUpdates(TradeData trade, AcctData acct) throws Exception {
try {
service.insertTrade(trade);
service.updateAcct(trade);
} catch (Exception up) {
ctx.setRollbackOnly();
throw up;
}
}
|
この手法を使用すると、実質的にコール・スタック内のより下位レベルでトランザクションを開始することになり、それによってデータベース内で処理を行う時間を減らすことができます。processTradeUpdates() メソッドが親メソッド (あるいはそれより上位) で変更または作成されたエンティティーのみを更新することに注目してください。この場合もトランザクションを 6 秒間も保持することはなく、300 ミリ秒間しか保持しません。
さて、今度は難しい部分です。API 層トランザクション・ストラテジーやクライアント・オーケストレーション・トランザクション・ストラテジーとは異なり、高並行性トランザクション・ストラテジーを一貫した手法で実装することはできません。そのため、図 1 は経験の長いホッケー選手の顔のように見えるのです (歯がいくつも欠けています)。一部の API 呼び出しではトランザクションが API 層で開始、終了されるかもしれず、他の場合には (LUW 内での 1 つのテーブルの更新では特に) トランザクション・スコープは DAO 層のみかもしれません。手法としては、複数のクライアント・リクエストの間で共有されるメソッドを識別し、より上位レベルのメソッドでトランザクションが開始された場合には、そのトランザクションを必ずより下位レベルのメソッドで利用するようにします。残念ながら、こうすることによる影響として、そのトランザクションの所有者ではない下位レベルのメソッドが、例外が発生した場合にロールバックを実行する可能性があります。その結果、そのトランザクションを開始した親メソッドは例外に対して是正アクションを行うことができず、既にロールバック用にマーキングされているトランザクションをロールバック (またはコミット) しようとすると、例外が発生してしまいます。
実装のガイドライン
状況によっては、トランザクションのスコープをほんの少し狭めるだけでスループットと並行性の要件を満足できますが、大幅にトランザクションのスコープを狭めない限り求める結果が得られない場合もあります。どのような状況であれ、高並行性トランザクション・ストラテジーの設計や実装を行う際には、実装に関する以下のガイドラインが役立つはずです。
- まず read-first 手法で開始し、その後で lower-level 手法を試します。そうすることで、トランザクションは少なくともアプリケーションのアーキテクチャーの API 層に収まり、他の層にまで広がることがありません。
- 宣言型トランザクションを使用する場合には、トランザクション属性として
MANDATORY ではなく常に REQUIRED を使用し、そのトランザクションを開始したメソッドが別のトランザクションのメソッドを呼び出してしまう場合に対して保護する必要があります。
- 高並行性トランザクション・ストラテジーを使用する前に、そのトランザクションのスコープ外で読み取り操作を実行しても比較的安全であることを確認する必要があります。エンティティーのモデルを調べ、複数のユーザーが同時に同じエンティティーに対して操作を行うことが一般的なのか、稀なのか、あるいは不可能なのかを確認する必要があります。例えば、2 人のユーザーが同じアカウントを同時に変更することはできるのでしょうか。そうしたことは一般的であるというのが答えであれば、古いデータを取得するという例外が発生するリスクがより高くなり、高並行性トランザクション・ストラテジーはそのアプリケーションのプロファイルには不適切な選択ということになります。
- すべての読み取り操作がトランザクションのスコープ外で行われなければならないわけではありません。ある特定のエンティティーが複数のユーザーによって同時に変更されることが頻繁にある場合には、是が非でもそのエンティティーをトランザクションのスコープに加えるようにしてください。ただし、トランザクション・スコープに加える読み取り操作と処理が多ければ多いほど、スループットとユーザー負荷の処理能力が低下することを認識しておく必要があります。
まとめ
結局のところ、すべてはトレードオフです。アプリケーションまたはサブシステムの要件として、スループットが高く、ユーザーの観点で並行性が高いという 2 つの要件を満たすには、データベースには高い並行性が必要です。データベースで高い並行性を実現するためには、データベースのロックを削減し、リソースを保持する時間を可能な限り短くする必要があります。データベースのタイプや構成によっては、これらの要求に対応することができますが、ほとんどの場合、そうした要求に対するソリューションは、最終的にコードやトランザクション処理の設計にかかってきます。こうした問題を少し考えることで、後で行う面倒で複雑なリファクタリング作業が軽減されます。適切なトランザクション・ストラテジーを選択することは、アプリケーションの成功には欠かせません。ユーザーの観点での並行性を高くするという要求に対応するには、ソリューションの選択肢として高並行性トランザクション・ストラテジーを検討する価値があります。高並行性トランザクション・ストラテジーでは、高いレベルでデータの完全性と整合性を確保できる一方、高並行性と高スループットへの要件も満たすことができます。
参考文献 学ぶために
議論するために
著者について  | 
|  | Mark Richards は、Collaborative Consulting, LLC のディレクター兼シニア・テクニカル・アーキテクトです。彼は『Java Message Service』(O'Reilly、2009年) の第 2 版、そして『Java Transaction Design Strategies』(C4Media Publishing、2006年) の第 2 版の著者であり、寄稿者としても『97 Things Every Software Architect Should Know』(O'Reilly、2009年)、『NFJS Anthology Volume 1』(Pragmatic Bookshelf、2006年)、『NFJS Anthology Volume 2』(Pragmatic Bookshelf、2007年) などの本に貢献しています。IBM、Sun、The Open Group、そして BEA の認定アーキテクトおよび技術者である彼は No Fluff Just Stuff Symposium Series ではお馴染みの講演者で、世界各地のその他のカンファレンスやユーザー・グループでも講演を行っています。 |
記事の評価
|