図1●今回取り上げる7つのポイント<BR>実行性能を高めるためのポイント(ポイント2,3,4)や,トラブルを事前に回避するためのポイント(ポイント1,5,6)――などを取り上げる
図1●今回取り上げる7つのポイント<BR>実行性能を高めるためのポイント(ポイント2,3,4)や,トラブルを事前に回避するためのポイント(ポイント1,5,6)――などを取り上げる
[画像のクリックで拡大表示]
図2●ロック待ちの回避方法&lt;BR&gt;UPDATE/DELETE/INSERT文は自動的にロックがかかる。SELECT文は「FOR UPDATE句」をつければロックがかかる。同時に同じデータをロックする場合,一方が他方のロック解除を待つ。ロック待ちを避けたい場合,SELECT文では「NOWAIT」オプションをつける。NOWAITオプションをつければ対象データがロックされていても,ロック待ちを行うことなく処理が戻される
図2●ロック待ちの回避方法<BR>UPDATE/DELETE/INSERT文は自動的にロックがかかる。SELECT文は「FOR UPDATE句」をつければロックがかかる。同時に同じデータをロックする場合,一方が他方のロック解除を待つ。ロック待ちを避けたい場合,SELECT文では「NOWAIT」オプションをつける。NOWAITオプションをつければ対象データがロックされていても,ロック待ちを行うことなく処理が戻される
[画像のクリックで拡大表示]
図3●アンケート結果を様々な切り口で集計するSQL文の例&lt;BR&gt;「1.回答者人数を年齢範囲で集計する」,「2.ある質問の回答数を年齢範囲で集計する」――という2つのSQL文の例を示した。このような集計は,SQL文でデータをまとめて取り出した後でJavaやVisual Basicのプログラムで集計することもできる。だが,大量データから集計する場合,できるだけSQL文で処理した方が実行性能が良い
図3●アンケート結果を様々な切り口で集計するSQL文の例<BR>「1.回答者人数を年齢範囲で集計する」,「2.ある質問の回答数を年齢範囲で集計する」――という2つのSQL文の例を示した。このような集計は,SQL文でデータをまとめて取り出した後でJavaやVisual Basicのプログラムで集計することもできる。だが,大量データから集計する場合,できるだけSQL文で処理した方が実行性能が良い
[画像のクリックで拡大表示]

ロック待ちを回避したい場合,SELECT~FOR UPDATE文ではNOWAITオプションを付ければよい。実行性能を向上させるポイントは,(1)データ集計などはできるだけRDBMS上で処理し,(2)省略可能な処理はできるだけ実施しないことである。(3)省略可能な処理には,SQL文の解析処理とデータベースへの接続処理がある。そのほか,ミドルウエアの仕様を確認しておくこと,本番データを使ってテストしておくこと,などがポイントである。

 SQL文を利用する際に注意すべき基礎的なポイントを解説するセミナーの最終回である。前回は,トランザクション*カーソル*の利用における注意点について説明した。製品の違いによるトランザクションの挙動の違いと,暗黙カーソルでエラーになるケースなどを説明した。

 最終回の今回は,データベース・アプリケーションの開発で注意すべきポイントを7つ紹介する(図1[拡大表示])。7つの中には,実行性能を高めるためのポイントと,トラブルを事前に回避するためのポイントなどがある。なおRDBMSは,筆者の利用経験の多いOracleをベースとする。実行結果などはすべてOracle上で稼働したものになるが,基本的にはほかのRDBMSでも同様である。

ポイント1 ロック待ちを制御する

 ロック*は,データベースの整合性を維持するために欠かせないRDBMSのメカニズムである。複数のトランザクションが同じタイミングで同じデータを更新しようとすると,RDBMSは1つのトランザクションの処理だけを進め,ほかのトランザクションを“待ち”状態にする。アプリケーション開発においては,ロック待ちを意識することが重要である。ロック待ちになったアプリケーションは“だんまり状態”になってしまうが,アプリケーションによってはだんまり状態になるのがふさわしくないことがある。そのような場合,ロック待ちを回避することが必要である。

NOWAITオプションでロック待ちを回避

 ロックの基本的な動きを確認しておこう。UPDATE*文,DELETE*文,INSERT*文を発行すると,RDBMSが自動的に対象データに排他ロック*をかける。ロックの対象は,レコード,データ・ブロック*,テーブルなどである。SELECT*文においても排他ロックをかける必要があるケースがあり,その場合SELECT文にFOR UPDATE句を付けると対象データに排他ロックがかかる(図2[拡大表示]の1.ロックのかけ方)。

 排他ロックは同一データに対して同時に1つのトランザクションだけがかけることができるものであり,すでにほかのトランザクションが排他ロックをかけている場合,排他ロックが解除されるまで待つ。これがロック待ちであり,ロック待ちの間はだんまり状態になる(同 2.ロック待ち)。ロック待ちを回避したい場合,SQL文に対して指定する。

 実際のアプリケーション開発では,「SELECT~FOR UPDATE文」においてロック待ちを回避させるケースが多い*1。この文でロック待ちを回避するにはNOWAITオプションを設定すればよい。NOWAITオプションが設定されている場合,ロック対象がすでにほかのトランザクションにロックをかけられていると,待ち状態になることなく,即座にエラーを返す(同 3.ロック待ちの回避)。この場合RDBMSはエラーを返すため,アプリケーション側でエラーに対する処理を忘れないようにしよう。

 ここまではOracleの場合であるが,RDBMS製品によってはNOWAITオプションが備わっていない。例えばPostgreSQLには,FOR UPDATE句はあるがNOWAITオプションは無い。そのようなRDBMSで同様の処理を実装するには,ロックの状態を格納するカラムやテーブルを用意し,アプリケーション側で制御するなどの工夫が必要である。

ポイント2 できるだけRDBMS上で処理する

 RDBMSやSQLの初心者,特にファイルにデータを格納するようなアプリケーションを開発していたエンジニアがやってしまいがちな失敗がある。RDBMSをデータ・ファイルと同様に扱ってしまうのだ。ファイルにデータを格納する場合,ファイルに対してはデータの出し入れだけを行い,データに対する処理はすべてアプリケーション・プログラム(JavaやVisual Basicなど)側で行う。RDBMSを利用する場合においてもこのようなアプリケーションの構造をそのまま当てはめると,実行性能が低下しやすい。RDBMSを利用する場合,基本的には,データに対する処理はできるだけRDBMS上で処理した方がよい。

大量データに対する処理はSQL文で記述する

 RDBMS上で処理した方が実行性能が高くなる理由は,ネットワーク上を移動するデータ量が少なくてすむからである。例えばクライアント/サーバー型のアプリケーションの場合,RDBMS上でデータを処理すれば,サーバーからクライアントに送信するデータは処理結果だけで済む。それに対してクライアント・プログラム側で処理する場合,処理対象のすべてのデータをサーバーからクライアントに送信しなければならない。大量データを集計するような処理の場合,明らかにRDBMS上で処理した方が効率が良い。

 RDBMS上で処理するということは,SQL文で記述するということである。SQL文を駆使すれば,様々な処理が記述できる。SQL文の応用例として,ここではアンケート結果を様々な切り口で集計するSQL文の例を挙げた。図3[拡大表示]上のようなQAテーブルに対し,年齢範囲(1~20,21~40,41~60)で回答者人数を集計するSQL文と,年齢範囲である回答の回答内容別の人数を集計するSQL文は,図3のように記述すればよい。これらの処理はQAテーブルからデータを取り出してアプリケーション・プログラム側で処理することも可能だが,データ量が多くなればなるほどSQL文で記述した方が実行性能は高い。


玉川 敏一
シーズ・ラボ ITソリューション部 セクションマネージャー