のんびり精進

調べた情報などをおすそ分けできれば。

Dart/FlutterのローカルDBの比較

モバイル以外にも対応したローカル DB を使いたかったので、複数のパッケージを調べてみました。
Hive を特に詳しく調べたため、そこだけ情報量が多めです。

sqflite

  • Android / iOS のみ 対応
  • macOS にも対応(2020/3/3 追記)
    • 2019/12 の v1.2.0 で対応済
  • Web 未対応
    • sql.js / dart:ffi / IndexedDB あたりを候補に検討中
    • 状況の情報は ここここここ
    • Web では複数タブによる同時アクセスがあり得るという難しさ
      • モバイルでも非同期に同時アクセスすることがあるので同じなのでは…
  • Windows待ち
  • macOS進んできている
  • 今後対応プラットフォームは広がるはず
    • サードパーティだが、主要なパッケージなので対応していくと思われる
    • 急ぎでなければ待てば良い

pub.dev

作者さんが慎重に検討している様子が伺えます。
慎重さを欠いて急いで対応してしまうより安心感があります。

idb_sqflite

  • おそらく sqflite の Web 対応版(IndexedDB)
    • 同じ作者
    • なぜか「WEB」の表記がないが、対応しているはず
      • 使われている idb_shim のほうは「WEB」のほか Dart の「NATIVE」「JS」も付いている
    • sqflite に Web 対応を加えたもの
      • しかし、ちょっと拡張しただけではなさそう
        • 追加コードが多く、ユーザが少ないうちは安定性が不安
  • デスクトップにも対応(?)
    • 「Supports Dart VM (Desktop) through idb_shim」と書かれている

pub.dev

shared_preferences

pub.dev

Flutter チームによるパッケージなので、Flutter 本体が正式サポートするプラットフォームを増やせばこのパッケージも対応範囲を広げていくことが予想されます。

Realm

  • NoSQL
    • SQLite などを用いない独自のデータベース
  • パッケージが二つ存在する
    • realm
      • Web にも対応
      • デスクトップはおそらく未対応
      • 公式ではなく個人による開発と思われる
      • パッケージのスコアが低くて採用対象にならない
    • flutter_realm
      • Web にも対応
      • デスクトップはおそらく未対応
      • 公式ではなく個人による開発と思われる
      • まだ v0.2.2
        • 活発に開発されている様子がない(最後が半年前とか)
      • ドキュメントも未整備
        • メソッドの説明などが不十分どころか書かれてすらいない
      • もう結構できているように見えるという感想もネット上にはあった
        • おそらく使った感想ではない
  • リレーション、トランザクション
    • 公式のパッケージはないので Dart / Flutter のことではないが、他の言語等の版では対応している
  • Flutter を含む新たなプラットフォームや言語への対応は 2020 年 8 月以降
    • ロードマップ
    • 8 月以降に開発開始だとすれば、かなり先になりそう

pub.dev

pub.dev

ネイティブアプリ開発では人気高いようです。
公式のパッケージが出たら使ってみたいです。
すぐにそのときが来るとは思えないので、今のところは別のものを使っておくのが良いでしょう。

Moor

  • ORM
    • SQLite をラップして抽象化したもの
    • sqflite を内部で使っている
      • それなのになぜか sqflite より先に Web 対応完了している
  • Android の Moor に似たもので、名前を逆転させたものらしい
  • パッケージが複数あり、組み合わせて使うものと思われる
  • おそらく Web / デスクトップ / Dart にも対応
    • moor というパッケージではそうなっている
    • moor_flutter のほうは「ANDROID」「IOS」のみになっている
  • ドキュメントがしっかりしている
  • 継続的に開発されている
  • データを扱うのに必要なコードを生成しておかなければならない
  • ちょっとややこしそう
    • パッケージが複数あるだけでも既にややこしくて敬遠したくなる
    • メソッド名が select() 等なので クエリビルダっぽい雰囲気
      • せっかく SQL を使わなくて済むよう抽象化しているのになぜ SQL に寄せるのか…
  • トランザクションに対応
  • リレーションにも対応

pub.dev

pub.dev

pub.dev

高機能なようですが、次の Hive と比べると複雑に見えます。

Hive 1.x

注意:
v2.0 で作り直しが検討されていて v1.x の Box と非互換になる見込みです。
後述の「Hive 2.0」をお読みください。

  • NoSQL(KVS)
  • モバイル / Web / デスクトップ / Dart と幅広く対応
    • ネイティブへの依存がない
  • 独自形式
    • 指定した場所に作られる .hive ファイルに保存
    • Web は IndexedDB
    • SQLite は使っていないはず
      • なぜか Issue に書かれているエラーメッセージに「Error calling sqlite3_step」という情報があるが、ソースコード内にはなかった
  • ドキュメントがしっかりしている
  • 簡単、わかりやすい
    • shared_preferences に似た KVS だが、もう少し高機能
      • 何でも保存可能
        • ただし、List / Map / DateTime / Uint8List 以外はアダプタが必要
  • 継続的に開発されている
  • SQLite や Shared Preferences より 高速 (らしい)
    • ベンチマークはバイアスをかけることもできるので単純には信用できない
    • 通常の Box なら値をメモリにキャッシュ済みなので速いのはわかる
      • shared_preferences も読むのは同じ理由で速い
    • 書き込みも速いのはなぜ…?
  • 画像等のバイナリファイルも保存可能
  • リレーションに対応
    • RDBMS のリレーションのイメージで見るとちょっと違うかも
  • トランザクション非対応
    • 対応予定はあるが、ListViewMapView 等の機能より優先度は下
    • putAll(), addAll(), deleteAll() でまとめて処理することで足りる場合もある
    • 保存後に関連データの削除も一緒にしたいような場合には不十分
  • データを扱うのに必要なコードを生成しておかなければならない
    • shared_preferences では生成した上で json_encode() も必要だが、こちらは生成のみで OK
  • SQLite のほうが適している場合がある
  • フィルタリングは where()
    • Box から取り出した valuesIterable なので次のように絞り込める
      • userBox.values.where((user) => user.name.startsWith('s'));
  • キーの順にソートされる
  • Box と LazyBox
    • Box
      • オープン時にデータがメモリにキャッシュされる
      • なので await なしでデータにアクセスできる
    • LazyBox
      • キーのみがキャッシュされ、値はアクセスしたとき
      • 大きな Box ではこちらのほうがメモリにやさしい
  • 暗号化
    • 通常は暗号化されない
      • 例えば Web は IndexedDB なので開発者ツールで確認できるが、キーも値もそのまま見える
    • generateSecureKey() で生成した暗号化のキーを openBox()encryptionKey という引数で渡す
      • データのキーは暗号化されない
      • 暗号化のキーは flutter_secure_storage 等で安全に保管する必要がある
      • 機微なデータはそもそもデバイス上に保存すべきではない(と思う)
  • Compaction
    • Hive は追加のみのデータストアなので .hive ファイルは大きくなっていく
      • 削除したときもその情報が「追加」される
    • 不要なデータを処分して無駄をなくすことができる
      • デフォルトでは、削除回数が60回を超えてエントリに対する削除率が15%を超えたタイミング
      • Box オープン時に条件を渡して変更可能
      • 自分で box.compact() を実行して行うことも可能

pub.dev

Hive 2.0(計画中)

2020/3/3 追記

  • Dart FFI と Rust の C interop を使って書き直される
    • 速度向上とメモリ使用量の抑制が期待できる
    • 作者がプロトタイプで試したところ好感触
  • データを楽にフィルタリングできるクエリ機能が追加される
  • ベースとなる技術が変わるので v1.x と非互換になる(API だけでなく Box も)
    • v1.x のブランチも継続されてバグ修正等が提供される予定
    • Hive はまだ若い段階なので、大きく変えるなら今だという決断
    • その後に変更があれば自動マイグレーションできるようにしていく

github.com

互換性がないのは残念で、v1.x を使っているユーザにとっては不安なところです。
しかし作者さんはしっかりとした考えを持っていて、理解・賛同できるものでした。

Hive は v1.x でも十分に速いですが、更に高速化して便利な機能も追加されるなら大変期待できます。
Rust を使うというのもなんだかワクワクしますね。
応援したいと思います。

ObjectBox

@_mono さんに情報をいただいて調べたので追記します。

  • NoSQL
  • Web は非対応(?)
    • GitHub の Issues や Projects に情報無し
    • example フォルダ内にも無し
  • Dart 対応
    • 「JS」の表記はない
  • デスクトップには対応(?)
    • パッケージのページには書かれていないがサンプルがある
  • パッケージのスコアはそこそこ
    • この記事を書いている時点で 85
      • Popularity が 70 と低めで、Health と Maintenance は 100
  • dart:ffi が使われている
  • リレーションに対応
    • Hive では HiveList という型にする必要があるが、そういう準備なく使えるかもしれない
      • Flutter 以外のモバイル向けの断片コード(公式ページ内 にあるもの)しか見ていないので推測
  • トランザクションにも対応
    • 「ACID properties and Multiversion Concurrency Control provide you with safe transactions and parallelism.」と書かれている
  • しっかりと開発が進められている
    • Projects を見るとその様子が伺える
  • 開発途上
    • パッケージのページに「still in an early stage with limited feature set (compared to other languages)」と書かれている
    • 公式ページ にまだ Dart が挙げられていなくて、Projects でも「Support array types」(ListUint8List など)のような ToDo がある
    • 機能がまだ少ないだけで、できている部分は十分に使えるかもしれない
  • 簡単そう
    • パッケージのページなどにあるコードを見た印象では簡単
    • 他の NoSQL に似た使い方で取っつきやすそう

pub.dev

Flutter のみのものではないので、以前からの洗練されてきた仕様が期待できます。
また、もともと慣れている人には嬉しいですね。
Web にいつ対応するのか気になるところです。

sembast

@mirock0606 さんに情報をいただいて調べたので追記します。

  • NoSQL(KVS)
    • IndexedDB、Datastore、Web SQL、NeDB 等にインスパイアされたもの
  • WEB や Dart(Native / JS)に対応
  • デスクトップはおそらく対応
  • パッケージのスコアは高い
  • sqflite、idb_sqflite と同じ作者
    • idb_sqflite で使われている idb_shim に依存している
  • 指定した .db ファイルに保存、 オープン時にメモリにキャッシュ、自動 Compaction
    • Hive に似た仕組み
  • ファイル内の形式は JSON
  • 保存できるデータの型
    • キー: int / String / double
    • 値: String / intdouble / Map / List / bool / null
  • トランザクション対応
  • リレーションは不明
  • フィルタリング
    • これは魅力的
      • 例: Finder(filter: Filter.greaterThan('name', 'cat'), sortOrders: [SortOrder('name')])
  • 暗号化対応
  • DB のインスタンスput()get() のたびに渡す
    • await store.record('title').put(db, 'hoge');
    • sembast のストアを用意してそれを使って実行するにも関わらず sembast の db をわざわざ渡す必要があるのは少し残念

pub.dev

作者が同じせいか、sqflite にトランザクションやフィルタリングの機能を加えて便利にした印象です。
特にフィルタリングに魅力を感じましたし、対応プラットフォームが広めなのも魅力です。
保存できる型が限られているのが残念です。
JSON シリアライズして保存すると JSON の文字列でフィルタリングすることになるのかなと思います。