実録パフォーマンス改善 - 高速化のためアーキテクチャやアルゴリズム選択から見直すSansanの事例

インフラの特性をふまえ、ミドルウェアの挙動を理解し、プロファイリングによってボトルネックを把握し、要求に合ったアーキテクチャを選択する。そういった工夫を重ねることでアプリケーションのパフォーマンスを改善する事例を、Sansanの千田智己さんに聞きました。

実録パフォーマンス改善 - 高速化のためアーキテクチャやアルゴリズム選択から見直すSansanの事例

アプリケーションの設計・実装方法を変えることで、性能が格段に向上するケースは数多くあります。有名IT企業のエンジニアは、どのような方針のもとでアーキテクチャあるいはアルゴリズム選択などでパフォーマンスを改善しているのでしょうか?

法人向けクラウド名刺管理サービス「Sansan」や個人向け名刺アプリ「Eight」を提供するSansan株式会社の千田智己さんに、これまで取り組んできた事例と、そのノウハウを教えていただきました。

1

千田 智己(せんだ・ともみ)
Sansan株式会社 Sansan事業部 プロダクト開発部
情報処理系専門学校卒業後、受諾開発・SESを行う地元企業に新卒入社。自治体向けシステムのPJにて、業務アプリケーション等の設計・開発に従事。上京後、旅行系の基幹システムやMDM開発、証券系システムのアーキテクチャ設計からインフラの構築・実装等、幅広く対応。2013年6月にSansan株式会社に入社。以来、法人向けサービスSansanの開発に携わる。

バッチのパフォーマンス改善
~インフラの特性をふまえて設計する~

──千田さんがこれまでSansanで行ってきたパフォーマンス改善の事例について教えてください。

千田 過去の事例から順に話すと、まず会社情報の名寄せを行うバッチのパフォーマンス改善を行いました。私が改修する前の仕様は「数分に1回起動して、データを1,000件ずつ処理する」というものだったのですが、サービスの利用者が多くなるにつれ、1,000件ずつの処理では名刺の流入に追いつかなくなっていました。

──なぜ1,000件ずつという制限がかかっていたのでしょうか?

千田 これには歴史的な経緯があって、Sansanではかつてインフラをオンプレで運用しており、ある高速ストレージを導入していました。この機器には「高い負荷をかけると温度が急上昇する」という特性があり、一定以上の温度になると安全機構が動いてストレージが落ちてしまうんです。

──それは恐ろしいですね……。

千田 そのため、わざとバッチの処理量を抑えていました。その後でインフラをAWSに移行しましたが、バッチ処理には手が入らないままだったんです。

──1,000件ずつを数千件にするなどで、暫定対応できなかったのでしょうか?

千田 おそらくできたのですが、根本解決にはなりません。ビジネスが右肩上がりに成長して継続的にデータが増えていたので、一時的に処理件数を増やしてもすぐに限界が来てしまいます。また、かなりの量のデータが滞留していたので、それらを救出するためにもバッチの性能を大幅に向上させる必要もありました。そこで、バッチのコードを完全に書き直すことに決めたんです。

──バッチの作り直しを決めたとき、何を大事にしてコードを書こうと考えましたか?

千田 一番大事にしたのは、スループットを出し続けられることです。改修後は1時間に1回起動するバッチとして作り直したのですが、今後どれだけデータが増えたとしても確実に1時間以内に処理を終えられるようにしました。結果的には2分以内には実行完了するほどにパフォーマンス改善でき、データが約30倍に増えても耐えられるようになりました。

2

──バッチのパフォーマンス改善を行う際には、何を大事にしてアルゴリズムの変更を行うべきでしょうか?

千田 まず、プロファイリングをすることが重要だと思います。I/OバウンドなのかCPUバウンドなのか、どの箇所がボトルネックになっているのかを可視化して、丁寧にチューニングをすること。

それから、「クエリを発行する回数をどれだけ減らせるか」も非常に重視しています。クラウド環境を利用しているとネットワークのレイテンシーを忘れがちになりますが、これによる処理の劣化は馬鹿になりません。

AWSで、バッチとデータベースがAZ(Availability Zone)間をまたぐ場所にある場合、同一AZの場合と比べると、ネットワーク通信にかかる時間が数倍は違ってきます。ですが、クラウド環境では、各サーバがどの箇所に配置されるかを自分たちでコントロールすることは難しい。そのため、できるだけネットワークの影響を受けないように、1回のデータベースアクセスで取得・更新するデータの量を多くすることが大事です。

Webアプリケーションの場合、1回のリクエストでクエリを発行する回数はそれほど多くありません。でも大量データを扱うバッチの場合、クエリの発行回数はかなり多くなります。だからこそ、よりネットワークのレイテンシーを考慮しなければいけない。アプリケーションを開発するときは、必ず「インフラがどういう特性を持っているか」を気にしなければいけないんです。

ヒント句(pg_hint_plan)の導入
~実行計画をコントロールして、効率の良いクエリを~

──他にはどのようなパフォーマンス改善の事例がありますか?

千田 ヒント句(pg_hint_plan)を導入して、発行されるクエリを安定させた事例があります。Sansanでは1つのデータベースのなかに大量の企業データを共存させるマルチテナントDBを用いているのですが、PostgreSQLの特性上、マルチテナントDBを利用するとどうしても実行計画が安定しなくなるという課題がありました。

──なぜ、実行計画が安定しないのでしょうか?

千田 SELECT句のWHERE句を複合条件にした場合の「行数推定」がうまくいかなくなるからです。リレーショナルデータベース内部では検索条件をもとに「これくらいのデータ量が返ってくるだろう」という行数推定が行われ、それをもとに実行計画が策定されますが、PostgreSQLでは特定の複合条件の場合に、行数が過少に算出されてしまいます。

その結果何が起きるかというと、結合アルゴリズムにおいて、本当はハッシュ結合(hash join)を使ってほしいのに、ネステッドループ結合(nested loop join)が選択されるケースが出てきてしまう。数万件×数万件のデータでネステッドループ結合をするようなことが起こり得ます。

──膨大な計算量になってしまいますね。

千田 そこで導入したのがヒント句でした。ヒント句を用いることで、特定のクエリにおいて常にハッシュ結合を選択させることが可能になります。行数推定を無視して、 実行計画をコントロールするわけです。

余談ですが、ヒント句を一度使うと(クエリの安定化が簡単にできて)やめられなくなるので、私たちは「ヒント句中毒」と呼んでいます(笑)。

──ヒント句中毒(笑)。

千田 PostgreSQLの行数推定の仕様については、Sansanのエンジニアである加畑(博也)が詳細にまとめた発表資料があります。ぜひ読んでみてください。

──すごい情報量。これを読むだけで、行数推定の仕様をかなり深く理解できますね。

千田 他にもPostgreSQLの実行計画を理解する上では、「第30回PostgreSQL勉強会」でTISの方が発表された資料がおすすめです。情報の凝集度が高くて勉強になるので、自分もよく参考にしています。

名刺検索処理の高速化
~コンピューター内部で何が起きているのかを理解する~

エンジニアHubに会員登録すると
続きをお読みいただけます(無料)。
登録のメリット
  • すべての過去記事を読める
  • 過去のウェビナー動画を
    視聴できる
  • 企業やエージェントから
    スカウトが届く