パフォーマンス
Kii Cloud はクラウド技術により極めて多数のユーザーに対応できる設計になっていますが、特定の箇所にデータが集中しすぎるとパフォーマンスが低下することも考えられます。ここでは、モバイルアプリ側での処理としてパフォーマンスの問題に対処する方法を示します。
このトピックでは、1 ユーザーまたは 1 グループあたり数十万件を超えるような規模でデータを扱うようなケースを想定した注意点を記載しています。最大でも数百件程度のデータしか扱わないような場合は、下記の データ転送 のトピックを除いて、パフォーマンスに対する考慮は特に必要はありません。
ユーザー数による影響
ユーザー数が増えた場合、それらのユーザーが持つデータは、Kii Cloud 側で自動的にスケールする設計になっています。ユーザー数の増加そのものが、パフォーマンスに影響を与えることは基本的にありません。
ただし、多くのユーザーから特定の Bucket にデータを集中して書き込むと、Bucket 内の Object 数が増えて問題を起こすことがあります。特にアプリケーションスコープやグループスコープの Bucket にデータを書き込む際は、ユーザー数の観点からの評価も行ってください。たとえば、アプリケーションスコープに 1 ユーザーが 1 Object だけを 書き込む場合、100 万ユーザーが同じ操作を行えば 100 万 Object になってしまいます。こうなると下記に示す方法により、Bucket へのアクセス方法に注意しないと、パフォーマンスの低下が発生します。
Bucket と Object の設計
パフォーマンスを保持するためには、Bucket 数を最大化し、Bucket 内の Object 数を最小化するように設計するのが基本です。
このページでは、パフォーマンス向上のためのテクニックをいくつか記載していますが、Bucket 中の Object 数を少なく保つことができていれば、これらを使わなくてもパフォーマンスの問題は起こらないはずです。アプリケーションの設計上、Bucket を分割しても問題なく検索や参照ができる場面では、積極的に Bucket を分割することをおすすめします。
なお、1 つのアプリケーションに Bucket を多数作成しても、パフォーマンスに影響はありません。
アプリケーションによっては、1 つの Bucket に多数の Object が作成されるのが避けられない場合、以下のような回避方法を検討してください。
Bucket を用途に合わせて分割する
Object が 1 つの Bucket に集中するのが避けられない場合、アプリケーションのサービス仕様や利用シーンに沿って、Bucket の分割が可能かどうか検討してください。
オンライン記事の出版アプリケーションを例に挙げます。このアプリケーションの主な用途が最新の記事を数件分だけ表示することなのであれば、最新の記事を参照するための専用 Bucket を導入することで、通常の利用シーンにおけるパフォーマンスを確保できます。過去の記事を含めて全件の KiiObject から特定の条件に合うデータを検索する場合には、最新とそれ以前の両方の Bucket へのアクセスが必要ですが、この操作の実行頻度が低い場合には全体のパフォーマンスが向上します。
Object をネストする
Object が大量に発生する場合、Object のネストを検討するのも 1 つの方法です。Object の内部に配列などで複数のデータを格納することで、Object 数そのものを削減できます。
たとえば、センサー値のように連続的に発生するデータを扱うアプリケーションでは、複数回のセンサーデータを 1 つの Object にまとめることができれば、Object 数を削減できます。これは、通信量や API コール数の節約の面でもメリットがあります。
Object をネストすると検索や編集の処理が複雑になりますが、表示だけが目的のような場合は検討の余地があります。アプリケーションの仕様、発生するデータ件数の見積もり、データのアクセス方法やアクセス回数などからの総合的な判断が必要です。
検索のパフォーマンス
Bucket 内に格納された Object は、Kii Cloud によって自動的にインデックス化されて高速な検索ができるようになります。ただし、検索方法によってはインデックスを有効に使用できず、Bucket 内の Object 数の影響を受ける場合があります。
特に、10 万件以上のオブジェクトを検索する場合は、下記の特性について評価するようにしてください。
単純なクエリーを And でつないだクエリーは、オブジェクト数に対して線形よりもよい傾向を示します。
ID や URL による Object 1 件の検索(CreateByUri など)は、Bucket 内の Object 数の影響をほとんど受けません。
- イコールでの検索(EqualClause)も、通常は Object 数の影響をほとんど受けません。ただし検索対象のカーディナリティが低い場合(性別のように Object 数に対してデータ値の種類が少ない場合)は Object 数の影響を受けます。
Or や Not(NotEqual)を使うと、Object 数に対してパフォーマンスの悪化が著しくなります。可能であれば、他の条件に変更することをおすすめします。
- Or を使いたい場合でも、式の変形によって Or の利用を回避できる場合があります。たとえば、
(key1 = a And key2 = b) Or (key1 = c And key2 = b)
のようなクエリーは、key2
でまとめて(key1 In (a, c) And key2 = b)
のように変形するとパフォーマンスが向上します。 - Or と Equals を組み合わせて使用するよりも、InClause(InWithXxx)を使う方がパフォーマンスが向上します。
- 値の種類が限定されるフィールドでは Not の代わりに InClause(InWithXxx)の利用を検討してください。たとえば、1~5 の整数しか値を取らないフィールドで、
NotEqual 2
を指定する代わりにInWithIntValue 1, 3, 4, 5
を指定するとパフォーマンスが向上します。 - Not(式) を使用したい場合、多くの場合は式の変形によって同等のクエリーを実現できます。下記の Not を含む式の変形 が適用できるかどうか検討してください。
- Or を使いたい場合でも、式の変形によって Or の利用を回避できる場合があります。たとえば、
Object ID はインデックスのプライマリキーの役割があります。頻繁に検索するフィールドが Bucket 内で一意である場合、それを Object ID に割り当てて検索条件に使用すると、パフォーマンスが向上します。
Geo クエリーはデータに応じて挙動が異なるため、単純に傾向を予測するのは困難です。事前の評価をおすすめします。
- 検索対象のオブジェクト数が多い場合は、Geo クエリー以外の方法で件数を絞り込むことをおすすめします。たとえば、ジオハッシュ などの手法を利用できれば、パフォーマンスが向上する場合があります。
以下のようなクエリーはパフォーマンスを低下させる原因になります。
- 深くネストしたクエリー
- 複雑なクエリー
- カーディナリティが低い条件のみでのクエリー(boolean でのフィルタリングなど)
Not を含む式の変形
Not(式) の形を持ったクエリーを使用する場合、ド・モルガンの法則などを使って式の変形ができる場合があります。Not を使わない形に変形することで、パフォーマンスを保持できます。
ド・モルガンの法則
Not( A Or B ) = Not( A ) And Not( B )
Not( A And B ) = Not( A ) Or Not( B )
例えば、数値型のフィールド Age
と、1~5 の数値しか取らないフィールド Answer
を扱うとき、「Age
が 40 以下、または、Answer
が 2 か 3」という条件に一致しない Object を検索したい場合、以下のようにして Not を含まない式に変形できます。
Not( ( Age
<= 40 ) Or In( Answer
, [2, 3] ) )
= Not( Age
<= 40 ) And Not( In( Answer
, [2, 3] ) )
= ( Age
> 40 ) And In( Answer
, [1, 4, 5] )
なお、変形後の式に Or が含まれる場合、ここでもパフォーマンスの低下のおそれがあるため、検索のパフォーマンス に示す方法などでさらに式を変形できないか、検討してください。
ソート使用時のパフォーマンス
検索時にソートを行う場合、Object 検索時の条件にソートで使用するキーを含めると、パフォーマンスが向上します。
たとえば、ゲームの結果をスコア順に取得する場合、スコアが 0 以上など、全件にヒットするダミー条件をクエリーに指定します。こうすると、ソートのための情報を事前に入手できるため、キーを検索条件に含めない場合と比較してパフォーマンスが向上します。
データ転送
クライアントから一度に多数の API コールを行うと、処理時間の増大やサーバー負荷の上昇につながります。
複数の KiiObject にアクセスする際は API の実行回数に注意する必要があります。処理時間や API コール数は、使用する API の特性によって増加の傾向が異なります。
KiiObject の書き込み処理や、ID 指定での読み込み処理では、KiiObject を 1 件ずつ処理します。KiiObject の件数が多いと処理時間や API コール数が増大します。数十件程度の KiiObject の処理であっても、公衆回線を使うと 10 秒以上の時間が必要になる可能性もあります。
クエリーを使用した読み込み処理では、1 回の API コールで最大 200 件の KiiObject を取得できるため、転送するデータ件数の影響は比較的小さくてすみます。
データ転送の最適化の方法は、Kii Balance チュートリアルで説明しています。詳細は データアクセスのパフォーマンス を参照してください。
なお、クエリーを使って非常に多くのデータを読み込む必要がある場合、サーバー負荷の観点からは一度にまとめて全件を読み込むよりも、画面表示の状況に応じて必要な部分を順に読み込む方が、より適切です。
サーバー負荷の分散
大量のデータを 1 件ずつ転送する必要がある場合、サーバー負荷の分散についても考慮してください。特に、プッシュ通知やメディアを通したマーケティング上の告知や、新機能のリリースなど、アクティブユーザーが同時に Kii Cloud にアクセスするような状況で、大量のデータアクセスが発生しないような設計が必要です。
Kii Cloud では、アプリケーション単位で、一定時間内に異常な負荷が集中するとエラーを返す仕組みがあります。この制限値には余裕があるため、通常の運用負荷の変動では問題なく動作する設計ですが、アクティブユーザーの一斉リクエストではエラーになる可能性があります。
データへのアクセス回数が多くなる場合は、以下の対処を検討してください。
上記の Object をネストする に記載した方法により、1 つの KiiObject にできるだけ多くのデータをネストして格納すれば転送回数を抑えられ、負荷が下がります。同時に処理時間も削減されます。
バックグラウンド処理によって少しずつ読み書きを行ったり、乱数(デバイス間で同一の値を返さないもの)で処理の開始時刻を決めたりすると、サーバーの負荷が分散してトラブルを避けることができます。
その他のテクニック
Server Code の利用
クライアントと Kii Cloud の間の通信回数が多い処理は、Server Code 内で処理して結果だけを取得すると、パフォーマンスが向上する場合があります。
デバイスと Kii Cloud 間の通信より、Kii Cloud のサーバー内の通信の方が高速であるためです。トランザクション に示すように、データの一貫性保持の観点からも効果があります。ただし、API コール数は内部の呼び出し回数分も消費します。
Object の削除方法
Object は 1 件ずつしか削除できないため、Bucket 単位で削除できるような設計になっていると、削除のパフォーマンスが向上します。通信量、処理時間、API コール数の増加なども抑制できます。
管理者権限での実行
Bucket 内の検索や Object 数のカウント処理を管理者権限で行うと、結果を高速に取得できます。
これらの処理では、ログイン中のユーザーが実際にアクセスできる Object を ACL によって確認します。管理者権限を使うと、単純に結果だけを取得できるため、パフォーマンスがよくなります。ACL によってアクセスできない Object がある場合は、管理者でカウントすると異なる結果になるため、ご注意ください。
特に、Object 数のカウント処理では、結果の取得量に対して ACL 確認処理の比率が大きくなるため、パフォーマンスの改善効果が大きくなります。
実装する際は、Server Code を 管理者権限 で実行する方法を使ってクエリーを実行する処理を用意し、クライアントから 手動実行 で呼び出す方法が簡単です。
特に、1 つの Bucket の中に 10 万件以上のデータが格納されるような場合は、この方法を使用することをおすすめします。