SQLiteルネサンス:世界で最もデプロイされたDBが2026年にプロダクションを席巻する理由
意外に思われるかもしれませんが、世界で最もデプロイされているデータベースエンジンはSQLiteです。 PostgreSQLでもMySQLでもMongoDBでもありません。SQLiteです。すべてのiPhone、Androidデバイス、Mac、Windows 10+マシン、Firefox・Chromeブラウザ、エアバスA350、さらには火星探査ローバーにまで入っています。現在アクティブなSQLiteデータベースは文字通り数兆個あります。
にもかかわらず、開発者コミュニティは長い間SQLiteをおもちゃ扱いしてきました。「SQLiteはプロダクション向きじゃない。」「プロトタイプには十分だけど。」「いずれ本物のデータベースが必要になる。」この認識はあまりにも根深く、ローカル開発以外の用途でSQLiteを提案すれば、コードレビューで笑い物にされるレベルでした。
それが変わりました。 2025-2026年に注目すべきことが起きたのです。Turso、Cloudflare D1、LibSQL、Litestream、そして組み込みデータベースムーブメント——これらの技術が合流して、SQLiteの実際の制約を解決しつつ、その驚異的なシンプルさはそのまま残しました。結果として、SQLiteは以前は考えられなかった種類のアプリケーションで正真正銘のプロダクションデータベースになりました。
ハイプではありません。アーキテクチャの話です。何が起きているのか、なぜ重要なのか、そして2026年にSQLiteを選ぶべき場面と避けるべき場面を詳しく見ていきましょう。
SQLiteが「プロダクション不適格」だった理由
盛り上がる前に、なぜサーバーサイドでSQLiteが敬遠されてきたか率直に振り返りましょう。あの評価は間違っていたわけではなく、別の時代に対する正確な評価だったのです。
従来の制約
1. シングルライター並行性。 SQLiteはファイルレベルのロックを使います。ライターは同時に1つだけ。WAL(Write-Ahead Logging)モードでは書き込み中でも並行読み取りが可能ですが、それが限界です。複数接続から高い書き込みスループットが必要なら、従来のSQLiteでは対応しきれません。
2. 組み込みレプリケーションなし。 PostgreSQLにはストリーミングレプリケーション、MySQLにはbinlogレプリケーションがあります。SQLiteには……ファイルのコピーがあります。高可用性やフェイルオーバー、地理的分散が必要なアプリケーションには不向きでした。
3. 単一マシンストレージ。 データベースはディスク上のファイルです。クラスタリングなし、シャーディングなし、分散ストレージなし。データはそのマシン1台と運命を共にします。
4. ネットワークアクセスなし。 PostgreSQLやMySQLはネットワークサービスとして動作し、どこからでも接続できます。SQLiteは組み込みライブラリなので、アプリケーションが直接通信します。つまり、複数のサービスからの共有アクセスができません。
これらは実際の制約です。消えたわけではありません。しかし周辺環境があまりにも劇的に変わったため、以前ほど重要でなくなった、あるいは直接解決されたのです。
何が変わったか:4つの柱
柱1:ハードウェアが十分に速くなった
最も過小評価されている変化がハードウェアです。最新のNVMe SSDは約50万〜100万ランダムIOPSを100μs未満のレイテンシで実現します。SQLiteがNVMe上で動くと、シングルライターモデルでも毎秒数千回の書き込みを処理できます。大多数のWebアプリには十分すぎるスペックです。
数値を見てみましょう:
従来のHDD:
ランダムIOPS:~100-200
書き込みレイテンシ:~5-10ms
SQLiteスループット:~100-200 writes/sec
NVMe SSD(2026年):
ランダムIOPS:~50万-100万
書き込みレイテンシ:~10-50μs
SQLiteスループット:~1万-5万 writes/sec
ほとんどのWebアプリは毎秒数百回以上の書き込みを必要としません。毎分100件の注文を処理するECサイト?毎秒2回以下です。DAU1万人のSaaSアプリ?毎秒100回を超えることさえ難しいでしょう。NVMe上のSQLiteなら余裕です。
柱2:WALモードとBEGIN CONCURRENT
2010年から存在するSQLiteのWALモードは、書き込み中でも並行読み取りを可能にします。多くの開発者が実際にベンチマークを取らず、「シングルライター」と聞いただけでスルーしていました。
実際のところ、WALモードのスループットは優秀です:
-- WALモード有効化(一度だけ) PRAGMA journal_mode=WAL; -- プロダクション推奨SQLite設定 PRAGMA busy_timeout = 5000; -- ロック待ち最大5秒 PRAGMA synchronous = NORMAL; -- 十分な耐久性、高速 PRAGMA cache_size = -64000; -- 64MBキャッシュ PRAGMA foreign_keys = ON; -- 参照整合性を適用 PRAGMA temp_store = MEMORY; -- テンポラリテーブルをRAMに
LibSQLのようなSQLiteフォークで利用できるBEGIN CONCURRENT拡張はさらに一歩進んで、同じページを変更しない限り複数のライターが同時に進行できます:
BEGIN CONCURRENT; INSERT INTO orders (user_id, total) VALUES (42, 99.99); COMMIT; -- 同じデータページに触れなければ -- 複数の接続が同時に実行可能
柱3:LibSQL——すべてを変えたフォーク
LibSQLはSQLiteのオープンソースフォークで、完全な互換性を維持しつつ、誰もが望んでいた機能を追加しています。Tursoチームが開発したLibSQLは、SQLiteの制約を物理法則ではなくエンジニアリングの課題として扱います。
主な追加機能:
- ネイティブレプリケーション。 プライマリサーバーから同期する埋め込みレプリカをサポート。ローカルの読み取り性能とレプリケーション耐久性を両立できます。
- **
BEGIN CONCURRENT**によるマルチライターサポート。 - サーバーモード。 LibSQLがHTTPとWebSocket経由でネットワークアクセス可能なサーバーとして動作し、「ネットワークアクセス不可」を解決。
- ALTER TABLE改善。 制約付きカラム追加のようなSQLiteで困難な操作がスムーズに。
- WASM UDF。 WebAssemblyで書かれるユーザー定義関数。
- ベクトル検索内蔵。AI/エンベディングのユースケースに対応。
柱4:プラットフォーム層
真の革命はLibSQL単体ではなく、その上に構築されたプラットフォームです。
TursoはLibSQLのマネージドサービスで:
- 世界30+拠点のエッジレプリカ
- アプリに同期する埋め込みレプリカ(サブミリ秒の読み取り)
- テナント単位のデータベース(ユーザー/組織ごとに1DB——マルチテナンシーのハック不要)
- ポイントインタイムリカバリとブランチング
Cloudflare D1はCloudflareのサーバーレスSQLite:
- Cloudflareのエッジネットワーク(300+都市)で実行
- Workersとのネイティブ統合(ネットワークホップゼロ)
- エッジノードへの自動読み取りレプリケーション
- バッチ操作対応SQL API
LiteFS(Fly.io)は歴史的な文脈として触れておく価値があります:
- FUSEベースでFly.ioマシン間のSQLiteレプリケーション
- 自動プライマリ選出とフェイルオーバー
- アプリケーションに対して透過的
- ⚠️ 注意: LiteFS Cloud(マネージドバックアップサービス)は2024年10月に終了し、Fly.ioはアクティブな開発の優先度を下げています。LiteFS自体は安定しておりプロダクションで使用可能ですが、pre-1.0ベータ状態でロードマップの保証はありません。新規プロジェクトにはTursoやD1がより安全な選択です。
Litestream:
- SQLiteをS3/GCS/Azureへ継続的ストリーミングバックアップ
- オブジェクトストレージからのポイントインタイムリカバリ
- 軽量で実戦で鍛えられ、積極的にメンテナンスされている——ホスティングプラットフォームに関係なくセルフマネージドSQLiteバックアップに最適
アーキテクチャ:プロダクションSQLiteの実際
具体的に見ていきましょう。2026年にSQLiteで実際のアプリをどう設計するかをお見せします。
パターン1:Turso+埋め込みレプリカ
Webアプリで最もポピュラーなパターンです。アプリがTursoプライマリから同期するローカルSQLiteレプリカを内蔵します。
import { createClient } from '@libsql/client'; // 埋め込みレプリカ付きクライアントの作成 const db = createClient({ url: 'file:local.db', // ローカルレプリカ syncUrl: 'libsql://my-app-db.turso.io', // リモートプライマリ authToken: process.env.TURSO_AUTH_TOKEN, syncInterval: 60, // 60秒ごとに同期 }); // 読み取りはローカルレプリカから——サブミリ秒 const users = await db.execute('SELECT * FROM users WHERE active = 1'); // 書き込みはリモートプライマリへ、その後同期 await db.execute({ sql: 'INSERT INTO users (email, name) VALUES (?, ?)', args: ['[email protected]', 'Alice'], }); // 必要なときに強制同期 await db.sync();
読み取りパスが卓越しています。アプリはローカルのSQLiteファイルから読み取ります。ネットワークホップなし。コネクションプールなし。コールドスタートなし。全読み取りがサブミリ秒のレイテンシ。 書き込みはネットワーク経由でプライマリに行きますが、ほとんどのアプリはリードヘビーなので問題になりません。
パターン2:Tursoでテナント単位のDB
SQLiteアーキテクチャが最も輝くパターンです。すべてのテーブルにtenant_idカラムを付ける代わりに、各テナントに専用のデータベースを渡します:
import { createClient } from '@libsql/client'; function getClientForTenant(tenantId: string) { return createClient({ url: `libsql://${tenantId}-my-app.turso.io`, authToken: process.env.TURSO_AUTH_TOKEN, }); } // テナントAの操作はテナントAのDBのみに影響 const tenantA = getClientForTenant('tenant-a'); await tenantA.execute('SELECT * FROM invoices'); // テナントBは完全に隔離 const tenantB = getClientForTenant('tenant-b'); await tenantB.execute('SELECT * FROM invoices');
このパターンのメリット:
- 真の隔離。 テナント間のデータ漏洩リスクなし。毎クエリの
WHERE tenant_id = ?不要。 - 独立したスケーリング。 負荷の高いテナントは専用リソースを持てる。
- シンプルなクエリ。 すべてのクエリがデフォルトでテナントスコープ。
- コンプライアンスが簡単。 GDPR削除?データベースファイルを消せば終わり。
- 独立したバックアップ/リストア。 一つのテナントを復元しても他に影響なし。
パターン3:Cloudflare D1+Workers
すでにCloudflareを使っているなら、D1がWorkersからのゼロレイテンシDBアクセスを提供します:
export default { async fetch(request: Request, env: Env): Promise<Response> { // ネットワークホップゼロ——D1はWorkerと同じマシンで動作 const { results } = await env.DB.prepare( 'SELECT * FROM products WHERE category = ?' ).bind('electronics').all(); // 複数操作のバッチ処理 const batchResults = await env.DB.batch([ env.DB.prepare('UPDATE inventory SET count = count - 1 WHERE id = ?').bind(productId), env.DB.prepare('INSERT INTO orders (product_id, user_id) VALUES (?, ?)').bind(productId, userId), ]); return Response.json(results); } };
パターン4:ローカルファースト+同期
最も魅力的なパターン:ローカルファーストアプリケーション。データベースがクライアントデバイスに存在し、サーバーと同期します:
import { createClient } from '@libsql/client/web'; const localDb = createClient({ url: 'file:local-user.db', syncUrl: 'libsql://user-data.turso.io', authToken: userToken, }); // オフラインでも動作——ローカルDBで読み書き await localDb.execute( 'INSERT INTO notes (title, body) VALUES (?, ?)', ['議事録', 'Q3ロードマップの議論...'] ); // オンラインになったらサーバーと同期 await localDb.sync();
ベンチマーク:実際の数値
具体的な数値で見ていきましょう。SQLiteベースのソリューションと従来のアプローチの比較です。
読み取り性能
プライマリキーによる単純SELECT(p50レイテンシ):
SQLite(ローカルファイル): ~0.01ms
SQLite(Turso埋め込みレプリカ): ~0.02ms
Cloudflare D1(Workerから): ~0.5ms
PostgreSQL(同一リージョン): ~1-3ms
PostgreSQL(マネージド、例:Neon): ~3-10ms
PlanetScale MySQL(リージョナル): ~3-8ms
書き込み性能
単一INSERT(p50レイテンシ):
SQLite WALモード(NVMe): ~0.05ms
SQLite(Turso、プライマリ書き込み): ~15-50ms(ネットワーク)
Cloudflare D1(書き込み): ~5-30ms
PostgreSQL(同一リージョン): ~1-5ms
書き込みでは、ネットワークベースのSQLiteソリューションの方がレイテンシが高くなります。書き込みがプライマリに到達する必要があるためです。これがトレードオフ:読み取りは圧倒的に速い代わりに、書き込みはネットワークを経由します。リードヘビーなアプリ(大半のWebアプリ)には、これは優れたトレードオフです。
並行書き込みスループット
持続的writes/sec(単一データベース):
SQLite WAL(ローカル、NVMe): ~1万-5万
SQLite+BEGIN CONCURRENT: ~5千-2万(マルチライター)
Turso(単一プライマリ): ~1千-5千(ネットワーク制約)
Cloudflare D1: ~500-2千
PostgreSQL: ~1万-5万
MySQL: ~1万-5万
書き込みが多いワークロードでは、従来のデータベースがまだ有利です。複数プロセスから毎秒数千の並行書き込みトランザクションが本当に必要なら——リアルタイム入札、高頻度ログパイプラインなど——PostgreSQLやMySQLが依然として正解です。
プロダクションでSQLiteを使うべき場面と避けるべき場面
SQLiteを選ぶべき場面:
1. リードヘビーなアプリ。 大半のWebアプリは読み取りが90%以上です。ブログ、コンテンツサイト、ECカタログ、ダッシュボード、ドキュメントサイト——すべてリードヘビーです。埋め込みレプリカ付きSQLiteが最適です。
2. 複雑さなしに地理的分散が必要なとき。 TursoのエッジレプリカやCloudflare D1で、データが物理的にユーザーの近くに配置されます。レプリケーショントポロジの管理は不要です。
3. テナント隔離が必要なとき。 データ隔離要件が厳格なSaaSアプリが、テナント単位DBパターンから大きな恩恵を受けます。
4. ローカルファーストやオフライン対応アプリを作るとき。 オフラインで動作して後から同期するアプリなら、SQLiteが自然な基盤です。
5. 運用オーバーヘッドを最小限にしたいとき。 管理するDBサーバーなし。チューニングするコネクションプールなし。多くのユースケースでSQLiteはゼロ運用です。
6. サイドプロジェクトや中小規模アプリ。 ここがスイートスポットです。大多数のWebアプリは単一SQLiteデータベースが処理できる範囲を超えることはありません。
SQLiteを避けるべき場面:
1. 複数ソースからの高い持続的書き込みスループットが必要なとき。 異なるプロセスから毎秒数千の並行書き込みトランザクションが本当に必要なら、PostgreSQLかMySQLを使いましょう。
2. 複雑な分散トランザクションが必要なとき。 SQLiteにはPostgreSQLやCockroachDBのような分散トランザクション機能がありません。
3. RLS(Row-Level Security)や高度なアクセス制御が必要なとき。 PostgreSQLのRLSは、単一DB内で細粒度のアクセス制御が必要なマルチテナントで実績があります。SQLiteにはこれがありません。
4. チームがすでにPostgreSQL/MySQLに強く、インフラも整っているとき。 流行りだからSQLiteに移行するのはお勧めしません。既存スタックがうまく動いているなら、移行コストに見合わないでしょう。
スタック構成:全部まとめると
2026年のSQLiteプロダクションスタック:
ミニマルスタック
アプリ:Next.js / Remix / SvelteKit / Astro
DB:Turso(LibSQL)+埋め込みレプリカ
ORM:Drizzle ORM(LibSQL/Tursoファーストクラスサポート)
ホスティング:Vercel / Cloudflare / Fly.io
Drizzle ORMはLibSQL/Turso統合が素晴らしいです:
// drizzle.config.ts import { defineConfig } from 'drizzle-kit'; export default defineConfig({ schema: './src/db/schema.ts', dialect: 'turso', dbCredentials: { url: process.env.TURSO_DATABASE_URL!, authToken: process.env.TURSO_AUTH_TOKEN!, }, });
// src/db/schema.ts import { sqliteTable, text, integer } from 'drizzle-orm/sqlite-core'; export const users = sqliteTable('users', { id: integer('id').primaryKey({ autoIncrement: true }), email: text('email').notNull().unique(), name: text('name').notNull(), createdAt: text('created_at').notNull().default(sql`(datetime('now'))`), }); export const posts = sqliteTable('posts', { id: integer('id').primaryKey({ autoIncrement: true }), title: text('title').notNull(), content: text('content'), authorId: integer('author_id').references(() => users.id).notNull(), published: integer('published', { mode: 'boolean' }).default(false), });
// src/db/index.ts import { drizzle } from 'drizzle-orm/libsql'; import { createClient } from '@libsql/client'; import * as schema from './schema'; const client = createClient({ url: process.env.TURSO_DATABASE_URL!, authToken: process.env.TURSO_AUTH_TOKEN!, }); export const db = drizzle(client, { schema }); const publishedPosts = await db .select() .from(schema.posts) .where(eq(schema.posts.published, true)) .leftJoin(schema.users, eq(schema.posts.authorId, schema.users.id));
プロダクションSQLiteの運用Tips
プロダクションでSQLiteを使うなら、以下のプラクティスがよくある落とし穴から救ってくれます:
1. WALモードは必ず有効に
PRAGMA journal_mode=WAL;
交渉の余地なしです。WALモードがあって初めて、書き込み中の並行読み取りが可能になります。
2. 適切なPRAGMA設定
PRAGMA journal_mode = WAL; PRAGMA busy_timeout = 5000; PRAGMA synchronous = NORMAL; PRAGMA cache_size = -64000; PRAGMA foreign_keys = ON; PRAGMA temp_store = MEMORY; PRAGMA mmap_size = 268435456; -- 256MBメモリマップI/O PRAGMA page_size = 4096; -- ファイルシステムのブロックサイズに合わせる
3. SQLITE_BUSYの適切なハンドリング
async function executeWithRetry(db, sql, args, maxRetries = 3) { for (let i = 0; i < maxRetries; i++) { try { return await db.execute({ sql, args }); } catch (err) { if (err.code === 'SQLITE_BUSY' && i < maxRetries - 1) { await new Promise(r => setTimeout(r, Math.pow(2, i) * 100)); continue; } throw err; } } }
4. バックアップ戦略
# Litestream — S3への継続的バックアップ litestream replicate /data/app.db s3://my-bucket/app.db # ポイントインタイムリストア litestream restore -o /data/restored.db s3://my-bucket/app.db # Turso — 組み込みポイントインタイムリカバリ turso db create my-app --from-db my-app-backup --timestamp "2026-02-26T10:00:00Z"
大きな視点:なぜこれが重要なのか
SQLiteルネサンスは単にSQLiteの話ではありません。データベースの考え方における根本的な転換を象徴しています:
1. ローカリティこそ正義。 最速のデータベースクエリは、ネットワーク境界を超えないクエリです。埋め込みレプリカとエッジデータベースがこの原則を体現しています。
2. シンプルさは複利で効いてくる。 DBコネクションプールの設定、ORMの接続文字列、マネージドDBインスタンス——すべて運用コストです。SQLiteはその大部分を消し去ります。時間とともに、このシンプルさが複利のように積み上がります。
3. 「適切なツール」の定義が変わりつつある。 長年、本格的なプロジェクトの「適切なツール」はPostgreSQLでした。多くのアプリで今でもそれは正しいです。しかし、コンテンツサイト、個人プロジェクト、小規模SaaS、ローカルファーストアプリ、エッジ関数——こうした拡大するユースケースでは、モダンなツーリングを備えたSQLiteが本当に適切なツールになっています。
4. シングルテナンシーが回帰している。 マルチテナントPostgreSQLパターン(1DB、全テーブルにtenant_id)は、クエリ、マイグレーション、バックアップ、コンプライアンスに複雑さを生みます。テナント単位のSQLiteデータベースが、多くのSaaSアプリにクリーンなアーキテクチャを提供しています。
まとめ
SQLiteルネサンスは現実であり、衰える気配はありません。高速化したハードウェア、LibSQLの革新、TursoやCloudflare D1のようなプラットフォームの組み合わせが、SQLiteの歴史的制約を十分に解決し、正当なプロダクションの選択肢にしました。
ただし、何であり何でないかを明確にしておきましょう:
- リードヘビーなWebアプリ、テナント隔離が必要なSaaS、エッジデプロイ、ローカルファーストアプリ、中小規模プロジェクトには実用的なプロダクションデータベースです。
- あらゆるシナリオでPostgreSQLを置き換えるものではありません。高い書き込みスループット、複雑な分散トランザクション、RLS、高度な拡張エコシステムは引き続きPostgreSQLの領域です。
一番のアドバイスは?次のサイドプロジェクトをSQLiteで始めてみてください。 「本番データベース」までの仮のものとしてではなく、本番のDBとして。TursoかD1でホスティングし、DrizzleをORMに使い、何か作ってみてください。思った以上に遠くまで行けるはずです。
そして万が一キャパシティを超えたら——統計的にほとんどのプロジェクトではそうなりませんが——PostgreSQLへの移行はよく知られた道です。しかし、SQLiteが最初から最後まで必要なものすべてだったと気づくかもしれません。
世界で最もデプロイされたデータベースが、ようやくサーバーサイドで正当な評価を得始めています。正直に言って、やっとです。
関連ツールを見る
Pockitの無料開発者ツールを試してみましょう