<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Embedded on Tarragon</title><link>https://tarrragon.github.io/blog/tags/embedded/</link><description>Recent content in Embedded on Tarragon</description><generator>Hugo -- gohugo.io</generator><language>zh-TW</language><copyright>Tarragon (CC BY 4.0)</copyright><lastBuildDate>Thu, 21 May 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://tarrragon.github.io/blog/tags/embedded/index.xml" rel="self" type="application/rss+xml"/><item><title>SQLite</title><link>https://tarrragon.github.io/blog/backend/01-database/vendors/sqlite/</link><pubDate>Wed, 13 May 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/01-database/vendors/sqlite/</guid><description>&lt;p>SQLite 是世界上部署最多的 DB（手機、瀏覽器、car、IoT 都有）。傳統定位是 embedded、單檔案與低操作成本資料庫；multi-tenant 網路服務通常會先看 PostgreSQL、MySQL 或 managed SQL。但近年因 Cloudflare D1（serverless SQLite）、Turso（distributed SQLite）、Litestream（SQLite replication）等服務興起，出現「SQLite as production DB」的新場景。&lt;/p>
&lt;h2 id="教學路線單檔正式狀態與-local-first">教學路線：單檔正式狀態與 local-first&lt;/h2>
&lt;p>SQLite 服務頁的教學目標是把單機、單檔案、edge、desktop、test fixture 的正式狀態責任說清楚。讀者讀完後要能判斷 SQLite 何時是 production state，何時要轉向 server database、edge KV 或分散式 SQLite 變體。&lt;/p>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>學習段&lt;/th>
 &lt;th>核心問題&lt;/th>
 &lt;th>對應段落&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>Embedded state&lt;/td>
 &lt;td>單檔案資料庫如何成為 &lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/source-of-truth/" data-link-title="Source of Truth" data-link-desc="說明正式資料來源如何決定資料判斷、修復與一致性責任">source of truth&lt;/a>&lt;/td>
 &lt;td>定位、適用場景&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Local-first&lt;/td>
 &lt;td>device、edge、desktop、test fixture 的責任形狀&lt;/td>
 &lt;td>適用場景、案例對照&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Writer boundary&lt;/td>
 &lt;td>single writer、file lock、WAL 如何決定服務上限&lt;/td>
 &lt;td>容量特性、容量規劃要點&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Distributed variants&lt;/td>
 &lt;td>Turso、LiteFS、rqlite、D1 解決哪類同步或 edge 問題&lt;/td>
 &lt;td>跟其他 vendor 的取捨、章節群結構&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>替代路由&lt;/td>
 &lt;td>何時升級 PostgreSQL、MySQL、DynamoDB 或 edge KV&lt;/td>
 &lt;td>不適用場景、下一步路由&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;h2 id="定位單檔案-embedded--新興分散式-sqlite-生態">定位：單檔案 embedded + 新興分散式 SQLite 生態&lt;/h2>
&lt;p>SQLite 跟 PostgreSQL / MySQL 承擔不同層級的資料責任：&lt;/p>
&lt;ul>
&lt;li>以 function-call API 使用，省掉 server process&lt;/li>
&lt;li>單一檔案（含 schema、data、index、metadata）&lt;/li>
&lt;li>無 user / role / connection 概念&lt;/li>
&lt;li>同 process 同時 read / write 受 file lock 限制&lt;/li>
&lt;/ul>
&lt;p>傳統定位：test fixture、CLI tool data store、mobile app（iOS / Android 內建）、edge device。&lt;/p>
&lt;p>新興定位：edge serverless（Cloudflare D1）、distributed SQLite（Turso、rqlite）、replicated SQLite（Litestream）。&lt;/p>
&lt;h2 id="容量特性">容量特性&lt;/h2>
&lt;p>&lt;strong>單檔案上限&lt;/strong>：&lt;/p>
&lt;ul>
&lt;li>DB 最大 281 TB（理論）&lt;/li>
&lt;li>實務上單表 &amp;gt; 100 GB 開始有 vacuum / index 問題&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>並發寫&lt;/strong>：&lt;/p>
&lt;ul>
&lt;li>WAL mode：可同時多 reader + 1 writer&lt;/li>
&lt;li>寫入仍由 single writer boundary 控制&lt;/li>
&lt;li>寫吞吐受 disk fsync 限制（通常 &amp;lt; 1K WPS）&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>並發讀&lt;/strong>：&lt;/p>
&lt;ul>
&lt;li>WAL mode 多 reader 可同時跑&lt;/li>
&lt;li>read-only workload 可以撐高吞吐&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>Cross-process / cross-instance&lt;/strong>：&lt;/p>
&lt;ul>
&lt;li>多個 process / instance 同時寫同一檔案會破壞 single writer boundary&lt;/li>
&lt;li>需要分散時用 Litestream（replication）或 Turso（distributed）&lt;/li>
&lt;/ul>
&lt;h2 id="適用場景">適用場景&lt;/h2>
&lt;p>&lt;strong>1. Test fixture / CI 用 DB&lt;/strong>：&lt;/p>
&lt;ul>
&lt;li>整合測試需要的 fixed DB&lt;/li>
&lt;li>比 spin up PostgreSQL container 快&lt;/li>
&lt;li>對應 &lt;a href="https://tarrragon.github.io/blog/backend/01-database/repository-adapter/" data-link-title="1.4 Repository Adapter 實作" data-link-desc="Port / Adapter 邊界、row mapping、error translation、ORM vs query builder 選型、contract test 設計">1.4 Repository Adapter&lt;/a> 的 contract test 模式&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>2. CLI tool / desktop app 內建 store&lt;/strong>：&lt;/p></description><content:encoded><![CDATA[<p>SQLite 是世界上部署最多的 DB（手機、瀏覽器、car、IoT 都有）。傳統定位是 embedded、單檔案與低操作成本資料庫；multi-tenant 網路服務通常會先看 PostgreSQL、MySQL 或 managed SQL。但近年因 Cloudflare D1（serverless SQLite）、Turso（distributed SQLite）、Litestream（SQLite replication）等服務興起，出現「SQLite as production DB」的新場景。</p>
<h2 id="教學路線單檔正式狀態與-local-first">教學路線：單檔正式狀態與 local-first</h2>
<p>SQLite 服務頁的教學目標是把單機、單檔案、edge、desktop、test fixture 的正式狀態責任說清楚。讀者讀完後要能判斷 SQLite 何時是 production state，何時要轉向 server database、edge KV 或分散式 SQLite 變體。</p>
<table>
  <thead>
      <tr>
          <th>學習段</th>
          <th>核心問題</th>
          <th>對應段落</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Embedded state</td>
          <td>單檔案資料庫如何成為 <a href="/blog/backend/knowledge-cards/source-of-truth/" data-link-title="Source of Truth" data-link-desc="說明正式資料來源如何決定資料判斷、修復與一致性責任">source of truth</a></td>
          <td>定位、適用場景</td>
      </tr>
      <tr>
          <td>Local-first</td>
          <td>device、edge、desktop、test fixture 的責任形狀</td>
          <td>適用場景、案例對照</td>
      </tr>
      <tr>
          <td>Writer boundary</td>
          <td>single writer、file lock、WAL 如何決定服務上限</td>
          <td>容量特性、容量規劃要點</td>
      </tr>
      <tr>
          <td>Distributed variants</td>
          <td>Turso、LiteFS、rqlite、D1 解決哪類同步或 edge 問題</td>
          <td>跟其他 vendor 的取捨、章節群結構</td>
      </tr>
      <tr>
          <td>替代路由</td>
          <td>何時升級 PostgreSQL、MySQL、DynamoDB 或 edge KV</td>
          <td>不適用場景、下一步路由</td>
      </tr>
  </tbody>
</table>
<h2 id="定位單檔案-embedded--新興分散式-sqlite-生態">定位：單檔案 embedded + 新興分散式 SQLite 生態</h2>
<p>SQLite 跟 PostgreSQL / MySQL 承擔不同層級的資料責任：</p>
<ul>
<li>以 function-call API 使用，省掉 server process</li>
<li>單一檔案（含 schema、data、index、metadata）</li>
<li>無 user / role / connection 概念</li>
<li>同 process 同時 read / write 受 file lock 限制</li>
</ul>
<p>傳統定位：test fixture、CLI tool data store、mobile app（iOS / Android 內建）、edge device。</p>
<p>新興定位：edge serverless（Cloudflare D1）、distributed SQLite（Turso、rqlite）、replicated SQLite（Litestream）。</p>
<h2 id="容量特性">容量特性</h2>
<p><strong>單檔案上限</strong>：</p>
<ul>
<li>DB 最大 281 TB（理論）</li>
<li>實務上單表 &gt; 100 GB 開始有 vacuum / index 問題</li>
</ul>
<p><strong>並發寫</strong>：</p>
<ul>
<li>WAL mode：可同時多 reader + 1 writer</li>
<li>寫入仍由 single writer boundary 控制</li>
<li>寫吞吐受 disk fsync 限制（通常 &lt; 1K WPS）</li>
</ul>
<p><strong>並發讀</strong>：</p>
<ul>
<li>WAL mode 多 reader 可同時跑</li>
<li>read-only workload 可以撐高吞吐</li>
</ul>
<p><strong>Cross-process / cross-instance</strong>：</p>
<ul>
<li>多個 process / instance 同時寫同一檔案會破壞 single writer boundary</li>
<li>需要分散時用 Litestream（replication）或 Turso（distributed）</li>
</ul>
<h2 id="適用場景">適用場景</h2>
<p><strong>1. Test fixture / CI 用 DB</strong>：</p>
<ul>
<li>整合測試需要的 fixed DB</li>
<li>比 spin up PostgreSQL container 快</li>
<li>對應 <a href="/blog/backend/01-database/repository-adapter/" data-link-title="1.4 Repository Adapter 實作" data-link-desc="Port / Adapter 邊界、row mapping、error translation、ORM vs query builder 選型、contract test 設計">1.4 Repository Adapter</a> 的 contract test 模式</li>
</ul>
<p><strong>2. CLI tool / desktop app 內建 store</strong>：</p>
<ul>
<li>Chrome / Firefox（cookies、history、bookmark）、Fossil SCM、iOS app</li>
<li>省掉 server、單檔案攜帶</li>
</ul>
<p><strong>3. Mobile app（iOS / Android）</strong>：</p>
<ul>
<li>iOS Core Data 底層用 SQLite</li>
<li>Android 自帶 SQLite API</li>
<li>offline-first app 的標準</li>
</ul>
<p><strong>4. Single-instance backend（特殊場景）</strong>：</p>
<ul>
<li>流量小 + HA 由備份 / restore / redeploy 流程承擔</li>
<li>例：Sidekick / 個人 SaaS / family-scale app</li>
<li>配合 Litestream 做 backup / DR</li>
</ul>
<p><strong>5. Edge / serverless（新興）</strong>：</p>
<ul>
<li>Cloudflare D1：edge SQLite、跟 Workers 整合</li>
<li>Turso：distributed SQLite、跨 region replication</li>
<li>跟傳統 SQLite 不同等級、是 <em>新的 product</em></li>
</ul>
<p><strong>6. Embedded device / IoT</strong>：</p>
<ul>
<li>沒網路或要降低 server 依賴</li>
<li>SQLite 內建、無 external dependency</li>
</ul>
<h2 id="不適用場景">不適用場景</h2>
<p><strong>1. 多 instance / 多 region web service</strong>：</p>
<ul>
<li>SQLite 的單檔模型以單 instance writer 為主要邊界</li>
<li>替代：PostgreSQL、Aurora、Spanner、CockroachDB</li>
</ul>
<p><strong>2. 高寫入吞吐（&gt; 1K WPS）</strong>：</p>
<ul>
<li>fsync 限制</li>
<li>替代：任何 server-based RDBMS</li>
</ul>
<p><strong>3. Multi-user 權限管理</strong>：</p>
<ul>
<li>無 user / role 概念</li>
<li>替代：PostgreSQL / MySQL</li>
</ul>
<p><strong>4. 跨機器 transaction</strong>：</p>
<ul>
<li>SQLite 是 single-machine</li>
<li>替代：分散式 SQL</li>
</ul>
<p><strong>5. 大規模 production OLTP</strong>：</p>
<ul>
<li>大規模 production OLTP 需要 server database 的 HA、replica、權限與操作邊界</li>
<li>替代：MySQL / PostgreSQL / Aurora</li>
</ul>
<h2 id="跟其他-vendor-的取捨">跟其他 vendor 的取捨</h2>
<p><strong>vs PostgreSQL（作為 test DB）</strong>：</p>
<ul>
<li>SQLite：快 spin up、SQL dialect 接近但有差異</li>
<li>PostgreSQL：跟 production 一致、發現的 bug 真實</li>
<li>選 SQLite：speed of iteration、簡單 query</li>
<li>選 PostgreSQL：catch production-like bug、PostgreSQL-specific 特性測試</li>
</ul>
<p><strong>vs Cloudflare D1</strong>：</p>
<ul>
<li>SQLite（local）：單機、自管</li>
<li>D1：edge serverless、跟 Workers 整合</li>
<li>選 SQLite：embedded / CLI / app 場景</li>
<li>選 D1：edge web service、跟 Cloudflare 生態整合</li>
</ul>
<p><strong>vs Turso（distributed SQLite）</strong>：</p>
<ul>
<li>SQLite：單機、單檔案</li>
<li>Turso：distributed、跨 region replication、SQLite-compatible</li>
<li>選 SQLite：simple use case</li>
<li>選 Turso：需要 SQLite simplicity + 全球分散</li>
</ul>
<p><strong>vs Litestream（replicated SQLite）</strong>：</p>
<ul>
<li>SQLite：單檔案</li>
<li>Litestream：把 SQLite 變成 streaming replicated 到 S3</li>
<li>選 Litestream：想要 SQLite simplicity + DR</li>
</ul>
<p><strong>vs Firebase / Firestore（mobile app）</strong>：</p>
<ul>
<li>SQLite：embedded、offline-first、無 sync</li>
<li>Firestore：realtime、自動 sync、雲端 store</li>
<li>選 SQLite：offline-first、單機</li>
<li>選 Firestore：multi-device sync、realtime</li>
</ul>
<h2 id="容量規劃要點">容量規劃要點</h2>
<p><strong>1. WAL mode 是 production baseline</strong>：</p>
<ul>
<li>default journal mode 是 rollback journal（每寫都 lock）</li>
<li>WAL（Write-Ahead Log）讓多 reader 可同時跑</li>
<li><code>PRAGMA journal_mode = WAL</code></li>
</ul>
<p><strong>2. fsync 配置</strong>：</p>
<ul>
<li><code>PRAGMA synchronous = FULL</code>（durable、慢）</li>
<li><code>PRAGMA synchronous = NORMAL</code>（faster、少數情況可能掉資料）</li>
<li><code>PRAGMA synchronous = OFF</code>（最快、不安全）</li>
</ul>
<p><strong>3. mmap 加速 read</strong>：</p>
<ul>
<li><code>PRAGMA mmap_size = 268435456</code>（256 MB）</li>
<li>把 DB 部分內容 mmap 進 RAM、加速 read</li>
</ul>
<p><strong>4. Cache size</strong>：</p>
<ul>
<li><code>PRAGMA cache_size = -64000</code>（64 MB cache）</li>
<li>大 cache 對 read-heavy workload 有幫助</li>
</ul>
<p><strong>5. Auto-vacuum</strong>：</p>
<ul>
<li>預設 off、delete 後檔案不縮小</li>
<li><code>PRAGMA auto_vacuum = INCREMENTAL</code> + 定期 <code>PRAGMA incremental_vacuum</code></li>
</ul>
<h2 id="章節群結構">章節群結構</h2>
<p>SQLite 章節群的責任是把單檔正式狀態、embedded process、writer boundary、backup / restore、test fixture、local-first 與 edge SQLite 變體拆成可教學路線。完整結構見 <a href="teaching-structure/">SQLite Teaching Structure</a>；下表列出目前已建立的 deep article、hands-on 與 migration route。</p>
<table>
  <thead>
      <tr>
          <th>層級</th>
          <th>文件</th>
          <th>狀態</th>
          <th>教學責任</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>結構總覽</td>
          <td><a href="teaching-structure/">Teaching Structure</a></td>
          <td>已有正文</td>
          <td>對齊 PG / MySQL 與 LLM 架構，固定 SQLite 後續讀法</td>
      </tr>
      <tr>
          <td>Core deep</td>
          <td><a href="file-lifecycle-backup-boundary/">File lifecycle / backup boundary</a></td>
          <td>已有正文</td>
          <td>WAL sidecar、backup API、restore drill、corruption route</td>
      </tr>
      <tr>
          <td>Hands-on</td>
          <td><a href="hands-on/">Hands-on 操作路線</a></td>
          <td>已有正文</td>
          <td>local file、backup restore、WAL busy、migration fixture</td>
      </tr>
      <tr>
          <td>Concurrency</td>
          <td><a href="wal-concurrency-locking/">WAL concurrency / locking</a></td>
          <td>已有正文</td>
          <td>single writer、file lock、<code>SQLITE_BUSY</code>、checkpoint</td>
      </tr>
      <tr>
          <td>Performance</td>
          <td><a href="pragma-tuning-performance/">PRAGMA tuning / performance</a></td>
          <td>已有正文</td>
          <td>journal、sync、cache、mmap、vacuum 的取捨</td>
      </tr>
      <tr>
          <td>Migration</td>
          <td><a href="schema-migration-versioning/">Schema migration / versioning</a></td>
          <td>已有正文</td>
          <td>app release、schema version、rollback、migration evidence</td>
      </tr>
      <tr>
          <td>Testing</td>
          <td><a href="test-fixture-best-practice/">Test fixture best practice</a></td>
          <td>已有正文</td>
          <td>SQLite 測試便利性與 production dialect gap</td>
      </tr>
      <tr>
          <td>Embedded app</td>
          <td><a href="mobile-desktop-embedded-store/">Mobile / desktop embedded store</a></td>
          <td>已有正文</td>
          <td>device local state、privacy、backup、app version</td>
      </tr>
      <tr>
          <td>Sync</td>
          <td><a href="local-first-sync-boundary/">Local-first sync boundary</a></td>
          <td>已有正文</td>
          <td>多裝置同步、conflict、server authority</td>
      </tr>
      <tr>
          <td>Edge variant</td>
          <td><a href="d1-turso-libsql-comparison/">D1 / Turso / libSQL comparison</a></td>
          <td>已有正文</td>
          <td>edge SQLite 產品與 local SQLite 的責任差異</td>
      </tr>
      <tr>
          <td>Replication</td>
          <td><a href="litestream-litefs-replication/">Litestream / LiteFS replication</a></td>
          <td>已有正文</td>
          <td>continuous backup、read replica、failover boundary</td>
      </tr>
      <tr>
          <td>SQL compatibility</td>
          <td><a href="sql-dialect-index-limits/">SQL dialect and index limits</a></td>
          <td>已有正文</td>
          <td>type affinity、index、constraint、PostgreSQL / MySQL gap</td>
      </tr>
      <tr>
          <td>Operations</td>
          <td><a href="observability-runbook/">Observability / runbook</a></td>
          <td>已有正文</td>
          <td>busy errors、WAL growth、backup evidence、incident route</td>
      </tr>
      <tr>
          <td>Migration route</td>
          <td><a href="migrate-to-postgresql/">SQLite to PostgreSQL</a></td>
          <td>已有正文</td>
          <td>多 tenant、權限、HA、audit 出現時的升級路線</td>
      </tr>
      <tr>
          <td>Migration route</td>
          <td><a href="migrate-to-d1-turso/">SQLite to D1 / Turso</a></td>
          <td>已有正文</td>
          <td>edge / serverless 化路線</td>
      </tr>
      <tr>
          <td>Migration route</td>
          <td><a href="migrate-from-postgresql-simplification/">PostgreSQL to SQLite simplification</a></td>
          <td>已有正文</td>
          <td>single-user / embedded 工具的反向簡化路線</td>
      </tr>
  </tbody>
</table>
<p>章節群的讀法是先讀 file lifecycle，再按壓力選 deep article。若問題是 write contention，讀 WAL locking；若問題是測試，讀 test fixture；若問題是 edge / serverless，讀 D1 / Turso comparison；若問題是服務長大，讀 SQLite to PostgreSQL migration。</p>
<h2 id="anti-recommendation-與升級路由">Anti-recommendation 與升級路由</h2>
<p>SQLite 的低操作成本容易讓團隊忽略它的 writer boundary。這一段先說何時維持 SQLite，再說何時升級到 server SQL、edge SQLite 變體或 managed KV。</p>
<table>
  <thead>
      <tr>
          <th>機制 / 路線</th>
          <th>維持簡單設計的條件</th>
          <th>升級訊號</th>
          <th>主要引用路徑</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Local SQLite</td>
          <td>單 process、單 writer、資料可用檔案備份保護</td>
          <td>多 instance 寫入、需要 HA、需要資料層權限</td>
          <td><a href="/blog/backend/knowledge-cards/database/" data-link-title="Database" data-link-desc="說明 database 在後端系統中如何承擔正式狀態、查詢與一致性責任">Database</a>、<a href="/blog/backend/knowledge-cards/source-of-truth/" data-link-title="Source of Truth" data-link-desc="說明正式資料來源如何決定資料判斷、修復與一致性責任">Source of Truth</a></td>
      </tr>
      <tr>
          <td>WAL + file backup</td>
          <td>read-heavy、寫入量低、RPO 可接受定期 snapshot</td>
          <td>restore 演練失敗、WAL growth 失控、RPO / RTO 變嚴格</td>
          <td><a href="/blog/backend/knowledge-cards/rpo/" data-link-title="RPO" data-link-desc="說明恢復點目標如何定義可接受資料損失範圍">RPO</a>、<a href="/blog/backend/knowledge-cards/rto/" data-link-title="RTO" data-link-desc="說明恢復時間目標如何約束事故回復策略">RTO</a></td>
      </tr>
      <tr>
          <td>Litestream / LiteFS</td>
          <td>單 primary 寫入清楚、主要需求是 backup 或 read replica</td>
          <td>需要多地 active write、跨 region transaction</td>
          <td><a href="/blog/backend/knowledge-cards/replication-lag/" data-link-title="Replication Lag" data-link-desc="說明資料副本落後正式來源多久，以及它如何影響讀取正確性">Replication Lag</a>、<a href="/blog/backend/knowledge-cards/stale-read/" data-link-title="Stale Read" data-link-desc="讀取到落後於最新寫入版本的舊資料">Stale Read</a></td>
      </tr>
      <tr>
          <td>Cloudflare D1 / Turso</td>
          <td>edge / serverless 生態已是主平台</td>
          <td>SQL 特性、migration、observability 或 vendor 限制卡住</td>
          <td><a href="/blog/backend/01-database/global-distributed-oltp/" data-link-title="1.11 全球分散式 OLTP" data-link-desc="Spanner / Aurora DSQL / Cosmos DB multi-region write / CockroachDB / TiDB 的全球一致性取捨">1.11 全球分散式 OLTP</a></td>
      </tr>
      <tr>
          <td>PostgreSQL / MySQL</td>
          <td>application 已進入多服務、多 tenant、權限與備份治理需求</td>
          <td>schema migration、connection、audit 與 failover 成主題</td>
          <td><a href="/blog/backend/01-database/vendors/postgresql/" data-link-title="PostgreSQL" data-link-desc="多用途 OLTP 主流關聯式資料庫、MVCC、豐富 SQL 特性、是 Aurora / Cosmos DB / Spanner / CockroachDB / Aurora DSQL 的相容目標">PostgreSQL vendor</a>、<a href="/blog/backend/01-database/vendors/mysql/" data-link-title="MySQL" data-link-desc="高併發網路服務常用關聯式資料庫、Vitess / PlanetScale 分片生態、GitHub / Shopify / Facebook 規模驗證">MySQL vendor</a></td>
      </tr>
  </tbody>
</table>
<p>SQLite 的簡單路徑是讓檔案生命週期成為正式操作流程。只要單一 writer、備份、restore、migration 與 file ownership 都能被 runbook 控制，SQLite 可以是正式狀態，而非臨時 cache。</p>
<p>升級到 server SQL 的訊號是操作責任超過檔案邊界。當團隊需要資料庫帳號、權限分層、read replica、線上 schema migration、集中 audit 或跨 instance failover 時，PostgreSQL / MySQL / Aurora 會比繼續包裝 SQLite 更清楚。</p>
<h2 id="已知-limitation-與後續路由">已知 limitation 與後續路由</h2>
<p>SQLite overview 目前已完成服務判斷與章節群正文路由。File lifecycle、WAL locking、PRAGMA tuning、schema migration、test fixture、local-first sync、edge product 差異、observability、hands-on 與 migration route 都已有對應正文；下一輪審查可集中在案例補強、引用精度與跨章重複整理。</p>
<h2 id="案例對照">案例對照</h2>
<p>SQLite 不在 09 case 庫的「規模化 vendor」類別、但作為 <em>embedded 跟 test</em> 廣泛使用：</p>
<ul>
<li>iOS Core Data：所有 iOS app 的 default DB</li>
<li>Chrome / Firefox：cookie、history、bookmark</li>
<li>Fossil SCM：repository metadata 與 application-file use case</li>
<li>Cloudflare D1：edge serverless（新興 production 場景）</li>
<li>Turso：distributed SQLite（新興 production 場景）</li>
</ul>
<h2 id="常見陷阱">常見陷阱</h2>
<ul>
<li><strong>default journal mode 不改 WAL</strong>：read 跟 write 互相 block、performance 差</li>
<li><strong>多 process / instance 同時寫同檔</strong>：corruption</li>
<li><strong>delete 後檔案沒縮小</strong>：忘了 vacuum</li>
<li><strong>synchronous=OFF 給 production</strong>：power loss 可能掉資料</li>
<li><strong>SQLite 跟 PostgreSQL 行為差異測試不足</strong>：SQLite test 過、PostgreSQL production 出 bug（特別是 date / time、NULL 處理、type coercion）</li>
</ul>
<h2 id="下一步路由">下一步路由</h2>
<ul>
<li>完整 T1 對照：<a href="/blog/backend/01-database/vendors/" data-link-title="資料庫 Vendor 清單" data-link-desc="規劃 SQL、managed SQL、document、KV 與 distributed SQL 的服務頁撰寫順序與教學大綱">01-database vendors index</a></li>
<li>平行：<a href="/blog/backend/01-database/vendors/postgresql/" data-link-title="PostgreSQL" data-link-desc="多用途 OLTP 主流關聯式資料庫、MVCC、豐富 SQL 特性、是 Aurora / Cosmos DB / Spanner / CockroachDB / Aurora DSQL 的相容目標">PostgreSQL vendor</a> / <a href="/blog/backend/01-database/vendors/mysql/" data-link-title="MySQL" data-link-desc="高併發網路服務常用關聯式資料庫、Vitess / PlanetScale 分片生態、GitHub / Shopify / Facebook 規模驗證">MySQL vendor</a>（production server-based RDBMS）</li>
<li>上游：<a href="/blog/backend/01-database/repository-adapter/" data-link-title="1.4 Repository Adapter 實作" data-link-desc="Port / Adapter 邊界、row mapping、error translation、ORM vs query builder 選型、contract test 設計">1.4 Repository Adapter</a>（test fixture 模式）</li>
<li>結構：<a href="/blog/backend/01-database/vendors/sqlite/teaching-structure/" data-link-title="SQLite Teaching Structure" data-link-desc="SQLite 服務章節群的大綱：從 embedded formal state、WAL、backup、test fixture、local-first、edge SQLite 到遷移路由">SQLite Teaching Structure</a>（完整章節群與寫作順序）</li>
<li>操作：<a href="/blog/backend/01-database/vendors/sqlite/hands-on/" data-link-title="SQLite Hands-on 操作路線" data-link-desc="SQLite local file lab、backup / restore drill、WAL busy reproduction、migration fixture、D1 / Turso preview 的操作型章節設計">SQLite Hands-on</a>（local file、backup restore、WAL busy reproduction、migration fixture、D1 / Turso preview）</li>
<li>深入：<a href="/blog/backend/01-database/vendors/sqlite/file-lifecycle-backup-boundary/" data-link-title="SQLite file lifecycle 與 backup boundary" data-link-desc="把 SQLite 單檔案正式狀態拆成 WAL、backup API、restore drill、corruption recovery 與操作責任邊界">SQLite file lifecycle 與 backup boundary</a>（WAL、backup、restore、file ownership）</li>
<li>官方：<a href="https://sqlite.org/docs.html">SQLite Documentation</a>、<a href="https://litestream.io/">Litestream</a>、<a href="https://turso.tech/">Turso</a>、<a href="https://developers.cloudflare.com/d1/">Cloudflare D1</a></li>
</ul>
]]></content:encoded></item><item><title>SQLite Mobile / Desktop Embedded Store</title><link>https://tarrragon.github.io/blog/backend/01-database/vendors/sqlite/mobile-desktop-embedded-store/</link><pubDate>Thu, 21 May 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/01-database/vendors/sqlite/mobile-desktop-embedded-store/</guid><description>&lt;blockquote>
&lt;p>本文是 &lt;a href="https://tarrragon.github.io/blog/backend/01-database/vendors/sqlite/" data-link-title="SQLite" data-link-desc="embedded、單檔案、test / CLI / edge 場景的標準選擇、近年因 Cloudflare D1 / Turso 等服務復興">SQLite&lt;/a> overview 的 implementation-layer deep article。Overview 已說明 SQLite 適合 mobile、desktop、CLI 與 embedded device；本文聚焦 &lt;em>device-local formal state 的資料責任、backup、privacy 與 sync boundary&lt;/em>。&lt;/p>&lt;/blockquote>
&lt;p>SQLite embedded store 的核心責任是讓 application process 在本機持有正式狀態。Mobile app、desktop app、browser profile、CLI tool 與 embedded device 常用 SQLite 保存 local data；這些資料可能只是 cache，也可能是使用者唯一資料來源。教學上要先判斷它是否承擔 &lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/source-of-truth/" data-link-title="Source of Truth" data-link-desc="說明正式資料來源如何決定資料判斷、修復與一致性責任">source of truth&lt;/a>，再決定 backup、sync、privacy 與 migration 責任。&lt;/p>
&lt;p>本文的判讀錨點是：embedded SQLite 的 production boundary 在 device lifecycle，database server 層的邊界在這裡不適用。OS backup、app upgrade、device loss、profile corruption、local PII、multi-device sync 與 user export / delete 都是資料庫責任的一部分。&lt;/p>
&lt;h2 id="embedded-state-model">Embedded state model&lt;/h2>
&lt;p>Embedded state model 的核心責任是把 local database file 放回 application lifecycle。SQLite 是典型的 &lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/embedded-database/" data-link-title="Embedded Database" data-link-desc="說明嵌入式資料庫如何隨 application process 運作，並把檔案生命週期責任交回應用">embedded database&lt;/a>：database file 通常跟著 app sandbox、user profile、CLI config directory 或 device storage 存在，它的 owner 是 application，而非獨立 DBA。&lt;/p>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>場景&lt;/th>
 &lt;th>SQLite 資料角色&lt;/th>
 &lt;th>主要風險&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>Mobile app&lt;/td>
 &lt;td>offline state、draft、cache、local profile&lt;/td>
 &lt;td>app upgrade、device loss、cloud backup leakage&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Desktop app&lt;/td>
 &lt;td>user profile、history、settings&lt;/td>
 &lt;td>profile corruption、manual file copy、multi-version app&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>CLI tool&lt;/td>
 &lt;td>local index、metadata、state cache&lt;/td>
 &lt;td>command interruption、portable file path&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Browser / profile&lt;/td>
 &lt;td>cookies、history、bookmark 類資料&lt;/td>
 &lt;td>privacy、profile migration、lock collision&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Embedded device&lt;/td>
 &lt;td>offline event、sensor / config state&lt;/td>
 &lt;td>power loss、flash wear、delayed sync&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>這張表的重點是資料角色而非產品名稱。同樣是 SQLite file，cache 可以清掉重建；draft、local-only note、sensor event 或 user history 可能需要正式 backup / export / delete。&lt;/p>
&lt;h2 id="backup-與-export">Backup 與 export&lt;/h2>
&lt;p>Embedded backup 的核心責任是讓使用者或服務能從 device / profile failure 復原。Mobile / desktop / CLI 的 backup 路徑常和 OS backup、app export、cloud sync 或手動複製混在一起；SQLite file lifecycle 要明確。&lt;/p></description><content:encoded><![CDATA[<blockquote>
<p>本文是 <a href="/blog/backend/01-database/vendors/sqlite/" data-link-title="SQLite" data-link-desc="embedded、單檔案、test / CLI / edge 場景的標準選擇、近年因 Cloudflare D1 / Turso 等服務復興">SQLite</a> overview 的 implementation-layer deep article。Overview 已說明 SQLite 適合 mobile、desktop、CLI 與 embedded device；本文聚焦 <em>device-local formal state 的資料責任、backup、privacy 與 sync boundary</em>。</p></blockquote>
<p>SQLite embedded store 的核心責任是讓 application process 在本機持有正式狀態。Mobile app、desktop app、browser profile、CLI tool 與 embedded device 常用 SQLite 保存 local data；這些資料可能只是 cache，也可能是使用者唯一資料來源。教學上要先判斷它是否承擔 <a href="/blog/backend/knowledge-cards/source-of-truth/" data-link-title="Source of Truth" data-link-desc="說明正式資料來源如何決定資料判斷、修復與一致性責任">source of truth</a>，再決定 backup、sync、privacy 與 migration 責任。</p>
<p>本文的判讀錨點是：embedded SQLite 的 production boundary 在 device lifecycle，database server 層的邊界在這裡不適用。OS backup、app upgrade、device loss、profile corruption、local PII、multi-device sync 與 user export / delete 都是資料庫責任的一部分。</p>
<h2 id="embedded-state-model">Embedded state model</h2>
<p>Embedded state model 的核心責任是把 local database file 放回 application lifecycle。SQLite 是典型的 <a href="/blog/backend/knowledge-cards/embedded-database/" data-link-title="Embedded Database" data-link-desc="說明嵌入式資料庫如何隨 application process 運作，並把檔案生命週期責任交回應用">embedded database</a>：database file 通常跟著 app sandbox、user profile、CLI config directory 或 device storage 存在，它的 owner 是 application，而非獨立 DBA。</p>
<table>
  <thead>
      <tr>
          <th>場景</th>
          <th>SQLite 資料角色</th>
          <th>主要風險</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Mobile app</td>
          <td>offline state、draft、cache、local profile</td>
          <td>app upgrade、device loss、cloud backup leakage</td>
      </tr>
      <tr>
          <td>Desktop app</td>
          <td>user profile、history、settings</td>
          <td>profile corruption、manual file copy、multi-version app</td>
      </tr>
      <tr>
          <td>CLI tool</td>
          <td>local index、metadata、state cache</td>
          <td>command interruption、portable file path</td>
      </tr>
      <tr>
          <td>Browser / profile</td>
          <td>cookies、history、bookmark 類資料</td>
          <td>privacy、profile migration、lock collision</td>
      </tr>
      <tr>
          <td>Embedded device</td>
          <td>offline event、sensor / config state</td>
          <td>power loss、flash wear、delayed sync</td>
      </tr>
  </tbody>
</table>
<p>這張表的重點是資料角色而非產品名稱。同樣是 SQLite file，cache 可以清掉重建；draft、local-only note、sensor event 或 user history 可能需要正式 backup / export / delete。</p>
<h2 id="backup-與-export">Backup 與 export</h2>
<p>Embedded backup 的核心責任是讓使用者或服務能從 device / profile failure 復原。Mobile / desktop / CLI 的 backup 路徑常和 OS backup、app export、cloud sync 或手動複製混在一起；SQLite file lifecycle 要明確。</p>
<table>
  <thead>
      <tr>
          <th>路徑</th>
          <th>適合資料</th>
          <th>注意事項</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>OS / device backup</td>
          <td>user-owned local state</td>
          <td>local PII、encryption、restore compatibility</td>
      </tr>
      <tr>
          <td>App export</td>
          <td>使用者可攜資料</td>
          <td>schema version、format stability、privacy</td>
      </tr>
      <tr>
          <td><code>.backup</code> / snapshot</td>
          <td>application-managed backup</td>
          <td>live DB consistency、WAL sidecar handling</td>
      </tr>
      <tr>
          <td>Cloud sync</td>
          <td>multi-device state</td>
          <td>conflict、server authority、delete propagation</td>
      </tr>
  </tbody>
</table>
<p>Backup 設計要先決定 restore target。Restore 到同 app version、未來 app version、或不同 device，會帶來不同 schema compatibility 與 privacy requirement。</p>
<h2 id="privacy-與-local-pii">Privacy 與 local PII</h2>
<p>Embedded SQLite 的 privacy 責任是治理 device-local data。資料在 server DB 中通常有 access log、IAM、DLP 與 retention policy；進入 SQLite file 後，風險轉到 device encryption、app sandbox、backup retention、debug export 與 support bundle。</p>
<table>
  <thead>
      <tr>
          <th>風險</th>
          <th>真實情境</th>
          <th>控制方向</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Local PII</td>
          <td>profile、token、message、draft</td>
          <td>最小化欄位、加密敏感值、限制 export</td>
      </tr>
      <tr>
          <td>Backup leakage</td>
          <td>OS cloud backup 含 database file</td>
          <td>設定 backup exclusion 或加密</td>
      </tr>
      <tr>
          <td>Support bundle</td>
          <td>使用者回報問題附上 DB</td>
          <td>scrub / redaction、只匯出必要 table</td>
      </tr>
      <tr>
          <td>Delete request</td>
          <td>server 刪除但 device local 留存</td>
          <td>sync delete、local purge、retention evidence</td>
      </tr>
  </tbody>
</table>
<p>SQLite file 要進入資料保護盤點。若 local DB 保存敏感資料，應連到 <a href="/blog/backend/07-security-data-protection/data-protection-and-masking-governance/" data-link-title="7.4 資料保護與遮罩治理" data-link-desc="以問題驅動方式整理資料分級、遮罩、匯出與備份治理">Data Protection</a> 與 <a href="/blog/backend/knowledge-cards/audit-log/" data-link-title="Audit Log" data-link-desc="說明高風險操作如何留下可追溯、可稽核的紀錄">Audit Log</a> 的相同問題，只是控制面改在 device / app。</p>
<h2 id="app-upgrade-與-schema-compatibility">App upgrade 與 schema compatibility</h2>
<p>App upgrade 的核心責任是保證新版 binary 能安全打開舊 database file。Mobile / desktop app 的使用者不會按照 backend deployment order 升級；同一時間可能存在多個 app version 與多個 DB schema version。</p>
<table>
  <thead>
      <tr>
          <th>問題</th>
          <th>設計策略</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>新 app 打舊 DB</td>
          <td>startup migration、<code>user_version</code>、backup before migration</td>
      </tr>
      <tr>
          <td>舊 app 打新 DB</td>
          <td>backward-compatible column、feature gate、minimum supported version</td>
      </tr>
      <tr>
          <td>使用者降版</td>
          <td>export / import、read-only fallback、no-downgrade notice</td>
      </tr>
      <tr>
          <td>多裝置不同版本</td>
          <td>sync protocol version、server-side compatibility</td>
      </tr>
  </tbody>
</table>
<p>這些策略要和 <a href="/blog/backend/01-database/vendors/sqlite/schema-migration-versioning/" data-link-title="SQLite Schema Migration and Versioning" data-link-desc="SQLite schema migration、user_version、table rebuild、ALTER TABLE 限制、app release compatibility 與 migration evidence">Schema Migration / Versioning</a> 對齊。Embedded app 的 migration failure 通常直接影響使用者啟動體驗，因此 migration 要能快速、可恢復、可診斷。</p>
<h2 id="sync-boundary">Sync boundary</h2>
<p>Sync boundary 的核心責任是把 single-device SQLite 和 multi-device state 分開。SQLite 保存本地狀態；跨裝置同步需要 transport、identity、conflict resolution、delete propagation 與 server authority。</p>
<table>
  <thead>
      <tr>
          <th>Sync 需求</th>
          <th>SQLite 角色</th>
          <th>下一步路由</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>單裝置 offline</td>
          <td>local source of truth</td>
          <td>SQLite + backup / export</td>
      </tr>
      <tr>
          <td>多裝置同步</td>
          <td>local replica / cache</td>
          <td><a href="/blog/backend/01-database/vendors/sqlite/local-first-sync-boundary/" data-link-title="SQLite Local-first Sync Boundary" data-link-desc="SQLite local-first app、multi-device sync、server authority、conflict resolution、delete propagation 與 offline-first trade-off">Local-first sync boundary</a></td>
      </tr>
      <tr>
          <td>即時多人協作</td>
          <td>local working copy</td>
          <td>server authority、CRDT、event log</td>
      </tr>
      <tr>
          <td>Server reporting</td>
          <td>local data upload / ETL</td>
          <td>API sync、queue、analytics store</td>
      </tr>
  </tbody>
</table>
<p>當 sync 需求出現時，SQLite 仍可作為 local store，但不再單獨承擔完整資料一致性。完整性要由 sync protocol 與 server-side validation 補上。</p>
<h2 id="production-踩雷">Production 踩雷</h2>
<h3 id="case-1把-cache-當正式資料">Case 1：把 cache 當正式資料</h3>
<p>Cache 被誤當正式資料的核心風險是清除 local DB 會造成不可恢復資料損失。許多 app 初期把 SQLite 當 cache；後來加入 draft、offline action 或 local-only setting，資料責任就改變了。</p>
<p>修正方向是逐 table 標示資料角色。Cache table 可清；formal state table 要 backup、migration、export 與 delete policy。</p>
<h3 id="case-2os-backup-帶走敏感資料">Case 2：OS backup 帶走敏感資料</h3>
<p>OS backup 的核心風險是 device-local PII 進入使用者或平台雲端備份。Server 端已刪除的資料，可能仍存在 device backup。</p>
<p>修正方向是決定哪些資料可被備份。Token、secret、敏感 PII 可排除或加密；user-owned content 則要提供 export / restore 語意。</p>
<h3 id="case-3app-upgrade-migration-失敗讓使用者卡在啟動頁">Case 3：App upgrade migration 失敗讓使用者卡在啟動頁</h3>
<p>Startup migration 失敗的核心風險是使用者卡在 app 啟動前，且修復能力有限。SQLite file 在使用者裝置上，SRE 通常需要透過 app update、support bundle 或 restore flow 處理。</p>
<p>修正方向是保留 pre-migration snapshot、提供 safe mode、收集匿名 schema / error evidence，並避免長 migration 放在 cold start。</p>
<h2 id="操作檢查清單">操作檢查清單</h2>
<p>Embedded SQLite 設計要回答：</p>
<ol>
<li>每張 table 是 cache、formal state、derived state 還是 sync queue。</li>
<li>Database file 在 app / OS 的哪個 storage boundary。</li>
<li>OS backup 是否包含 database file。</li>
<li>敏感欄位是否加密、排除或可清除。</li>
<li>App upgrade migration 是否有 pre-migration backup。</li>
<li>使用者 export / delete / support bundle 如何處理 SQLite data。</li>
<li>Multi-device sync 是否有 conflict 與 server authority 設計。</li>
</ol>
<h2 id="下一步路由">下一步路由</h2>
<ul>
<li>上游：<a href="/blog/backend/01-database/vendors/sqlite/" data-link-title="SQLite" data-link-desc="embedded、單檔案、test / CLI / edge 場景的標準選擇、近年因 Cloudflare D1 / Turso 等服務復興">SQLite overview</a></li>
<li>Sibling：<a href="/blog/backend/01-database/vendors/sqlite/local-first-sync-boundary/" data-link-title="SQLite Local-first Sync Boundary" data-link-desc="SQLite local-first app、multi-device sync、server authority、conflict resolution、delete propagation 與 offline-first trade-off">Local-first Sync Boundary</a>、<a href="/blog/backend/01-database/vendors/sqlite/schema-migration-versioning/" data-link-title="SQLite Schema Migration and Versioning" data-link-desc="SQLite schema migration、user_version、table rebuild、ALTER TABLE 限制、app release compatibility 與 migration evidence">Schema Migration / Versioning</a></li>
<li>操作：<a href="/blog/backend/01-database/vendors/sqlite/hands-on/" data-link-title="SQLite Hands-on 操作路線" data-link-desc="SQLite local file lab、backup / restore drill、WAL busy reproduction、migration fixture、D1 / Turso preview 的操作型章節設計">SQLite Hands-on</a></li>
<li>跨模組：<a href="/blog/backend/07-security-data-protection/data-protection-and-masking-governance/" data-link-title="7.4 資料保護與遮罩治理" data-link-desc="以問題驅動方式整理資料分級、遮罩、匯出與備份治理">Data Protection</a></li>
<li>官方：<a href="https://www.sqlite.org/whentouse.html">SQLite Appropriate Uses</a>、<a href="https://www.sqlite.org/backup.html">SQLite Backup API</a></li>
</ul>
]]></content:encoded></item></channel></rss>