SQLiteは、個別のサーバープロセスを必要とせず、デバイス上の単一ファイルにデータベース全体を保存する組み込みリレーショナルデータベースです。ゼロコンフィギュレーション、小さなライブラリサイズ、完全なSQLサポートにより、SQLiteはモバイルアプリケーションにおけるローカルデータストレージの標準となっています。SQLiteコンソーシアム(2025年)によると、このDBMSはiOSおよびAndroidのすべてのスマートフォンを含む40億以上のデバイスで使用されています。
重要なポイント
SQLiteは、専用サーバーを必要としないリレーショナルDBMSを実装するC言語ライブラリです。アプリケーションに直接組み込まれ、デバイスのファイルシステム上の通常のファイルに対してデータの読み書きを行います。ライブラリサイズは約600KBで、SQLiteは最も軽量なフル機能SQLデータベースです。
SQLiteはSQL:1999標準の大部分をサポートしており、JOIN、サブクエリ、トリガー、ビュー、インデックス、ウィンドウ関数を含みます。制限はALTER TABLE(限定的なサポート)と完全なRIGHT/FULL OUTER JOINに関するものです。それでも、モバイルアプリケーションの場合、SQLiteの機能はローカルストレージの99%のケースで十分です。
Stack Overflowの開発者調査(2025年)によると、SQLiteは組み込みソリューションで最も人気のあるデータベースであり、MySQL、PostgreSQLに次いで全DBMSの中で人気度第3位です。モバイル開発では、SQLiteはすべてのアプリケーションで直接またはラッパーを介して使用されています。
ゼロコンフィギュレーション — SQLiteはインストール、権限設定、ユーザー作成、サービス起動を必要としません。ライブラリをプロジェクトにリンクし、1つの関数を呼び出すだけでデータベースが作成されます。これは、サーバーのインストール、ポート設定、ユーザー設定が必要なクライアント・サーバー型DBMSと比較して、展開を根本的に簡素化します。
SQLiteデータベースファイルは、コピー、分析、ネットワーク経由での送信、バックアップからの復元が可能な一般的なクロスプラットフォームファイルです。ファイル形式はAPIレベルで安定しており、2004年に作成されたSQLite 3ファイルは現在のバージョンのライブラリで開くことができ、長期的なデータ互換性を保証します。
SQLiteのアーキテクチャは、Tokenizer、Parser、Code Generator、VM、B-Tree、Pager、OS Interface、Utilitiesの8つの仮想マシンで構成されています。SQLクエリはTokenizer(トークンへの分割)、Parser(ASTの構築)、Code Generator(バイトコードへの変換)を経て、B-TreeとPagerを介してデータページを読み取る仮想マシン上で実行されます。
SQLiteはテーブルとインデックスの保存にB-Treeを使用します。各テーブルは個別のB-Treeとして保存され、リーフノードにデータ行が含まれます。インデックスもB-Treeとして保存されますが、リーフにキーが含まれます。Pagerはファイルからメモリへのページ(デフォルト4096バイト)の読み込みを管理し、ジャーナルまたはWALを介してACIDトランザクションを提供します。
WAL(Write-Ahead Logging)はモバイルアプリケーションに推奨されるモードです。変更は最初に別のWALファイルに書き込まれ、その後定期的にメインデータベースに転送されます。WALを使用すると、データベースからの読み取り(古いデータ)と書き込み(WAL経由)を同時に行うことができ、マルチスレッドアプリケーションのパフォーマンスが向上します。標準のジャーナル(ロールバックジャーナル)は書き込み中に読み取りをブロックします。
| パラメータ | ロールバックジャーナル | WAL(Write-Ahead Logging) |
|---|---|---|
| 書き込み中の読み取り | ブロックされる | 許可される(古いデータを読む) |
| 書き込みパフォーマンス | 中程度 | 高い(WALへの順次書き込み) |
| ディスク消費 | 少ない(ロールバックジャーナルのみ) | 多い(WAL + メインデータベース) |
| クラッシュリカバリ | 最後のチェックポイントにロールバック | WALからリカバリ(データ損失なし) |
| 推奨 | シングルスレッドシナリオ向け | 一般的なモバイルアプリケーション向け |
モードの切り替えは1つのSQLクエリで行われます:PRAGMA journal_mode=WAL。バックグラウンド同期とデータを同時に読み取るUIスレッドを持つモバイルアプリケーションの場合、WALはより良いパフォーマンスとインターフェースのロック防止を提供します。
SQLiteはローカルデータストレージの唯一の選択肢ではありませんが、最も汎用性が高いものです。Realmはメモリ内のオブジェクトへの直接アクセスの高速性を提供しますが、独自のNoSQL形式を使用し、ライブラリサイズが大きくなります。iOSのCore DataはSQLite上のORMレイヤーであり、オブジェクトグラフ管理と操作の取り消しを追加します。
ほとんどのアプリケーションでは、予測可能なパフォーマンス、ベンダーロックインのなさ、実績のある安定性により、SQLiteが最適な選択肢であり続けています。RealmとCore Dataは、複雑なオブジェクトグラフ、リアクティブクエリ、またはデバイス間同期が必要なプロジェクトで正当化されます。
| 特性 | SQLite | Realm | Core Data |
|---|---|---|---|
| データベースタイプ | リレーショナル(SQL) | NoSQL(オブジェクトベース) | ORM(SQLite上) |
| ライブラリサイズ | ~600KB | ~4MB | Apple SDKに組み込み |
| パフォーマンス | 中程度 | 高い(インメモリオブジェクト) | 中程度(ORMオーバーヘッド) |
| プラットフォーム | iOS, Android, Web, デスクトップ | iOS, Android, Node.js | iOS, macOS |
| ベンダーロックイン | なし(オープンスタンダード) | 中程度(独自形式) | 高い(Appleのみ) |
SQLite、Realm、Core Dataの選択は、プラットフォーム、オブジェクトモデルの要件、同期戦略によって異なります。クロスプラットフォームプロジェクト(KMP、Flutter)の場合、SQLiteはデータモデルを変更することなくすべてのターゲットプラットフォームで動作する唯一の汎用的な選択肢です。
RoomはAndroid Jetpackのライブラリで、SQLite上にORMレイヤーを提供します。Roomはアノテーション付きDAOインターフェースから自動的にSQLクエリを生成し、コンパイル時にクエリの正確性を検証し、スキーマ変更時にデータベースマイグレーションをサポートします。RoomはAndroidでSQLiteを扱う推奨方法です。
SQLiteOpenHelperは、ORMなしでSQLiteを直接管理するための低レベルAPIです。このクラスはデータベースの作成、開封、アップグレードを管理します。SQLiteOpenHelperは、シンプルなSQLクエリのプロジェクトや、Roomの抽象化なしにSQLロジックを完全に制御する必要がある場合に適しています。
Roomではエンティティに@Entity、DAOに@Daoのアノテーションを付けます。Roomはアノテーション付きメソッドをSQLクエリに変換します。@InsertはINSERTを生成し、@Queryは指定されたSQLでSELECTを生成します。マイグレーションは、古いスキーマバージョンと新しいスキーマバージョンを指定してMigrationクラスを介して追加されます。Roomはコンパイル時にSQLを検証するため、本番環境での構文エラーを排除します。
@Entity
data class User(
@PrimaryKey val id: Long,
val name: String,
@ColumnInfo(name = "created_at")
val createdAt: Long
)
@Dao
interface UserDao {
@Query("SELECT * FROM user ORDER BY name ASC")
suspend fun getAllUsers(): List<User>
@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insertUser(user: User)
@Query("DELETE FROM user WHERE id = :id")
suspend fun deleteUser(id: Long)
}
Roomは自動的にUserDao_Impl実装を生成し、内部のRoomDatabaseを介してランタイムSQLiteクエリを含みます。コルーチン(suspend)により、DAOメソッドはUIをブロックせずにバックグラウンドスレッドで非同期に実行されます。@QueryのFlow戻り値タイプは、テーブルが変更されると自動的に結果を更新します。
FMDBはSQLite C API上のObjective-Cラッパーで、歴史的にiOSで最初に普及したライブラリです。クエリの実行と結果の取得のためにFMDatabaseオブジェクトとFMResultSetオブジェクトを提供します。FMDBはシンプルでミニマルですが、Swift固有の構文(オプショナル、Codable、async/await)をサポートしていません。
GRDBはSQLiteを扱うためのモダンなSwiftライブラリです。タイプセーフなAPI、Codableサポート、Combine Publishers、async/await、マイグレーション、リアルタイム変更監視を提供します。GRDBはSwift Concurrencyとの完全な統合と優れたコード可読性により、新しいSwiftプロジェクトで推奨されます。
GRDBは、FetchableRecordプロトコルとTableRecordプロトコルに準拠するRecordクラスを通じてテーブルを定義します。クエリは生のSQLではなく、タイプセーフな構文でSwiftで記述されます。GRDBはスキーマのバージョン管理とアプリバージョン間のマイグレーションのためのDatabaseMigratorもサポートしています。
struct User: Codable, FetchableRecord, TableRecord {
var id: Int64
var name: String
var createdAt: Date
}
let dbPool = try DatabasePool(path: dbPath)
var migrator = DatabaseMigrator()
migrator.registerMigration("v1") { db in
try db.create(table: "user") { t in
t.autoIncrementedPrimaryKey("id")
t.column("name", .text).notNull()
t.column("createdAt", .datetime).notNull()
}
}
let users = try await dbPool.read { db in
try User.order(Column("name")).fetchAll(db)
}
DatabasePoolは同時読み取りのためにSQLiteのWALモードを使用します。複数のリーダーが同時にデータベースにアクセスできる一方、単一のライターがWALを介してデータを更新します。GRDBは接続とトランザクションを自動的に管理し、手動同期なしで任意のスレッドからスレッドセーフなデータベースアクセスを提供します。
インデックスはSQLiteクエリを高速化する最も効果的な方法です。WHERE、JOIN、ORDER BYで使用されるカラムにインデックスが作成されます。100000レコードのテーブルの場合、インデックス付きカラムの検索は秒ではなくミリ秒で完了します。ただし、インデックスはINSERTとUPDATEを遅くするため、その数は書き込み頻度とバランスを取る必要があります。
単一トランザクション内でのバッチ挿入は、大量データの読み込みを根本的に高速化します。1000レコードを1つずつ挿入すると約1秒のオーバーヘッドが発生します。同じ1000レコードを1つのトランザクションで挿入すると約5〜10ミリ秒です。この違いは、個々のINSERTが同期ディスク書き込みで新しいトランザクションを作成することに起因します。
PRAGMAはライブラリの動作を設定するSQLiteコマンドです。主要な最適化プラグマ:PRAGMA synchronous=NORMAL(fsync頻度を低減)、PRAGMA cache_size=-8000(8MBのキャッシュを割り当て)、PRAGMA temp_store=MEMORY(メモリ内の一時テーブル)。大容量データを扱うモバイルアプリケーションの場合、これらのプラグマの組み合わせによりクエリが2〜3倍高速化します。
もう1つの重要な最適化は、SQLクエリのプリコンパイル(プリペアドステートメント)です。クエリが繰り返し実行される場合(例:10000行の挿入)、SQLを1回コンパイルしてステートメントを再利用すると、CPU負荷が30〜50%低減します。RoomとGRDBは自動的にプリペアドステートメントをキャッシュしますが、SQLite C APIを直接使用する場合は手動でコンパイルする必要があります。
class UserRepository(private val db: RoomDatabase) {
suspend fun insertBatch(users: List<User>) {
db.withTransaction {
users.chunked(500).forEach { batch ->
batch.forEach { user ->
insertUser(user)
}
}
}
}
}
withTransactionを使用したバッチ挿入は、すべてのINSERT操作が単一のトランザクション内で実行されることを保証します。チャンク化(chunked)によるサブバッチへの分割は、1つのトランザクションが大きくなりすぎて他のスレッドを長時間ブロックするのを防ぎます。バックグラウンド同期の場合、500レコードのサブバッチサイズが速度とUI応答性の最適なバランスを提供します。
よくある質問
はい、SQLiteはWALモードでマルチスレッドアクセスをサポートしています。複数のスレッドが同時にデータを読み取ることができますが、書き込みは1つのみです。RoomとGRDBは自動的に同期を管理します。ロールバックジャーナルモード(デフォルト)では、書き込み操作中はデータベース全体がロックされます。
SQLiteの制限は281TB(理論上の最大値)です。実際には、データベースサイズはデバイスの利用可能メモリによって制限されます。モバイルアプリケーションの場合、快適なサイズは1〜2GBまでです。2GBを超えるデータベースは、バックアップ、App Storeのアップデートを遅くし、RAM消費を増加させます。
SQLiteはデフォルトでデータを暗号化しません。ファイルにアクセスできるプロセスは誰でもデータを読むことができます。暗号化には、SQLCipher(AES-256拡張)、EncryptedDatabase(Android)を使用したRoom、またはiOSのEncrypted Core Dataを使用してください。暗号化により、データの読み取りおよび書き込み操作に5〜15%のオーバーヘッドが追加されます。
SQLiteはサーバープロセスを必要としない組み込みライブラリです。MySQLは独立したサーバー、ユーザー、アクセス権、ネットワークプロトコルを持つクライアント・サーバー型DBMSです。SQLiteはデータベースを単一ファイルに保存します。MySQLはサーバーが管理する複数のファイルに保存します。SQLiteはよりシンプルで軽量、MySQLはより強力でスケーラブルです。
SQLiteのマイグレーションには、ALTER TABLE(カラム追加)を使用するか、データ移行と古いテーブルの削除を伴う新しいテーブルを作成します。RoomはMigrationクラスを通じてこのプロセスを自動化します。startVersion、endVersion、およびスキーマ変更のSQLクエリを指定します。GRDBとFMDBも同様のDatabaseMigratorを提供しています。
まとめ
ターンキー方式のモバイルアプリケーションを開発します
IT Sectrは2017年からスタートアップや企業向けにiOS・Androidアプリケーションを開発しています。私たちがご相談に乗り、最適なソリューションをご提案します。