新機能がサービスのパフォーマンスに悪影響を与えないか素早く推定する

 こんにちは、 id:shiba_yu36 です。

 先日、新しい機能や改善を加えようとする時に、それがデータベースに対して悪影響を及ぼさないか、どのように検証すれば良いですかという相談を受けました。つまり、新しく作った機能を導入した瞬間にデータベースが高負荷になりサービス全体に影響を与えたりしないか不安であるという相談です。その相談に対して僕は、簡単な計算式を作り、その機能のデータベースへの影響度をざっくり推定することで、リリース前に時間をかけずにパフォーマンスに悪影響を与えないか推定できるという話をしました。

 この話をしていて、「リリースする前に新機能や改善がサービスのパフォーマンスに悪影響を与えないか素早く推定する」ことは、経験のあるエンジニアは自然とやっているけれど、あまりブログなどで言語化されていないと感じました。そこで、今回はこのことについてブログに書いてみます。


なぜ機能をリリースする前にパフォーマンスに悪影響がないか考える必要があるか

 サービスの新機能を作ったり、何かしらの改善を加えたりした場合、リリース前にある程度それがサービス全体のパフォーマンスに悪影響を与えないか検討すべきです。なぜなら、何も考えずにパフォーマンスに悪影響を与える機能を投入してしまった場合、その機能の影響でサービスの全体のレスポンススピードが悪化したり、最悪の場合サービス自体が落ちるなどといったことが起こりうるためです。

 パフォーマンスに悪影響を与える原因はいろいろあると思いますが、例えばMySQLなどのデータベースで考えるなら

  • 非常に重いクエリを投げてしまっていて、DBが詰まり、他の機能のレスポンスすら返せなくなった
  • クエリ単体は軽かったけど、あまりにも多くクエリを投げすぎていて、DBが詰まってしまった
  • 転送量が多すぎるクエリを投げてしまっていて、ネットワークで詰まってしまった

などといったことが起こらないように、事前に検証しておく必要があります。



ざっくりとパフォーマンスへの影響度を推定する

 上記したとおり、機能リリース前にサービスのパフォーマンスに影響を与えないか検証する必要があります。かといって、はじめから詳細な検証をしようとしてしまい、時間がかかりすぎても問題です。

 そこで僕はパフォーマンスに悪影響がないか心配な機能を作ったときは、詳細な検証に移る前にざっくりとパフォーマンスへの影響度を推定し、そこで問題がありそうと判明したときのみ詳細に調査するということをしています。ざっくり推定までで問題が起こらないということが分かれば、時間をかけずに検証を終えることが出来ます。

 おおよその影響度の推定は以下のように行っています。

  • 影響度を要素分解し計算式を作り、ざっくりした数字を計算する
  • 現状の数字と比較し、影響がどの程度か確かめる

影響度を要素分解し計算式を作り、ざっくりした数字を計算する

 パフォーマンスへの影響度を推定するときにまずやらなければならないのは、その機能によってどの程度数字が増えるかを計算することです。影響度を要素分解し計算式を作り、実際の数値を当てはめれば、ある程度の増分が把握できます。

 例えば「新機能のDBへの影響度」を知りたい時、要素分解し、以下のような計算式を作っています。

 まず新機能のDBへの影響度というのは、つまりその機能のエンドポイント全ての影響度を合計したものと考えます。エンドポイントとは単なるページの表示や、何かしらの更新処理など、その機能を作るための処理全てです。

 次にそれぞれのエンドポイントごとのDBへの影響度を考えます。それぞれのエンドポイントごとの影響というのは、1秒あたりのエンドポイントのアクセス数 * 1アクセスあたりのDBへの影響度です。ここで1秒あたりとしたのは、パフォーマンス測定をする時は秒単位で考えることが多いためです。

 次に1アクセスあたりのDBへの影響度とは何かを考えます。1アクセスあたりのDBへの影響度は、1アクセスでの「それぞれのクエリの影響度」 * 「そのクエリの実行数」を合計したものと考えられます。

 最後に1クエリの影響度ですが、これはEXPLAINを利用したり、実際に実行してみたり、転送量を計算したりして、気になることを調査すると分かるでしょう。


 ここまで分解すれば、アクセス数をアクセスログから計算したり、転送量を計算したりすることで、新機能をリリースすると

  • インデックスが効かず、200msほど時間がかかるクエリが2query/sほど増えそう
  • SELECTのクエリ数が30query/s程度増えそう
  • 転送量が30KB/sほど増えそう

などといったことが計算できるようになります。

現状の数字と比較し、影響がどの程度か確かめる

 新機能をリリースした時の増分が計算できれば、あとは現状の数字と比較すれば影響の大きさが把握できます。例えば「SELECTのクエリ数が30query/s程度増えそう」の場合

  • 元々30000query/s捌いていたのであれば、0.1%増くらいなら問題は少なそう
  • 元々300query/s程度であったのなら、10%増でもしかしたらまずいかもしれない

のように影響の大きさを推定することが出来ます。

 ここまででどう考えても問題が起こらないであろうと判断できれば、詳細な検証をせずにリリースしても良いという判断が出来ます。おおよその推定でこのように判断が出来れば詳細な検証をせずに済むので、素早く検証ができるというわけです。

 逆にここまでで問題が起こる可能性があると判断したら、そこで初めて、詳細な調査や負荷試験を始めれば良いでしょう。

ケーススタディ:新機能のDB転送量を不安に思っているケース

 それでは実際に例を使って考えてみます。

  • 特定のページに表示する要素を増やしたい
  • 表示する要素を増やすためにはDBへのクエリが2つ増える
  • クエリ1は転送量が気になるクエリであり、クエリ2は一瞬で返ってくるようなクエリである

という例を考えてみましょう。この場合心配なのは転送量が多くなりすぎないかです。

大体の転送量を計算する

 今回のケースの場合、エンドポイントは1つだけです。そのため、そのエンドポイントのアクセスでの影響度だけ考えます。

 まず、1秒あたりのエンドポイントのアクセス数を考えます。これはアクセスログやGoogle Analyticsなどで調べられるはずです。例えば日間で30万PVだとすると、秒間なら 30万 / (24 * 60 * 60) = 3.5PVほどとわかります。

 次に1アクセスあたりのDBへの影響度を考えます。これは今回のケースなら、「クエリ1のDBへの影響度 * 1 + クエリ2のDBへの影響度 * 1」で計算できるはずです。さらにクエリ2が何の心配もないなら単に「クエリ1のDBへの影響度 * 1」を考えれば良いでしょう。

 最後に「クエリ1のDBへの影響度」、つまりクエリ1の転送量を考えます。例えばDBのデータ定義的に1行30バイト程度で、平均的に3000行くらい取得するクエリになりそうと考えると、転送量は30 * 3000 = 90KBとなります。


 あとはこのデータを計算式に入れます。

  • 1アクセスあたりクエリの転送量 = クエリ1の転送量 + クエリ2の転送量 ≒ クエリ1の転送量 = 90KB
  • DBへの影響度 = 3.5PV/s * 90KB = 315KB/s

となり、今回の機能改善での影響は315KB/sの増加であるとわかりました。

現状と比べ、影響度を推定する

あとは現状と比べるだけです。

もし現状の転送量が1MB/s程度であれば、今回の機能により30%ほど転送量が増えてしまいます。直感的には影響度は非常に大きいでしょう。この場合、詳細の検討や、別の方法での実装へ移ります。

もし現状の転送量が300MB/sであれば、今回の増分は0.1%ほどです。その場合まあ問題ないだろうと考え、そのままリリースできます。



コツ: 悪い側に倒して考える

 今回のようにざっくり推定する時に、僕が一点だけ気をつけていることは、悪い側に倒して考えることです。

 例えばアクセス数が5万ほどあるページの改善の影響度を考える時に、もしかしたらそのページが一日10万PVになる日があるかもしれません。他にも平均的には転送量が10KBくらいのクエリだけど、もしかしたら転送量が多いクエリが発行されるページが多めにアクセスされ、実際の平均値はもう少し増えるかもしれません。このような時に問題が起こっても困ります。

 そのため僕は悪めのケースを考えて、そんな悪いケースで考えても問題ないなら問題なかろうという考え方をしています。

 例えば先程の例なら、日間30万PVなら今後の成長も見越してとりあえず日間100万PVとして、転送量90KBより増える可能性がありそうなら転送量120KBくらいにとりあえず増やして計算します。その数値で計算すると1.4MB/s程度増えることがわかります。もしこの悪めで考えた数字で問題がなければ、通常時は全く問題がないと判断できます。



まとめ

この記事ではサービスのパフォーマンスに悪影響がないか素早く推定する方法について、データベースへの平均的な影響度がどの程度かを推定する例で説明しました。しかし、このようにひとまずざっくりとした数字を出して現状と比べるという方法は

  • アクセスが一気に集中した場合にどうなるか
  • アプリケーションサーバーのパフォーマンスが問題ないか

など、データベースに関わらず利用できる方法かなと思います。今回書いてみたやり方が参考になればと思います。また、別の方法で考えているなどあれば教えてください。

hatenacorp.jp