読者です 読者をやめる 読者になる 読者になる

アニマネ開発日誌

アニメアプリのアニマネの開発日誌です。

アプリとサーバーの通信にJSONではなくSQLiteを使うと幸せになれるかも知れない条件まとめ

Objective-C アプリ開発

軽い気持ちで投稿したら、思わぬ反響を頂いたこの話。

賛否両論で色々な意見を頂きました。

問題点も含めてある程度メリット・デメリットが見えてきたので、最後にまとめてみます。

ブコメTwitterで色々と意見を頂いた方々ありがとうございました。

この場を借りてお礼申し上げます。

前回までのおさらい

クライアントとサーバー間で何らかのデータの受け渡しをする時に、
よく使われるフォーマットとしてJSONXMLがあります。

構造がシンプルなテキストで汎用性が高いため、あらゆるプラットフォーム間の差異を吸収するフォーマットとしてメジャーな存在です。

モバイルアプリも例外ではないのですが、JSONなどを使わずにSQLiteのDBファイルを直接渡してやりとりするというのが先日書いた記事です。 SQLiteクロスプラットフォームな上に1ファイルで完結するので、1つのファイルで様々なプラットフォームから読み込むことが可能です。

この特性を活かして、サーバー側でSQLiteのDBを生成し、アプリに直接渡すというのが先日記事に書いた内容です。

animane.hatenablog.com

僕自身もコロンブスの卵的な感じはしていたのですが、公開後に予想以上の様々な反応を頂きました。

animane.hatenablog.com

animane.hatenablog.com

今回は色々意見を頂けたことでかなり勉強になったので、最後にまとめておこうというのが本エントリです。

選択肢に入りそうなケース

JSONではなくSQLiteのDBファイルを直接やりとりする方法が選択肢に入りそうなケースを先に書いておきます。

  • サーバー側のリソースを節約したい
  • ユーザーの待ち時間を減らし、アプリを快適に動作させたい

ちょっと辛いケース

  • アプリだけでなくWebも含めて提供する必要がある場合
  • 高いセキュリティレベルが要求されるケース
  • 更新が頻繁に入るようなサービスモデル

前提条件

  • データの更新頻度は高くない
  • サーバーから受け取ったDBには参照系のみで済む
  • JSONで扱うには大きすぎるデータ量
  • SQLiteのDBファイルにした時に、モバイル回線でもやりとりできるデータ量に収められる

アニマネの場合は番組データで使っているのですが、更新は1日3回です。

大体1000件前後のレコード数を想定しています。

更新系が入る場合はDBをわけてATTACH DATABASEで連結すれば特に問題なく利用できます。
なので、iOSの場合はCoreData(SQLite)に保存したデータをJOINして引っ張ってこれます。

SQLiteは1ファイル1DBとなるのですが、上記に書いたとおり複数のDBでも同じように連結ができるので、 更新頻度や内容に応じてDBを複数ファイルに分けて管理するという方法がとれます。

実際アニマネではあまり変更がない作品情報で1DB、1日に3回更新される番組表を別DB(別ファイル)にして、 表示の時はJOINで繋いでいます。

データの正規化ができるので、通信量も減ります。

比較対象

JSONだけ特別にしているわけではないので、JSONと記載していますがXMLなどのデータフォーマット言語全般を指しています。

gzipによる通信の圧縮はどの環境でも使えるので、今回は比較しないことにしています。

あとJSONのクエリ言語についてはほぼ推測だけで書いているので、怪しい比較と思っておいてください。

JSONに関してはパーサーの性能もかなり依存しますが、ここでは一旦考えないものとします。

SQLite

  • 通常JSONで受け取るようなケースの代わりに、SQLiteのDBファイルを直接送付します。
  • DBファイルを受け取ったクライアントはFMDB(Objective-C)などのライブラリからDBにアクセスします。

JSONプレーン

  • よくあるAPIの利用ケースでJSONを読み込み、JSONパーサーによってコレクションに展開してプログラム上から利用することを前提としています。

JSON+クエリ言語

この記事を書くまで知らなかったのですが、LINQとかjq、Jmespathとかなるものがあります。

  • XMLでいうXPATH的な感じと理解しています。
  • MongoDBみたいなDBだとそもそも前提が変わりますが、JSONを読み込んでクエリ書けばXPATHみたいに色々できるよというような感じを想定しています。

サーバー側処理

JSONプレーン = JSON+クエリ言語 > SQLite


圧倒的にJSONのが楽と思います。

Web向けに提供しているAPIがあれば、JSONの場合はそのまま使えますが、
SQLiteはそうもいきません。

プログラムからSQLiteを扱うのは簡単とはいえ、サーバー側で事前にDBを用意しておく必要がある分、 作成する処理は増えます。

クライアント側処理

JSON+クエリ言語 => JSONプレーン => SQLite


これはJSONの方が楽なケースがほとんどです。

ただし、SQLiteの場合でもサーバー側で頑張って最適化して、 シンプルなデータ構造であればそれ程変わらないケースもあります。

アニマネではアプリ側でロジックはできるだけ書かないように最適化しています。

ファイルサイズ

SQLite > JSONプレーン = JSON+クエリ言語


アニマネのケースでは端末のディスク使用量はバイナリであるSQLiteの方が少なめです。

ちなみにgzip圧縮していれば、通信経路上のサイズはそれほど変わりません。

端末上にデータを保存する時にも圧縮しておけばいいのかも知れませんが、
利用する時に展開しても差し支えないレベルのデータ量であればそもそもSQLiteを使う必要はないので、
今回は考えないことにします。

メモリ効率

SQLite > JSONプレーン => JSON+クエリ言語


コードを書いてチェックしてませんが、これはSQLiteの方が効率良いと推測しています。

インデックスを張っていれば、最短経路でデータを修得できるので。

JSONの場合は処理系にもよりますが、一度メモリ上にコレクショなりに展開する分、
データ量に比例してメモリ使用量が必要になると思います。

JSONの場合はメモリに展開しなければ、実行速度が犠牲になるので、ここはSQLiteに軍配があがりそうです。

最近の端末は2GBとか積んでいる端末もありますが、メモリ効率の良さは大きなアドバンテージになります。

CPU効率

SQLite > JSONプレーン => JSON+クエリ言語


これもSQLiteかと思っています。 インデックスを張っていれば最小の計算で済みますが、インデックスなしではいくらパーサーが頑張ってもデータ量が増えると辛いかと。

ただ、件数によってはJSONの処理系がメモリに展開後に頑張ればどうなるかは分かりません。

システムの結合度

JSONプレーン > JSON+クエリ言語 > SQLite


これは圧倒的にJSONがよいですね。

SQLiteと骨を埋めるぐらいでないと、今回の手法は取れないと思います。

将来的な可用性

JSONプレーン > JSON+クエリ言語 > SQLite


これも圧倒的にJSONがよいですね。

気軽に書き換えられるぐらいのコード規模でないと、採用するのは怖いと思います。

バージョン互換

JSONプレーン > JSON+クエリ言語 > SQLite


これもSQLiteには超えられない壁ですね。

アプリとの親和性

SQLite > JSON+クエリ言語 => JSONプレーン


こちらはケースによると思いますが、SQLiteの方が優位です。

現在のモバイルアプリのDBの主流はSQLiteと思いますので、親和性はとても高いです。

アニマネではユーザーデータをCoreData(SQLite)に保存しつつ、 ユーザーデータと併せて表示する時にはATTACH DATABASEで連結して1回のクエリでデータを修得しています。

ただ、SQLiteの代替となるRealmなどもでてきていますので、今後の未来は明るいとも言い切れません。

クロスプラットフォーム

JSONプレーン > JSON+クエリ言語 > SQLite


クロスプラットフォームという点では、テキストのJSONが一番ですね。

SQLiteクロスプラットフォームで使えるとはいえ、新しい言語には弱いです。

セキュリティ

SQLite > JSONプレーン => JSON+クエリ言語


これはどちらかと言えば、SQLiteのが有利と思います。

SQLiteのDBを暗号化しておけば、気軽にDBの中を除くことはできないと思います。

とはいえ、高いセキュリティという訳ではないので、個人情報などのクリティカルなデータの保存には向きません。

結論

ということで個人的な結論。

密結合を覚悟、将来的なコードの書き換えを覚悟した上で、 それでもアプリの利便性を高めたい、サーバー側のリソースを減らしたいという場合には選択肢にいれてもいいのではないかと。

かなり尖った方法なので、大規模なサービスではなかなか採用できないと思います。

しかし、大規模なサービスの場合はかなりのサーバー側リソースの削減が期待できます。 (茨の道の気はしますが。)

大雑把に説明すると、10万人ぐらいのユーザーがいたとしてレコード数は1000件前後のデータを表示するのに、アプリから毎回APIリクエストが届くとします。

1000レコードと言っても、ユーザーからの検索条件やソートによって、キャッシュヒット率はあまりよくない。

このようなサービスをサーバー数10台で運営していたとして、そのリクエストのほとんどがクライアントで処理できたとしたら、相当なコストダウンになると思います。

更新頻度が少ないデータならキャッシュしていると思いますが、それでもトラフィックを捌くサーバーの台数や、 冗長化も含めるとクライアント側にデータがあったほうが確実にコストが安いと思います。

昨今のマイクロサービスとは逆行している考えでつぶしが効かない形のため、
長期的に見ると技術的な負債になったり、マルチに展開する時にスタートダッシュが掛けられないなど、投機的な損失になる可能性もあります。

あまり事例がないと(ゲーム業界だと割とあるのかも知れませんが)社内調整も大変そうなので、普通は採用し辛いかと。

ただ、感覚的にはアプリ開発の変化は、Web開発と比べて進化の速度が遅いと思うんですね。
結局OSのプラットフォームの上にのっかって作っているだけなので。

どちらかと言えばいかにUI/UXを工夫するかに重きがおかれている気がします。

Androidは未経験なのでiOSだけの話をすると、Swiftが大きな転換になったぐらいでしょうか。
最近だとRealmかな?
mBaaSも入るかも知れないけど、アプリ側でやっていることはプログラムのインタフェースが変わってくらいで、そんなに変化はない気はします。

逆にWebに関しては懐かしのWeb2.0JavaScriptが再発見され、CGMだなんだでリアルタイム性が重視されてCometみたいな手法がでてきて、
クロスブラウザを考慮したjQueryなどのフレームワークが台頭し、HTML5によってよりリッチなWeb体験が可能になり、
クライアントマシンの(JavaScriptエンジンも)性能向上によってReactみたいなこともできるようになり、ここ数年で開発環境は劇的に変化しています。

フロントエンドのUIを担当している人達もSassとかGruntの普及で黒い画面が当たり前の時代になってますしね。

サーバーサイドでみても、RDMSは着実に進化を進めつつ、サーバーのメモリ搭載量の向上によってメモリベースのKVSが登場し、
大規模なデータ処理のためにMapReduceHadoopなどの分散処理に特化したミドルウェアが生まれ、
ドキュメント指向のDBもMongoDBで実用的に使えるようになり、
列指向とドキュメント指向の両方を取り入れたBigtableやCassandraもあり、
並列処理、非同期、イベント駆動を取り入れた新しい言語も登場し、開発の選択肢は非常に多く複雑化しています。

最近だとディープラーニングとか機械学習の分野も熱いですね。

インフラ関連なら仮想化技術をベースにして、AmazonのEC2からクラウド時代が始まり、
PuppetからChef、Ansibleなどのプロビジョニングツールでサーバー管理がしやすくなり、
さらにVagrantやDockerなどで開発環境の立ちあげや共有が劇的に楽になっています。

10年前から考えると魔法か何かのような感覚です。

思いつくまま列挙してみましたが、Web側の進化に比べてアプリ側(iOSしか知りませんが)はXcode(とObjective-C)の進化の歴史で、 アプリ開発自体がハードとOSに密接に関連した密結合なわけで、それならいっそのこともうちょっと密結合に突っ込んでもいいのではないかと思います。
(Swiftは大きなパラダイムシフトと思いますが)

これも未経験で完全なる憶測ですが、Windowsのアプリなんかはびっくりするぐらい後方互換の維持ができていて、 XP時代のソフトウェアがWindows7で動いていたり(最近のWindows事情は疎いです)とコードの寿命は長いと思うのです。

それだけに、しっかりした設計と長く使えるような品質の高いコードが要求されますが、 モバイルアプリはそこまででもないケースが大半と思うのです。

モバイルアプリの場合はネットワークがあることが前提なので、
サーバー側で頑張って、アプリ側は表示をするだけのケースが多く、わりと作り替えが行いやすいコードなわけで、
さらにプラットフォーム側である程度の後方互換もあるという。

FacebookHTML5ベースからネイティブアプリにシフトしたことを見ても、 モバイルアプリが注力するべきはインタフェースであって、細かい計算はサーバー側で頑張るべきじゃないかと。

アニマネのケースをあまり説明していませんでしたが、SQLiteのDBといってもアプリ向けに最適なスキーマに変換しています。 サーバー側ではMySQLに保存しているデータをアプリ側で複雑な処理を行わなくてよいように、 最適化した形でSQLiteのDBに落としています。

なのでSQLiteを使うといってもほぼ単純なSELECT文がほとんどで、コードの多くはUIの操作に費やしています。

乱暴に言うとアプリ側のロジックなんて大したことしてなくて、そのほとんどをUIにつぎ込んでいるんだから、 今さら細かいことはいいんだよみたいな。

それならもうちょっと密結合してもいいんじゃね、と今書きながら感じました。

大規模開発の責任者ではなく、単なる個人開発なので言えることではありますが。。。

なんか勢いで色々書いちゃいましたが、今回軽い気持ちで書いた記事でここまで深く考えるとは思っていなかったので、とても勉強になりました。

今回の話はこれで最後にします。
(何か抜けがありそうな気もするので後でこっそり追記するかもです。)

ここまで読んで頂いた方々、ありがとうございました。

開発日誌はこれからもボチボチと書いていくので、また突拍子もないことを書くかもしれませんが、
生暖かい目で見守って頂けると嬉しいです。

あと、最後に宣伝。

エンジニアなら8割、いや6割ぐらいはアニメ見てませんか!?
アニメ見てるならぜひ使ってみてください!

app.animane.net

(色々書いている割に大したアプリ作ってねぇなと思われる気もしますが。。。今はメジャーアップデートに向けて鋭意製作中です!)

最後に宣伝するというあざとさを見せていますが、今回はよい勉強となり、実のある連休を過ごせました。 ご意見頂いた方々に改めてお礼を申し上げます。