はじめに

NTTデータ先端技術株式会社にてアジャイル開発並びに技術調査業務に従事している志田です。業務では通常のアジャイル開発プロジェクトとは別に部署内や社内でトラブルが発生した際の問題解決支援を行うこともあります。プロジェクトが抱えるトラブルの中で多く発生するものは、やはり性能問題です。システム開発も終盤に近づきアプリケーション自体は動くようになってきたが、特定のページの表示が遅い、全体的に動きがもっさりしているといった課題があり、解決のための支援を要請されることがあります。性能問題を解決するには特定の業務課題を解決するという知識より、インフラからアプリまで広い技術が求められる分野です。

今回は、性能問題の解決するステップから包括的な技術が求められる理由を提示していきたいと思います。

性能改善の基礎

性能でトラブルになるプロジェクトの多くは、まず性能指標が定義されていないことが多いと感じています。顧客やプロジェクト内でシステムに対して漠然と遅いと感じているが、どこまでの目標を満たすべきなのかを定義しないまま開発を進めているという状況です。やはり目標を決めないとそれを達成するためのアクションも取れない、取らないものですので性能に関してはまず性能指標を決めるところから実施する必要があります。昔からよく使う性能指標は以下の2点です。

  • スループット
  • 単位時間あたりに処理するタスクの数という意味です。Webシステムではページ表示のためのHTTPリクエストを1タスクとして、1秒間に処理できるリクエスト数を計測することが多いです。この時の単位がTPS(Throughput Per Second)です。
  • 応答時間(ターンアラウンドタイム)
  • 1回のタスクを実行してから実行が完了するまでの時間が応答時間(ターンアラウンドタイム)です。Webシステムではページ表示のリクエストが到達してから、サーバでの処理を完了するまでの時間としてミリ秒単位で計測することが多いです。

他にも同時接続数なども指標値を決めることもありますが、多くの場合は上記2点を決めておくことが多い印象です。また、応答時間に関してはどの時点を開始とみなすか、どの時点を終了とみなすかが開発側と顧客側とで意識違いが生まれることがあります。よくあるのは応答時間を3秒以内と定義したが、開発側はサーバでの処理時間を3秒と認識し、顧客側はブラウザのアドレスバーにアドレスを入力してから画面表示が完了するまでの時間と認識しているというケースです。サーバでの処理が3秒で完了したとしても、その後HTMLデータや画像、CSS、JSのダウンロード、ダウンロード後のレンダリング処理やJavaScript処理が実行され最終的に画面が表示されるといった時間が追加で発生するため、画面表示自体はすでに3秒を超えてしまいます。このダウンロードやブラウザでの処理はネットワークの帯域、状態や端末自体のスペックに依存して変化してしまう部分ですので、開発者の立場としてはTTFB(Time To First Byte)の値を応答時間とするのがやりやすいと思います。こちらはブラウザからリクエストを投げたのち、レスポンスデータの最初の1バイトが返ってくるまでの時間で、サーバでの処理時間と同一の値です。

こういった性能指標の策定の仕方も近年変わってきており、SRE界隈で提唱されているSLA/SLO/SLIといったサービスレベルに関する規定を行い、サービスレベルの中の一つの要素として性能を担保していくという進め方も増えています。

計測とボトルネック分析

性能目標を定め、測定方法を決め、いざ測定となり、測定した結果が目標に満たないといった場合に何が目標達成を阻害する要因になったのかを調べるのがボトルネック分析です。システムのボトルネックとなる部分は数限りなくありますが、それの絞り込みを行うために負荷を変えながらシステムのリソースを確認していきます。以下にボトルネック分析でよく確認する項目を列挙します。

  • アプリケーションのCPU利用率
  • 徐々に負荷を上げていった場合に単調増加でCPU利用率も増加していき最終的にCPU利用率100%まで到達すれば問題なし
  • アプリケーションのメモリ使用量
  • 連続した測定を行った場合に増加と減少を繰り返し、ベースラインが増加していなければ問題なし
  • データベースのCPU利用率
  • 測定期間中に100%に張り付く事態が発生しなければ問題なし
  • データベースのメモリ使用量
  • 連続した測定を行った場合に増加と減少を繰り返し、ベースラインが増加していなければ問題なし

他にもIO使用量であったりネットワークの使用量、キューサイズであったりミドルウェアのリソース状況であったりを確認していきますが、まずは上記の4点を確認することが多いです。特にアプリケーションのCPU利用率が重要なメトリクスになり、これが適切に100%に到達している場合はアプリケーションがインフラリソースを使い切っている状況になりますので、ボトルネックとしては非常にわかりやすい部類です。アプリケーションのCPUが100%に到達しない場合はどこか他の場所にボトルネックがある可能性があります。アプリケーションのスレッドの中でデッドロックや待ちが発生しているであったり、外部システムからの応答が遅延しているであったり、ディスクへの書き込みや読み込みが大量発生していたりとアプリケーションが動き続けているわけではなく、どこか別の場所でアプリケーションを停止させるイベントが発生していると考えるべきです。

経験上、以下のようなパターンでのボトルネックがありました。

  • 画面を構築する際にアプリケーションで繰り返し処理の中でSQLを実行している
  • 1画面表示するのに数千回SQLを発行しているといった問題があり、このため画面表示に時間がかかっているという問題がありました。この場合アプリケーションサーバのCPUが100%に到達する前にデータベースのCPUが高騰していく傾向にあります。
  • 処理の中で都度都度ディスク上のファイルを読み込んでいる
  • アプリケーションの設定ファイルの情報を処理のたびに読みだし、書き込みをするといった問題もありました。こちらはアプリケーションのCPUが100%に到達する前にディスクIOがネックになってしまい一定以上CPU利用率が上がらないという結果になりました。
  • 処理のたびにコネクションを貼り、かつプールやクローズをしていない
  • 他のシステムのAPIを呼び出したり、独自にソケット通信をしたりといったシステムでファイルディスクリプタが枯渇するといった問題が発生しました。こちらはCPU利用率が100%に満たないうちにリソース開放待ちが発生して応答が遅延していく動きをみせます。

上記のボトルネックのパターンはまだクラウドが普及しきる前のオンプレミスサーバでよく発生していたものです。先年、クラウドのPaaSを利用してシステムを構築しましたがCPUもメモリもまだまだ余裕があるのに一定の負荷を超えると応答時間が著しく低下するといった問題にあたりました。原因究明したところ、そのPaaSサービスではSNATの通信のために利用できるポートに制限があり、SNAT通信のポート開放待ちが発生しているということだったのでした。クラウドは簡単に利用できる代わりにオンプレミスでは存在しなかった特別な制限があることもあり問題解決にはより広い技術が求められています。

改善のアプローチ

ボトルネックの分析ができれば、あとはボトルネックを解消することが性能改善のアプローチになります。改善方法も概ねよく取られる手法があり、その手法を提示します。

  • キャッシュを利用する
  • キャッシュという言葉だけでは非常に広いのですが、Webシステムであれば静的コンテンツをキャッシュするCDN(Contents Delivery Network)からWebサーバソフトウェアであるIISやApache、nginxでのキャッシュ、ミドルウェアが保持しているキャッシュやデータベースのクエリとその結果のキャッシュやKVSを利用したキャッシュなどさまざまなサービス、ソフトウェアがキャッシュ機構を持っています。キャッシュを利用することで再計算をする回数を軽減でき、性能に関して大きな影響が期待できます。ただし、キャッシュを利用することによりいつ、どういう契機でキャッシュのクリアをするのかを検討する必要があり運用が煩雑になるというデメリットもあります。
  • コネクションプールを利用する
  • サーバとサーバの通信のためのコネクションであったりデータベースとのコネクションであったりはコネクションプールを利用できます。コネクションはコネクションを貼るという行為自体も低速な処理になり、かつ、前述のファイルディスクリプタであったりポートであったり限りあるリソースを消費する行為です。コネクションプールを利用することでコネクション生成のコストを低減するとともに、リソースの枯渇を防げるようになります。ただし、コネクションプールのサイズという新たな制約事項を生み出してしまうためコネクションプールの開放待ちという別の性能課題が発生しないように設計が必要です。
  • サーバのスペックを上げる
  • 従来は禁じ手の側面が強かったのですが、今はアプリケーションの改修コストやアーキテクチャ再検討のコストに比べ、非常に安価にサーバのスペックを上げられるようになりました。このため、CPUボトルネックであればスケールアウト、スケールアップを行うのが最も手っ取り早い方法となります。

これらの手法は昔から実施されてきた手法ですが、今ではあえて改善しないというのも一つの選択肢になってきていると感じています。これは性能課題の本質として、性能を向上したいというのはあくまで目的を達成する手段であり、性能が悪いことによるユーザ体験の低下を防ぐのが本来の目的です。このため、例えば遅い処理に対してバックドロップを表示するなどの手法で処理が遅くてもユーザ体験が低下しないようにするというのも対応として十分だと思います。

経験上、システム開発の初期段階では性能は軽視されがちです。これは性能測定をしようにも、測定対象のシステムがまだできていないため問題が認識しがたいものとなっているためです。性能は非機能要求のため、多くの場合、ある程度の機能要求の実現がなされた後、つまりシステムがある程度動くようになってから目を向けられる分野です。こうなると、性能問題が問題として認識されるのはシステムがある程度組み上がってからとなります。

しかし、性能問題が混入されたタイミングはいつかというとシステムのアーキテクチャにあるという場合が多く、性能問題が発生することが原因で最も多いのはこちらです。他の非機能要求である可用性などは冗長構成を取るなど固定的な対策方法があるのに対し、性能対策はシステム構成に応じて多種多様な対策があり、アプリケーションだけ、インフラだけに目を向けていると性能問題が発生しないアーキテクチャを考えることは難しいでしょう。。アプリケーションにもインフラにも精通したエンジニアがアーキテクチャを検討し、そのうえで適切な測定をしてはじめて性能問題の発生を防げます。このように、性能問題自体はそれだけで一つの分野になるほど奥が深く、かつ解決するのが難しいものです。このため、アプリケーションもインフラも両方できるエンジニアが活躍しやすい分野だと思います。

[PR]提供:エヌ・ティ・ティ・データ先端技術