のんびり精進

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

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

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

2022年12月補足(結論)

記事を書いたときは Isar に注目していましたが、現在は Drift に落ち着きました。
ややこしそうな印象を受けて試さずにいて、しばらく後に再び気になったときに触って良さに気づいたので、もっと早く使えば良かったと思いました。 イチオシです。

  • 高い機能性、型安全
  • あらゆる場所で使われている SQLite なので非常に堅牢(参考記事
    • この一点だけを見ても Isar のような個人開発のデータベースは比較にならない
  • SQLite の最新バージョンを利用できる
  • 既に持っている RDB / SQL の知識を活用できる
  • RDB なので高度な操作も自在にできる
  • Flutter Favorite に選ばれている
  • Flutter のドキュメントの一部である Happy paths recommendations で Hive と並んで紹介されている
  • Stream 社がスポンサーになっていて継続性などに不安がない

などが理由です。
クエリビルダ、データのクラスなどがやや難解ですが、理解すると「最高!」と思えるようになります。
SQL に抵抗感のない方には強い力となるでしょう。

※単純なデータなら shared_preferences か Hive で事足ります。用途に合わせて選びましょう。 ※Isar も 4.0.0 以降で SQLite がサポートされるようです。

sqflite

  • Android / iOS のみ 対応
  • macOS にも対応(← 2020/3/3 追記)
    • 2019/12 の v1.2.0 で対応済
  • WindowsLinux でもちょっとした記述を足すだけで使用可能(← 2020/7/31 追記)
  • Web 未対応
  • 今後対応プラットフォームは広がるはず(2021/4/5 現在、もう Web 以外対応済み)
    • サードパーティだが、主要なパッケージなので対応していくと思われる
    • 急ぎでなければ待てば良い

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

  • NoSQL
  • Web 対応済み
  • macOS も対応していると思われる(2020/3/3 追記)
    • 2019/12 の v0.5.5 で対応済
  • デスクトップは不明(2021/4/5 現在、全プラットフォーム対応済みと思われる)
    • GitHub の Issues が無効化され、Projects もほぼ使われていないので、状況がわかりにくい
  • シンプルな KVS でしかない
  • 読み書きのメソッドが用意されていない型では JSON シリアライズが必要
  • トランザクションの機能がない

pub.dev

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

Realm

  • NoSQL
    • SQLite などを用いない独自のデータベース
  • MongoDB とのリアルタイム同期(2021/1/9 追記: 大きな特徴なのに忘れていた…)
    • 公式 SDK が出たら Firestore の代替になりそう
  • パッケージが二つ存在する
    • realm
      • Web にも対応
      • デスクトップはおそらく未対応
      • 公式ではなく個人による開発と思われる
      • パッケージのスコアが低くて採用対象にならない
      • 2021/4/1 に developer preview が公開 ⇒ 後述
    • flutter_realm
      • Web にも対応
      • デスクトップはおそらく未対応
      • 公式ではなく個人による開発と思われる
      • まだ v0.2.2
        • 活発に開発されている様子がない(最後が半年前とか)
      • ドキュメントも未整備
        • メソッドの説明などが不十分どころか書かれてすらいない
      • もう結構できているように見えるという感想もネット上にはあった
        • おそらく使った感想ではない
  • リレーションシップ、トランザクション
    • 公式のパッケージはないので Dart / Flutter のことではないが、他の言語等の版では対応している
  • Flutter を含む新たなプラットフォームや言語への対応は 2020 年 8 月以降
    • ロードマップ
    • 8 月以降に開発開始だとすれば、かなり先になりそう
    • 進展があり(2021/1/9 追記)
      • "we have approved a Dart/Flutter SDK preview."
        • プレビュー版の開発を始めることが approve されたのか、もうプレビュー版が出来上がって approve されたのか、どちらなのか不明

pub.dev

pub.dev

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

2021/4/5 追記

package:realm のほうは以前は個人名で公開されていたと思いますが、4/1 に同じパッケージ名で Realm 公式として developer preview になりました。
まだ実用段階ではありませんが、今後はこの公式のほうを追うのが良いです。

developer preview では、Flutter 同梱の Dart SDK 2.12 は使えない、リモートとの同期機能が未実装、といった制限が多数あるので、試すならドキュメントをしっかり読みましょう。

Drift

Moor から Drift にリネームされました。

  • ORM
    • SQLite をラップして抽象化したもの
    • sqflite を内部で使っている
      • それなのになぜか sqflite より先に Web 対応完了している
      • 訂正
        • drift_sqflite で sqflite を使うこともできる
  • Android の Room に似ているので、その名前を反転したらしい
  • Web / デスクトップ / Dart にも対応
    • 2.9.0 で Web サポートが stable になった
  • ドキュメントがしっかりしている
  • 継続的に開発されている
  • Android / iOS で OS に組み込まれている古い SQLite の代わりに最新のバージョンを利用できる
    • pubspec.yaml で sqlite3_flutter_libs の使用を宣言するだけ
  • データを扱うのに必要なコードを生成しておかなければならない
    • 自動生成による大きな利点があるのだと思うが、個人的にコード生成系は好まないので必須なら辛い
    • ⇒ 使ってみると便利だった
  • クエリビルダが難しいが、使わずに取得等のメソッドを SQL から生成 することもできる
    • これすごい!!
  • 必要になるパッケージはやや多い(drift, drift_dev, build_runner など)
  • SQLite なのでトランザクションなどにはもちろん対応
  • マイグレーションとそのテストを補助する機能が追加された

moor | Dart Package

moor_flutter | Flutter Package

moor_ffi | Dart Package

pub.dev

Hive

注意:
作り直しが検討されていて従来の Box と非互換になります(Isar という別パッケージ)。
元のパッケージはメンテナの人が管理していくことになったため、安心して継続利用できます。
後述の「Isar」をお読みください。

Isar ベースに変えることが 検討 されています。

  • 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

Isar

Hive v2.0 として提案されましたが、Isar という別パッケージとして進められることになりました。

2020/3/3 追記

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

github.com

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

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

注意:
少し 制限 があります。

2020/6/16 追記

v2 を新たなパッケージにして v1 のメンテに誰かが協力する案が出てきました。
少し不安が減りますね。

2020/7/31 追記

Isar という名前で進められていることが3週間ほど前に明らかになりました。
完全に別のパッケージとなります。
Hive のほうのメンテナンスは既に権限が協力者に与えられました。

2021/4/5 追記

1 月にアルファ版がリリースされました。
2/15 には「数週間内に beta 版」と 作者が言っていて 3 月に v0.1.0 が出たので、それが beta 版だと思います。
3 月下旬に追加された Inspector(中身を確認できるツール)が便利そうで、大変期待できます。
1 ~ 2 ヶ月後に stable になるそう です。

pub.dev

ObjectBox

  • 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.」と書かれている
  • 簡単そう
    • パッケージのページなどにあるコードを見た印象では簡単
    • 他の NoSQL に似た使い方で取っつきやすそう

pub.dev

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

2021/5/20 追記

二日前に最初の stable 版である 1.0 がリリースされました!

objectbox.io

記事を書いたときに気づいていませんでしたが、デバイス間やクラウド/オンプレサーバとの間でデータを 同期する機能(おそらく有料)もあるようで、なかなか良さそうです。
また、Dart/Flutter 専用ではなくて複数の言語で使えることや、パフォーマンスが良いことも魅力です。
今後のローカル DB 選定では有力候補の一つになりそうです。

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 の文字列でフィルタリングすることになるのかなと思います。