目次


アプリケーション・パフォーマンスのトラブルシューティング: パート 1: トラブルシューティングの手法とコードのヒント

Comments

はじめに

長年にわたり、私たちは多くの優れた Domino アプリケーションを見てきました。創造的なアーキテクチャー、配慮に富んだユーザー・インターフェース、正確なコードなど、数々の優れた要素があります。しかし、その一方ではパフォーマンスに問題を抱える Domino アプリケーションも存在します。これらの問題には、アーキテクチャーから効率の悪いコードまで、さまざまな原因が考えられます。また、トラブルシューティングのツールや手法が限られていたり、これらの理解が不足していると、お客様 (およびトラブルシューティング担当者) の状況はさらに困難になります。この 2 部構成の記事では、索引とコードの手法、およびコードの問題点の検出に役立つ新しいツールについて解説します。

パート 1 では、実際の企業アプリケーションにおいて短時間で問題を絞り込むプロセスを例に取り、パフォーマンスのトラブルシューティングにおける実証済みの手法を解説します。また、ビューの索引更新とエージェントの最適化に役立つヒントについても解説します。シリーズのパート 2 では Lotus Notes/Domino 7 の新規ツールを取り上げます。このツールは、エージェント・パフォーマンスのモニタリングとトラブルシューティングに役立ちます。このシリーズは、経験のある Notes アプリケーション開発者を対象としています。

問題を識別する

アプリケーション (またはサーバー) の動作が遅いと、IT 担当部署への電話による問い合わせが急増します。Lotus/IBM サポートやコンサルティング・サービスへの問い合わせも例外ではありません。この問題を解決するには 2 つの方法があります。1 つは、ボトルネックを識別し、狭くなっている箇所を広げる方法です。定義上、これは連続的なプロセスですが、通常は、効果が減少する明らかなポイントがあります。もう 1 つの方法は、最初からこれらのボトルネックを避けるようアプリケーションまたはサーバーを計画し、構築することです (言うのは簡単でも、実行は困難です)。この記事では、主に最初のアプローチについて説明しますが、2 番目の実装方法についても提案します。

私たちは、ボトルネックの識別に役立つ 4 つのステップを考案しました。

詳細を入手する

アプリケーション (またはサーバー) の動作が遅いという相談を受けたときは、まず、そのアプリケーションに詳しい人と綿密に話し合い、ポイントを絞って詳細な質問を行ないます。これまでの経験から、質問をするときは次の 3 つの領域に重点を置くと、有益な回答が得られる可能性が高まります。

  1. どこに問題がありますか (トポロジー的および時系列的)? 知りたいのは、Domino アプリケーション、サーバー、ネットワークなどのうち、どの部分が遅くなっているかです。次のような質問が役に立ちます。
    • 動作が遅くなるとき、それはサーバーで実行したすべてのアクションで発生しますか、あるいは 1 つのアプリケーション内のアクションだけで発生しますか?
    • サーバー上のすべてのアクションが影響を受ける場合、別のサーバーはどうなりますか? ファイル・サーバーはどうですか?
    • ネットワーク上のすべてのアクションが影響を受ける場合、それは特定のビル、棟、またはフロアだけで発生しますか?
    • 少し角度を変えた質問として、これらの問題は特定の時間または日に発生しますか? 特定の種類のマシンだけに発生しますか? 古いマシンに発生しますか? 同じ OS のマシンだけに発生しますか?
  2. 何に問題がありますか (アクション)? 知りたいのは、アプリケーションのどの部分、またはユーザーのどの操作がパフォーマンスの問題に関連しているかです。次のような質問で、詳細な情報を入手します。
    • データベースを開くときに、遅くなりますか? ビューを開くときですか? ビューをスクロールするときですか? もしそうなら、すべてのビューが影響を受けますか、または特定のビューだけが影響を受けますか? 特定のビューだけが遅くなる場合は、該当するビューを少なくとも 1 つか 2 つ把握します。
    • 文書を作成、編集、保存、または読み込むときに、遅くなりますか? もしそうなら、アクションを特定します。そして、どのフォームの文書でもそうなるのか、特定のフォームの文書だけなのかを判断します。もちろん、該当するフォームを把握します。
    • 特定のボタンをクリックしたときに発生しますか? これは最も簡単なシナリオです。どのボタンか識別できれば、特定のコードだけを調べればよいからです。
  3. 変更されたものは何ですか? ほとんどの場合、お客様は特別な変更はしていないと考えています。もし、重要な変更が行なわれていたら、私たちに相談せずに、その変更箇所を入念に調べるはずです。次のような質問をして、事実を明確にします。
    • このアプリケーション (またはサーバー) はいつも動作が遅いですか?
    • そうでない場合は、ユーザーを追加したときにアプリケーションが遅くなりましたか? データが増えたときですか?
    • 最近、アプリケーションに新しい機能を追加しましたか?
    • 最近、ソフトウェアまたはハードウェアをアップグレードしましたか?

仮説を立てる

前の質問に対する回答に基づき、問題に対するいくつかの仮説を検討することから始めます。たとえば、Main フォームの文書で作業するときに必ずアプリケーションが遅くなるという回答からは、そのフォームのコードを調べる必要があるという結論に至るでしょう。そして、パフォーマンスの改善という観点から、アプリケーション開発者にコードを見直してもらいます。あるいは、ユーザーが特定のボタンをクリックしたときに必ずアプリケーションが遅くなる場合は、そのボタンのコードを直接調べます。

一方、どのような操作を行なうときも、一日中パフォーマンスが頻繁に低下したり、全体的にサーバーの動作が遅いという回答であれば、サーバータスクが過負荷になっていることが考えられます。一般的に、最初に調べるのは索引の更新です。頻繁にまたは定期的に索引の更新が行なわれるようにアプリケーションを書くことは、珍しいことではありません。また、ユーザーとデータが増加するにつれ、索引更新の負荷が高まります。実際に、ビューを最新の状態に保つために indexer が定期的に動作している実働サーバーを見たことがあります。このようなケースでは、CPU サイクルをユーザーの要求に配分する余地がなく、ユーザーはサーバーが応答しないとしばしば感じるようになります。

データの収集

いずれのケースでも、疑わしい箇所があるときは、仮説を検証または討論するためにデータを収集します。これは反復的な作業なので、1 つまたは 2 つの手順を数日ではなく数時間で処理することが理想的です。

仮説に基づいて、異なるツールを使用してデータを収集します。どのリリースの Lotus Domino でも、Notes.ini でロギングを自由に設定できます。これは非常に価値があります。ビューの索引更新に問題があると考えられる場合は、log_update=1 をオンにします。この値をサーバー設定文書に入力すると、サーバーの Notes.ini ファイルにこの値が書き込まれます。この設定は 1 日だけそのままにしておき、その後削除します (通常、この設定によってログファイルに書き込まれる情報はあまり多くないので、設定を残したまま運用を続ける企業も多くあります)。次に、サーバーの log.nsf の [Miscellaneous Events] ビューで測定日の情報を見て、indexer の動作対象とその所要時間を調べます。次のようなエントリが表示されます。

01/04/2005 11:35:47 AM  Updating views in Merkle\CM.nsf
01/04/2005 11:36:22 AM  Finished updating views in Merkle\CM.nsf
01/04/2005 12:02:17 PM  Updating views in Merkle\CM.nsf
01/04/2005 12:02:39 PM  Finished updating views in Merkle\CM.nsf
01/04/2005 12:36:04 PM  Updating views in Merkle\CM.nsf
01/04/2005 12:36:15 PM  Finished updating views in Merkle\CM.nsf
メモ: 通常、これらの 6 行の間には、他のロギング情報が何百行も含まれています (何千行にはなりません)。このため、必要な情報だけを検索してテキスト・ファイルにコピーすると、関連する情報が上記のようにまとめられて分析しやすくなります。

それぞれの Domino タスクは (複製でも Agent Manager でも) 同様のログ機能を持っているので、これらのタスクに関するデータを入手するのはたいへん容易です。

分析と繰り返し

収集したデータを分析し、問題に近づいているかどうかを判断します。そして、質問、仮説、データ収集、データ分析のプロセスを繰り返します。

トラブルシューティングの例

長年にわたり、私たちはパフォーマンスの最適化を支援するために、数多くの企業アプリケーションを調査する機会を得てきました。そこで理解したのが、ほとんどのパフォーマンスの問題は、次のいくつかのカテゴリーに分けられることです。

  • フォームの設計このトピックについては、この記事では扱いません。一般に、典型的な使用法を念頭に置きながら、編集モードと読み込みモードでパフォーマンスを最適化することが必要です。
  • エージェント トラブルシューティングといくつかのコードのヒントをこの記事で解説します。
  • ビューの索引更新 これについても、トラブルシューティングといくつかのコードのヒントを解説します。

企業で発生した問題の実例を紹介します。ある企業から、1つのアプリケーションで 1日中すべてのアクティビティのパフォーマンスが低下しているという報告がありました。良いパフォーマンスのときもありますが、忙しい時間になるほどパフォーマンスが低下します。さらに質問を重ねると、ほとんどすべてのユーザー・アクションで、速いときと遅いときがあることがわかりました。このことから、私たちは、フォーム、エージェント、およびデータベースのその他のアクションでコードを調査しても意味はないと判断しました。つまり、サーバーリソースを大量に消費しているサーバータスクが少なくとも 1つは存在するはず、と確信したのです。ビューを開いたり、ビュー内でのスクロールの際に非常に遅くなることがあるというユーザーからの報告が、大きなヒントになっています。そこで、ビューの索引更新について調査することにしました。

log_update=1 を設定し、索引に関するデータを 1 日の間収集しました。ここから、対象となるデータベース Merkle\CM.nsf に関連するすべての行を抜き出しました。これが、先に示した 6 つの行です。調査の目的で、xyz.nsf で pdating ビューを使用することを考えました。xyz.nsf は対象となるアプリケーションのパスとファイル名です。先頭の u を省くことで、大文字と小文字の区別を気にする必要がありません。

この 6 行から明らかになった興味深い点は、update タスクがそのサイクルを完了させるまでに 30 分もかかっていることです。通常、update タスクは 15 分ごとに実行され、Domino Server ではその設定を変更できません。特定のデータベースで update タスクが 15 分以上もかかる場合は、次の 2 つの理由が考えられます。まず、一定の時間内にデータベースのアクティビティがないため、update タスクが 1 回以上のサイクル中でそのデータベースをスキップした場合。これは、まさに深夜または週末に発生する現象ですが、使用度の高いデータベースが就業時間内にこのようなることはほとんどあり得ません。

次に考えられるのは、更新が必要なすべてのデータベースのすべてのビューで、update タスクが更新を完了させるまでに 15 分以上かかっていることです。このような場合、サーバーがビューの索引更新作業に費やされるため、パフォーマンスが停滞することが一般的です。

この企業のケースでは、私たちは後者が原因であると判断し、企業にとってこれが貴重な情報となりました。しかし、これで解決したわけではありません。次のステップは、もう一度、質問、仮説、データ収集、データ分析の 4 つの手順によるプロセスを開始することです。

update タスクが完了までに約 30 分もかかる場合は、ログファイルの [Miscellaneous Events] 文書を再度調べ、なぜそのように長時間かかるのかを探ります。1 つの可能性として、莫大な量のデータベースとビューが存在することがあります。非常に量が多いと、1 つずつの索引はすぐに更新できても、すべての索引を更新するのに 30 分以上かかってしまいます。この観点から、データベースによってサーバーが過負荷になっていることはないか質問します。

もう 1 つの可能性は、データベースとビューの数はあまり多くないが、処理に非常に時間がかかるケースです。この仮説を立てた場合は、ログの設定を log_update=2 に変更します。この設定では、データベースレベルではなく、索引を更新する各ビューごとにタイムスタンプが記録されます。この結果に基づき、索引更新に非常に時間がかかる特定のビューを調べます。ただし、この設定にすると大量のデータが log.nsf に書き込まれるので、データ収集後はできるだけ早くオフにするか、log_update=1 に変更します。

また、3 番目の可能性として、1 日を通して非常に大量のデータが変更されるケースも考えられます。

これらの点については、当然ながら、ログから回答を得ることも、企業から直接回答を得ることもできます。企業としては、どれだけのデータ量が多すぎるのか不明かもしれませんが、アプリケーションの使い方と、1日あたりのデータのおよその変更量については説明できます。

先ほどの企業の例では、レベルを上げたログによって、一部のビューが索引更新に非常に長い時間かかっていることが明らかになりました。これらのビュー (主要アプリケーションに含まれないビューも含む) を調べたところ、アクセスされるたびにビュー全体を強制的に再構築する方法で、ビューがコーディングされていました。ビューの選択式あるいは列式に @Now または @Today が含まれていたので、ユーザーがこれらのビューを操作するたびにビューを最初から再構築しなければならず、これが非常に時間のかかるプロセスとなります。さらに、これが Domino 5 サーバーであり、リリース 5 では、update タスクがデータベース内の任意のビューを更新するたびに、時刻/日付に依存するビューもかならず更新されます。

このことを知った企業は、時刻/日付に依存するビューを使用せずにビジネス・ニーズを満たすようコードを変更しました。これにより、パフォーマンスはすぐに飛躍的に向上しました。この問題の解決に要した合計時間は、私たちが約 5日間で、企業の開発者がコードを変更およびテストして新しいアプローチに成功するまでが 10 ? 20 日間です。

コードのヒント

私たちは長年の観察とテストから、パフォーマンスの改善に役立つ、非常に一般的で重要なコーディングの手法をいくつか確立してきました。いずれも簡単に導入できます。次のような手法があります。

  • 時刻/日付に依存するビュー
  • クリックでソートできるビューの列
  • エージェントでの文書コレクションの取得

時刻/日付に依存するビュー

選択式や任意の列式に @Now または @Today を含むビューは、時刻/日付に依存するビューと言えます。つまり、パフォーマンスの点で見ると、ビューが更新されるときに、必ずビューそのものも完全に再構築されることを意味します。このようなビューの再構築には 50 ? 100 秒もかかることがありますが、同じビューで @Now または @Today 式を含まない場合は、ほんの 100 ミリ秒ほどで更新できます。1日にビューが何百回も更新されることを考えると、これは非常に大きな違いとなります。

たとえば、ユーザーがビューを開くと、ビューが更新されます。ビュー内でユーザーが文書を編集および保存すると、ビューが更新されます。ビューのリロードを示す青い矢印が表示され、ユーザーがこれをクリックすると、ビューが更新されます。いずれのケースでも、ビューが単に更新されるのではなく再構築が行なわれると、ユーザーはたいへん待たされ、サーバーには大きな負荷がかかることになります。

Lotus Notes/Domino 6 では、update タスクはこれらのビューを完全に無視します (前のセクションの update タスクの説明を参照)。つまり、このビューが存在するだけで支障になることはありません。Lotus Notes/Domino 6 では、ユーザーの操作が原因の場合にのみ、このビューが問題となります。

また、Lotus Notes/Domino 6 では、索引オプション (ビューを自動的に更新する: 自動、n 分ごと、手動) が機能するので、時刻/日付に依存するビューを手動で更新できます。これによって、ユーザーが更新しなくてもビューがすぐに開かれるため、サーバーの負荷も削減されます。索引オプションをこのように設定すると、ユーザーが [F9] を押したとき、または更新用の青い矢印をクリックしたときにのみ、ビューが更新されます。通常のビューは、update タスクによって 15 分ごとに更新されます。しかし、update タスクは時刻/日付に依存するビューをスキップするため、このようなビューは更新 (つまり、再構築) されません。

ビューを古い状態のままで維持することは良い方法とはいえませんが、Lotus Notes/Domino 6 のこのオプションは、実際に非常に効果があります。一定の間隔でビューを更新する必要がある場合は、エージェントを実行してビューを更新するか、ビューの更新オプションで、n 時間ごとに自動的にビューを更新します。

クリックでソートできるビューの列

これは、Lotus Notes/Domino 6 の新機能ではありませんが、優れた機能です。この機能を使用すると、ユーザーはソート機能が設定されている任意の列を使用して、ビューを再ソートできます。しかも、動作が速いので、このような再ソート機能をユーザーに提供しても、パフォーマンスには影響しないだろう、と考えがちです。しかし、それは正しくありません。

パフォーマンスの観点から、ソート用の小さな矢印をクリックすることは、ソート方向が異なる、オリジナルと同様の新しいビューを構築することに匹敵します。このため、サイズが 10 MB のビューがあり、その 4 つの列にクリックによるソート (昇順または降順) が設定されている場合は、ビューは約 50 MB ぶんの重みを持つことになり、オリジナルと比べて索引の更新に 5 倍の時間がかかります。また、4 つの各列に昇順と降順の 2 つの矢印がある場合は、合計として 8 つの矢印を持つので、ビューは約 90 MB に等しくなり、索引の更新時間はオリジナルの約 9 倍にもなります。

この機能は慎重に使用してください。すべてのビューの列に上下の矢印を設定しても、アプリケーションは使いやすくならないばかりか、確実に遅くなります。特に、非表示の検索用のビューには、列のクリックソート機能は設定しないでください。このようなビューは、バックエンドのコードだけが使用するので、クリックソート機能は無駄になるだけです。

文書コレクションの取得

企業アプリケーションの分析に長年携わるうちに、エージェントのコード (および、特に PostOpen や QueryClose などのフォームのイベントのコード) で文書コレクションを取得し、これらの文書に対して読み書きを行なうケースがしばしばあることに気づきました。そして、文書コレクションの取得方法が、作成するコードのパフォーマンスに大きく関与することが明らかになっています。

この点を強調すると、適切な文書コレクションを取得することはパフォーマンス面での影響は小さく、コレクションのサイズとそれに対する操作が重要であると考えるのが自然かもしれません。しかし、その反面、コードの最適化という観点からは、コレクションの取得方法こそが、かなり大きな変動要因といえます。ここでは、パフォーマンスの高い順に、それぞれの手法について説明します。

文書のコレクションを取得し、ビューに表示できる値を読み取る必要がある場合は、view.GetEntryByKey または view.GetAllEntriesByKey を使用して適切な NotesViewEntry または NotesViewEntryCollection へのハンドルを取得し、目的の ColumnValues を読み取ることが、最も速くデータを取得する方法です。しかし、この方法の場合、コードを実行するときにビューの索引を更新することが、パフォーマンス向上を打ち消してしまわないか気になります。私たちの経験では、ビューで数個の列を検索することで索引の更新に 100 ミリ秒以上かかることはほとんどありません。したがって、これは実際には問題にならないと考えています。特に、このようなビューの検索はできるだけ合理的に実行したいものですが、このようなビューを保持することで得られる節約は、ビューを最新状態に保つための小さなコストを補って余りあるものです。

何らかの理由でビューにうまく表示できないデータを読む必要があるときは、バックエンドの文書へのハンドルを取得すると NotesItems に直接アクセスできます。このような場合は、view.GetDocumentByKey または view.GetAllDocumentsByKey を使用して NotesDocument または NotesDocumentCollection を取得できます。

メモ: set doc = view.GetNextDocument ( doc ) を使用してビュー内の各文書にアクセスする場合は、次の 2 つの理由で view.AutoUpdate = False を使用する方がよいでしょう。まず、スピードが大幅に改善します。さらに、文書を変更しても (削除も可能)、次の文書に進んだときにコードが失敗しません。

パフォーマンスの観点から、db.ftSearch を使用してバックエンドの Notes 文書のハンドルを取得することは、ビュー経由で文書にアクセスする方法と同じぐらいの速さです。リッチテキストフィールドからの検索が目的の場合は、事実上このオプションしかありません。もちろん、この方法で高いパフォーマンスを得るには、更新済みの全文索引が必要です。db.ftSearch を使用する他の欠点としては、構文が少し複雑になることと、Notes.ini 変数を使用して最大値の設定を変更しない限り、結果セットのサイズに制限があることです。

db.Search を使用して小さなサイズの文書コレクションを得ることは、パフォーマンス的に不利となります。しかし、この方法を使用すると、ビューや全文索引の構築を気にせずに検索を実行できるので、状況に応じた検索が容易です。これは、他のユーザーによって所有され、ビューや全文索引を作成する権限のないデータベースに対して検索を実行できることを意味します。また、コレクションのサイズが大きくなると、db.Search が有利になります。つまり、コレクションのサイズが、データベースに含まれる全文書の 5 ? 10 パーセントに達すると、db.Search が文書コレクションを取得する最も速い方法となります。たとえば、データベースに 10 万件の文書があるとき、検索結果として約 5,000 件以上の文書を得る場合は、db.Search が実際に最も高いパフォーマンスをもたらします。

まとめ

この記事では、パフォーマンスの問題のトラブルシューティングに関し、4 つの手順によるプロセスと、いくつかの効果的な手法を解説しました。これらの内容が、トラブルシューティングへの理解を深め、今後のガイドラインとして役立つことを希望します。索引の更新とコードのヒントは、パフォーマンスに優れたアプリケーションの作成に役立ちます。その後、このプロセスを使用することは当分の間ないかもしれません。しかし、アプリケーションでパフォーマンスの問題のトラブルシューティングが必要になったときは、このシリーズのパート 2 で取り上げる Lotus Notes/Domino 7 のパフォーマンス・トラブルシューティング・ツールについてお読みください。


ダウンロード可能なリソース


関連トピック


コメント

コメントを登録するにはサインインあるいは登録してください。

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=60
Zone=Lotus
ArticleID=340546
ArticleTitle=アプリケーション・パフォーマンスのトラブルシューティング: パート 1: トラブルシューティングの手法とコードのヒント
publish-date=07292005