さんぽしのBLOG

さんぽしのBLOG

非理系の大学生が頑張りに頑張り抜いてエリートエンジニアになるシンデレラストーリー(予定)です。

CyberAgentの2daysインターンに参加してパフォーマンスチューニングを完全に理解した話

こんばんは

さんぽしです。

6/6 - 6/7でCyberAgentの2days サーバーサイド向け 開発型インターンシップに参加していました。

2日間オンラインでパフォーマンスチューニングを実際に行い、その後解説をもらうという流れでした。

この記事ではパフォーマンスチューニング何も分からんの僕がアプリケーションに対してどのような修正を入れて行ったのかをざくっと時系列で紹介していきます

ツール色々いれる

まず事前にまとめておいた↓のgistを基に

  • alp
  • pprof
  • pt-query-digest

を導入しました。

Cheat Sheet on Performance Tuning · GitHub

一瞬で導入できたのでgistにまとめといてよかったという気持ちになりました。 ついでにソースコードをGit管理して、Nginxとかの設定ファイルは手元にバックアップしました。

vimmerなのでファイル編集とかに困らなかったのは楽でした

pprof見てみる

なんかgetArticleTagNamesという関数が遅そうってことが判明 見てみると明らかにN+1だったので修正を入れました。

が、点数に変化なし。

ちなみに初期スコアは750点くらいです

DBから画像バイナリを抜く

DBに画像のバイナリが直で突っ込まれていたので、修正しました。

具体的には

  • photo_binaryに挿入しようとしている部分を代わりにlocalに保存するように変更
  • デフォルトの画像を落として、わざわざdecodeしないようにする
  • Nginxで配信するように変更

をしました。

が、またも点数に変化なし。泣きました

スロークエリを見る

スロークエリをpt-query-digestで解析すると、index貼ってないのにわちゃわちゃSELECTしてる部分が色々あったので適当に良さげなindexを貼りました。 スコアが一気に1500くらいになりました。

記念のスクショ

この時に暫定一位になっていて、命は短いだろうと悟っていたので記念にスクショを撮る

f:id:sanpo_shiho:20200607173717p:plain

MYSQLとかNginxの設定をいじる

ネットに「こうするといいよ!」みたく転がってる設定をいれるが効果なし ちなみにこの辺りで2位に転落しました

初日の中間解説でindexの解説がされる

この解説でみんながindexを張り出したが意外と抜かれなかったのでN+1改善とかが後になって効いてきてるのかもーとか思ってました

deferをなくす

deferをなくすといいらしいとどこかで見た気がしたので全部のdeferをなくしました。

効果がなかった上に非常にめんどくさくて悲しくなりました。

Template周りの改善

以下の記事を参考にtemplateを毎回Parseしていたので修正しました。

mercari_go_isucon.md · GitHub

20点くらい上がりました。

getLoginUsers改善

getLoginUsersというLoginしているUserを取得する関数が遅かったです RedisでややこしいLoginUserの管理をしているのが原因だったので、"login_users"というkeyでセット型でLoginUserを管理するように変更

200点くらい上がりました

いいね数をRedisに載せる

いいね数の取得のクエリが時間がかかってそうだったのでいいね数をRedisで管理

具体的に

  • どの記事に誰がいいねしているかというのを"iine_{articleID}"というkeyのセット型でRedisに載せる
  • GetInitializeでDB内に既に入っているいいねをRedisに載せるように修正
  • PostIineでDBと共にRedisにもいいねの情報をいれる
  • getArticleIineUsersとgetIineCountでDBからではなくRedisからいいねの情報を取得

の変更を加えました。

200点くらい上がってこれで1950点くらいになりました。

getArticleTagNames再改善

N+1を直したgetArticleTagNamesがいまいちまだ遅いのでsql/databaseではなくgorpを導入して一気に複数列を取得するように変更しましたが効果無し。

また最終時間ギリギリにRedisに載せようとしましたが時間が足りず…

getPopularArticlesが遅い

getPopularArticlesは直近一定期間でいいね数が多かった記事を取得する関数です。 これが遅くて初日から何度か修正を試みたのですが、最後まで修正できませんでした。

まずはSQLを改善することをTableの構造変更も含めて検討しました。 しかし、直近一定期間のいいね数をTableに格納するとなるとかなりの頻度で定期的に更新をかけなければいけないので諦めました。 (ベンチが走るのは一瞬なのでこれでもいけたかもとは思いましたが…)

次に、先ほどのようにRedisに載せることを検討しました。 しかし、

  • Redisからいいね取得→直近の物だけに絞る→記事ごとにいいね数を数える→いいね数上位5件を取得
  • 定期的にRedisを更新

がかなり煩雑すぎる処理になるなと思い、諦めました。

あとで聞いた話だと優勝のhppくんはRedisでやったってことなのでほへぇってなりました。

反省点

  • deferやtemplate改善などのプロファイルから見えてない部分の改善は優先度を下げればよかった(実際そこまで効果が大きくなかった)
  • Nginx、MYSQLの勉強不足で設定まわりを弄れなかった(特にNginx)

終わりに

終結果は1950点ほどが最大値でした。順位は最後2時間見えないようになっていたので分かりませんが、20人中4-7位辺りだと思います。(多分…)

パフォーマンスチューニングは本当に全然経験がなく、今回ガッツリ2日間の開発-解説で本当に大きく成長できたな〜と思いました。

とても楽しい2日間でした、ありがとうございました!!

9月のISUCONにむけてパフォーマンスチューニング完全理解していきたいと思います