kintone の性能改善について紹介します

こんにちは、ミドルウェア開発チームの青木(@a_o_k_i_n_g)です。

今回は、サイボウズ製品のひとつである kintone に対して行った性能改善の成果を紹介したいと思います。kintone は面倒なコーディング無しに業務アプリケーションのようなものを作ることができ、様々なデータを格納したり、複雑な条件で検索したりソートしたり、アクセス権もきめ細やかに設定したりできるというサービスです。この kintone はサービスの性質上、多種多様で複雑なクエリを発行します。またデータ量も膨大で MySQL だけで計数十テラバイトのデータが存在しており、クエリの処理時間が長時間かかってしまうこともあります。

サイボウズでは kintone の性能改善に力を入れており、今回はその成果を紹介しようと思います。

はじめに

kintone はテナントごとに大きく使い方が異なり、性能改善が効くケースもあれば効かないケースもあります。例えば kintone 上のデータが 1000 件なのか 100 万件なのかで効果の有無は変わりますし、kintone 上のデータの内容や検索する条件、アクセス権の設定などでも効果は大きく変わります。

そのため今回紹介する改善例は必ずしも全ユーザーに効果があったわけではないことをご了承ください。また、グラフの縦軸目盛りは非表示にしていることも合わせてご了承ください。

kintone の性能改善で得た MySQL に関する技術的な知見はこちらの記事で紹介しているので、ご参考になれば幸いです。 blog.cybozu.io

改善リリース1: 最適化機構の改善

kintone には以前からクエリの最適化機構が備わっており、この機構を通すことで MySQL のレコードスキャン数が最小で済むようなクエリを出力できます。この最適化機構では与えられた検索条件を元にクエリの最適な JOIN 順序を決定したり、あるいはもっとダイナミックにクエリを書き換えて最速のクエリを出力するような処理を行ったりしています。このリリースではこの最適化機構を改善し、より多くのパターンのクエリについて最適化機構を通すようにし、レコードスキャン数の削減を行いました。詳しくはJOIN する順序を制御しようを参照ください。

この修正は、最適化が効くケースでは大きな効果がありました。これは最適化がよく効いたテナントのレスポンスタイムのグラフです。横軸が時間、縦軸がレスポンスタイム95パーセンタイル値を表します。今回の改善に関連する API ごとに色分けしており、いずれもレスポンスタイムが減少しました。 f:id:cybozuinsideout:20190129194638p:plain

この改善で Innodb_rows_read がおよそ半減しました。これは特定のテナントの値ではなく kintone 全体で見た値で、莫大な量のスキャン数を削減出来たことを表しています。 f:id:cybozuinsideout:20190129194708p:plain

改善リリース2: アクセス権判定処理の改善

kintone では、kintone 上で扱うデータにアクセス権を付与することが出来ます。このアクセス権はユーザー側としては非常に便利な機能なのですが、クエリを重くするひとつの要因でもあります。アクセス権判定処理はクエリ上ではサブクエリとして表現され、場合によってはそのサブクエリが大量のレコードをスキャンします。この部分を改善し、カバリングインデクスを使うよう修正しました。カバリングインデクスについてはカバリングインデクスを活用しように記載されているのでご参照ください。これはデータを選択する際にインデクスデータのみのスキャンで済むようにしたもので、レコードスキャン数が多かったり何度も実行されたりするようなケースでは特に効果があります。

その結果、データ取得系 API のレスポンスタイムが改善しました。劇的な改善というほどではありませんが、この改善はほとんどすべてのユーザーに対して効くものです。 f:id:cybozuinsideout:20190129194730p:plain

改善リリース3: 巨大オフセットの対応

kintone のデータをバックアップするなどの目的で、kintone のデータをクロールするようなアクセスはしばしばあります。kintone 上に数十万件やそれ以上のデータを格納しているテナントの場合、社内では巨大オフセット問題と呼ばれている問題が発生します。

巨大オフセット問題とは、通常は遅くないクエリであっても「全データ100万件のうち99万件目から500件取得」というようなクエリだと遅くなる、という問題です。以前の記事中の巨大オフセットについてではユーザー側に啓蒙するだけに留まっていましたが、さらなる検討により一部のケースではクエリ側で改善できることが判明しました。一般的な kintone の重いクエリは複数のテーブルを JOIN しているのですが、特定のパターンに関して言えば JOIN 処理を行う前に OFFSET を処理できることが判明し、その改善を取り込んだものです。

こちらは非常にヘビーに kintone を使うことで社内で有名だったテナントのスロークエリ量です。塔のようなスロークエリの塊がほぼ無くなりました。 f:id:cybozuinsideout:20190129194753p:plain

この改善にマッチしたとあるテナントでは、スロークエリ量が 1/100 まで削減できたケースもありました。

一部のケースでは改善できましたが、巨大オフセットは未だ我々を悩ませる問題であり、ユーザー側で改善できるものでもあります。こちらにユーザー側で行える対策を記載しているので、今後も周知していきたいと考えています。

kintoneの大量レコード取得を高速化 - cybozu developer network

改善リリース4: 不要なクエリの削除

こちらの改善ではある不要なクエリを発行しないようにしました。以前は効果的であったキャッシュとしての意味を持つクエリだったのですが、データベースのバージョンやフレームワークが変わるにつれてキャッシュの意味を成さなくなり、それだけでなく性能劣化を引き起こすケースもあることが判明したので削除しました。

この改善は多くのテナントに効くものですが、中でも社内でヘビー級で有名な特定のテナントには非常に良く効き、関連する API のレスポンスタイム90パーセンタイル値がほぼ 1/10 にまで下がりました。 f:id:cybozuinsideout:20190129194810p:plain

改善リリース5: 最適化機構の改善

前述した最適化機構を更に改良し、より最適なクエリを発行するよう修正しました。MySQL がスキャンするレコード数が最小で済むようより多くの情報を最適化機構に取り入れた改善です。最適化が効くケースのテナントでは大きく改善し、日々のスロークエリ量を削減することに成功しました。 f:id:cybozuinsideout:20190129194825p:plain

改善リリース6: マシンリソース増強

とある環境に非常に重いリクエストを多数投げられており、データベースサーバーの CPU 使用率を限界まで使い切っていました。kintone のデータベースサーバーは一般的に見てハイスペックなマシンであり、リソースが足りなくなることはそう多くないのですが、このケースではデータベースサーバーの CPU を使い切ってしまい待ち行列が発生しているという状況でした。

発行されている重いクエリを改善するというのがおそらく一番真っ当な方法なのかも知れませんが、当時はその重い原因となるリクエストについて調査がさほど進んでいなかったことから CPU コア数を増強することにしました(その後クエリの改善も行っています)。CPU を増強したことにより待ち行列が解消され、レスポンスタイムが改善しました。 f:id:cybozuinsideout:20190129194838p:plain

Innodb_s_lock_os_waits などの各種項目でも改善が見て取れました。 f:id:cybozuinsideout:20190129194853p:plain

改善リリース7: カーネルパラメータの修正

このリリースでは 3 つの効果的な改善を取り込みました。ひとつ目はあるタイミングで意図せず CPU が省電力モードになってしまっていたのを修正したことです。CPU の省電力モード等のモードは Linux だと /sys/devices/system/cpu/cpu*/cpufreq/scaling_available_governors で設定されており、powersave から performance に変更することで搭載している CPU を最高性能で動かせるようになります。

また、MySQL への効果的なインデクスを追加してあるサブクエリをカバリングインデクスにしたことと、アクセス権判定時のレコードスキャン数を減らすという改善を取り込み、いずれも効果がありました。

これらの改善により、kintone の 主要な API のレスポンスタイムがほぼ半減 しました。特定のユーザーにのみ効果があるような改善ではなく、すべてのユーザーに効果があります。 f:id:cybozuinsideout:20190129194909p:plain

今後の kintone の性能について

おかげさまで kintone の契約社数は順調に伸びています。その分様々な kintone の使い方をするユーザーも増え、重いリクエストを連続で投げ続けられるなどは日常茶飯事です。

例えばこちらはとある環境下の MySQL のスロークエリ量です(この環境下には多数の kintone テナントがいます)。このグラフは半年間ほどのレンジで、日ごとにどれだけのスロークエリが発行されたかを表しており、右に行くにつれて増大し負荷がどんどん高まっていることがわかると思います。この環境では昨年11月の改善が効いたので最後は減っていますが、減らせていない環境も残念ながらあります。 f:id:cybozuinsideout:20190129194923p:plain

今回紹介したもの以外にも多数の性能改善を行っています。データベース側だけでなくアプリケーション側も改善し、例えば不要なリクエストを減らしたり不要な処理を削減したりなども行っています。また、特定のテナントが激しく kintone を利用しても他のテナントに性能的な影響が出ないようにする仕組みなども取り入れています。

昨年は kintone 史上最も性能改善が取り込まれた一年になりました。それでもまだ性能問題が無くなったわけではなく、ご迷惑をお掛けしているお客様もいます。今後も引き続き快適な kintone を使えるよう日夜努力して性能改善を行っていく予定です。今後の kintone の性能にご期待ください。