<?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>Pragma on Tarragon</title><link>https://tarrragon.github.io/blog/tags/pragma/</link><description>Recent content in Pragma 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/pragma/index.xml" rel="self" type="application/rss+xml"/><item><title>SQLite PRAGMA Tuning and Performance</title><link>https://tarrragon.github.io/blog/backend/01-database/vendors/sqlite/pragma-tuning-performance/</link><pubDate>Thu, 21 May 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/01-database/vendors/sqlite/pragma-tuning-performance/</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 的容量規劃要點；本文聚焦 &lt;em>PRAGMA 設定如何變成 durability、latency、檔案大小與 restore risk 的取捨&lt;/em>。&lt;/p>&lt;/blockquote>
&lt;p>SQLite PRAGMA tuning 的核心責任是把單檔資料庫的行為固定成可重複、可觀測、可回退的操作契約。SQLite 的許多重要行為由 connection-level 或 database-level PRAGMA 控制；這些設定看起來像小開關，實際上會影響 crash recovery、commit latency、reader / writer 衝突、檔案大小與測試一致性。&lt;/p>
&lt;p>本文的判讀錨點是：PRAGMA 是 durability / latency / maintenance 的顯性取捨，而非效能魔法。Production runbook 要記錄設定值、設定時機、驗證 query 與回退條件，避免不同 process、test runner 或 migration tool 用不同 SQLite 行為。&lt;/p>
&lt;h2 id="baseline-pragma">Baseline PRAGMA&lt;/h2>
&lt;p>SQLite baseline PRAGMA 的責任是讓 application 每次啟動都進入同一個資料庫模式。對 production-like local store、small backend 或 test fixture，建議把 journal、sync、foreign key、busy timeout 與 checkpoint 明確設定，而非依賴語言 binding 預設值。&lt;/p>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-sql" data-lang="sql">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">&lt;span class="n">PRAGMA&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">journal_mode&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">WAL&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">PRAGMA&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">synchronous&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">NORMAL&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">3&lt;/span>&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">PRAGMA&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">foreign_keys&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">ON&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">4&lt;/span>&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">PRAGMA&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">busy_timeout&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="mi">5000&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">5&lt;/span>&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">PRAGMA&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">wal_autocheckpoint&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="mi">1000&lt;/span>&lt;span class="p">;&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&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>&lt;code>journal_mode=WAL&lt;/code>&lt;/td>
 &lt;td>降低 reader / writer 衝突&lt;/td>
 &lt;td>回傳值為 &lt;code>wal&lt;/code>，觀察 &lt;code>-wal&lt;/code> file&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>synchronous=NORMAL&lt;/code>&lt;/td>
 &lt;td>平衡 fsync cost 與 crash durability&lt;/td>
 &lt;td>查 &lt;code>PRAGMA synchronous&lt;/code>，跑 restore drill&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>foreign_keys=ON&lt;/code>&lt;/td>
 &lt;td>啟用 FK enforcement&lt;/td>
 &lt;td>&lt;code>PRAGMA foreign_key_check&lt;/code>&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>busy_timeout&lt;/code>&lt;/td>
 &lt;td>吸收短暫 writer queue&lt;/td>
 &lt;td>記錄 busy wait 與 timeout rate&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>wal_autocheckpoint&lt;/code>&lt;/td>
 &lt;td>控制 WAL growth cadence&lt;/td>
 &lt;td>觀察 WAL size 與 checkpoint duration&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>這張表的重點是把設定與 evidence 綁在一起。若某個 PRAGMA 缺少成功訊號與失敗訊號，就先維持保守預設；盲目追求「最快」通常會把風險推到 power loss、restore 或長尾 latency。&lt;/p>
&lt;h2 id="journal_mode-與-wal-boundary">&lt;code>journal_mode&lt;/code> 與 WAL boundary&lt;/h2>
&lt;p>&lt;code>journal_mode&lt;/code> 的核心責任是決定 transaction 如何保護原始資料。SQLite 預設 rollback journal 對簡單場景合理；WAL mode 則讓 reader 可以在 writer append WAL 時保有 snapshot，適合多 reader、短寫入、互動式 workload。&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>&lt;code>DELETE&lt;/code>&lt;/td>
 &lt;td>最簡單、低併發、短生命週期檔案&lt;/td>
 &lt;td>write / read 衝突較明顯&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>WAL&lt;/code>&lt;/td>
 &lt;td>read-heavy、local app、小型 API&lt;/td>
 &lt;td>需要治理 &lt;code>-wal&lt;/code>、&lt;code>-shm&lt;/code>、checkpoint&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>MEMORY&lt;/code>&lt;/td>
 &lt;td>暫存測試、可丟資料&lt;/td>
 &lt;td>crash 後 recovery 風險高&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>OFF&lt;/code>&lt;/td>
 &lt;td>可重建資料、一次性 bulk load&lt;/td>
 &lt;td>production formal state 應避開&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>WAL mode 是多數 production-like SQLite 的 baseline，但它也引入 sidecar file 與 checkpoint 責任。完整判讀見 &lt;a href="https://tarrragon.github.io/blog/backend/01-database/vendors/sqlite/wal-concurrency-locking/" data-link-title="SQLite WAL Concurrency and Locking" data-link-desc="SQLite WAL mode 如何降低 reader / writer 衝突、保留 single writer boundary，並用 SQLITE_BUSY、WAL growth、checkpoint 訊號判斷 production 上限">WAL concurrency / locking&lt;/a>。&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 的容量規劃要點；本文聚焦 <em>PRAGMA 設定如何變成 durability、latency、檔案大小與 restore risk 的取捨</em>。</p></blockquote>
<p>SQLite PRAGMA tuning 的核心責任是把單檔資料庫的行為固定成可重複、可觀測、可回退的操作契約。SQLite 的許多重要行為由 connection-level 或 database-level PRAGMA 控制；這些設定看起來像小開關，實際上會影響 crash recovery、commit latency、reader / writer 衝突、檔案大小與測試一致性。</p>
<p>本文的判讀錨點是：PRAGMA 是 durability / latency / maintenance 的顯性取捨，而非效能魔法。Production runbook 要記錄設定值、設定時機、驗證 query 與回退條件，避免不同 process、test runner 或 migration tool 用不同 SQLite 行為。</p>
<h2 id="baseline-pragma">Baseline PRAGMA</h2>
<p>SQLite baseline PRAGMA 的責任是讓 application 每次啟動都進入同一個資料庫模式。對 production-like local store、small backend 或 test fixture，建議把 journal、sync、foreign key、busy timeout 與 checkpoint 明確設定，而非依賴語言 binding 預設值。</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="ln">1</span><span class="cl"><span class="n">PRAGMA</span><span class="w"> </span><span class="n">journal_mode</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">WAL</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="w"></span><span class="n">PRAGMA</span><span class="w"> </span><span class="n">synchronous</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">NORMAL</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="w"></span><span class="n">PRAGMA</span><span class="w"> </span><span class="n">foreign_keys</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">ON</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="w"></span><span class="n">PRAGMA</span><span class="w"> </span><span class="n">busy_timeout</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">5000</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="w"></span><span class="n">PRAGMA</span><span class="w"> </span><span class="n">wal_autocheckpoint</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">1000</span><span class="p">;</span></span></span></code></pre></div><table>
  <thead>
      <tr>
          <th>設定</th>
          <th>服務責任</th>
          <th>驗證方式</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><code>journal_mode=WAL</code></td>
          <td>降低 reader / writer 衝突</td>
          <td>回傳值為 <code>wal</code>，觀察 <code>-wal</code> file</td>
      </tr>
      <tr>
          <td><code>synchronous=NORMAL</code></td>
          <td>平衡 fsync cost 與 crash durability</td>
          <td>查 <code>PRAGMA synchronous</code>，跑 restore drill</td>
      </tr>
      <tr>
          <td><code>foreign_keys=ON</code></td>
          <td>啟用 FK enforcement</td>
          <td><code>PRAGMA foreign_key_check</code></td>
      </tr>
      <tr>
          <td><code>busy_timeout</code></td>
          <td>吸收短暫 writer queue</td>
          <td>記錄 busy wait 與 timeout rate</td>
      </tr>
      <tr>
          <td><code>wal_autocheckpoint</code></td>
          <td>控制 WAL growth cadence</td>
          <td>觀察 WAL size 與 checkpoint duration</td>
      </tr>
  </tbody>
</table>
<p>這張表的重點是把設定與 evidence 綁在一起。若某個 PRAGMA 缺少成功訊號與失敗訊號，就先維持保守預設；盲目追求「最快」通常會把風險推到 power loss、restore 或長尾 latency。</p>
<h2 id="journal_mode-與-wal-boundary"><code>journal_mode</code> 與 WAL boundary</h2>
<p><code>journal_mode</code> 的核心責任是決定 transaction 如何保護原始資料。SQLite 預設 rollback journal 對簡單場景合理；WAL mode 則讓 reader 可以在 writer append WAL 時保有 snapshot，適合多 reader、短寫入、互動式 workload。</p>
<table>
  <thead>
      <tr>
          <th>模式</th>
          <th>適合情境</th>
          <th>注意事項</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><code>DELETE</code></td>
          <td>最簡單、低併發、短生命週期檔案</td>
          <td>write / read 衝突較明顯</td>
      </tr>
      <tr>
          <td><code>WAL</code></td>
          <td>read-heavy、local app、小型 API</td>
          <td>需要治理 <code>-wal</code>、<code>-shm</code>、checkpoint</td>
      </tr>
      <tr>
          <td><code>MEMORY</code></td>
          <td>暫存測試、可丟資料</td>
          <td>crash 後 recovery 風險高</td>
      </tr>
      <tr>
          <td><code>OFF</code></td>
          <td>可重建資料、一次性 bulk load</td>
          <td>production formal state 應避開</td>
      </tr>
  </tbody>
</table>
<p>WAL mode 是多數 production-like SQLite 的 baseline，但它也引入 sidecar file 與 checkpoint 責任。完整判讀見 <a href="/blog/backend/01-database/vendors/sqlite/wal-concurrency-locking/" data-link-title="SQLite WAL Concurrency and Locking" data-link-desc="SQLite WAL mode 如何降低 reader / writer 衝突、保留 single writer boundary，並用 SQLITE_BUSY、WAL growth、checkpoint 訊號判斷 production 上限">WAL concurrency / locking</a>。</p>
<h2 id="synchronouscommit-latency-與資料損失窗口"><code>synchronous</code>：commit latency 與資料損失窗口</h2>
<p><code>synchronous</code> 的核心責任是控制 SQLite 在關鍵時刻要求 storage flush 的強度。官方 PRAGMA 文件說明 WAL mode 下 <code>NORMAL</code> 會把 sync 主要放在 checkpoint 路徑；這通常讓 commit 更快，但 crash durability 的語意要由 service owner 接受。</p>
<table>
  <thead>
      <tr>
          <th>設定</th>
          <th>服務語意</th>
          <th>適合情境</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><code>FULL</code></td>
          <td>更保守的 durability</td>
          <td>金錢、ledger、不可重建 local state</td>
      </tr>
      <tr>
          <td><code>NORMAL</code></td>
          <td>多數 WAL production-like baseline</td>
          <td>local app、小型服務、可接受極小 crash window</td>
      </tr>
      <tr>
          <td><code>OFF</code></td>
          <td>追求速度，放棄重要 durability</td>
          <td>scratch DB、可重建 cache、bulk import staging</td>
      </tr>
  </tbody>
</table>
<p><code>synchronous=OFF</code> 要被視為明確風險接受。若資料是 <a href="/blog/backend/knowledge-cards/source-of-truth/" data-link-title="Source of Truth" data-link-desc="說明正式資料來源如何決定資料判斷、修復與一致性責任">source of truth</a>，設定檔、runbook 與 review 都應避免把 staging 的快速設定帶進 production。</p>
<h2 id="cachemmap-與-memory-pressure">Cache、mmap 與 memory pressure</h2>
<p>SQLite memory tuning 的核心責任是降低 read path I/O，同時避免把 device / container memory 壓到不可控。<code>cache_size</code> 控制 SQLite page cache；<code>mmap_size</code> 讓讀取可透過 memory-mapped I/O 加速，但仍受平台、檔案大小與 memory budget 影響。</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="ln">1</span><span class="cl"><span class="n">PRAGMA</span><span class="w"> </span><span class="n">cache_size</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="o">-</span><span class="mi">64000</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="w"></span><span class="n">PRAGMA</span><span class="w"> </span><span class="n">mmap_size</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">268435456</span><span class="p">;</span></span></span></code></pre></div><table>
  <thead>
      <tr>
          <th>設定</th>
          <th>改善目標</th>
          <th>觀測訊號</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><code>cache_size</code></td>
          <td>減少重複 page read</td>
          <td>query latency、disk read、memory usage</td>
      </tr>
      <tr>
          <td><code>mmap_size</code></td>
          <td>降低 read syscall cost</td>
          <td>p95 / p99 read latency、address space</td>
      </tr>
      <tr>
          <td><code>temp_store</code></td>
          <td>控制 temp table 位置</td>
          <td>sort / join query latency、memory pressure</td>
      </tr>
  </tbody>
</table>
<p>Memory 設定要和 workload size 一起看。Desktop app、mobile app、edge worker、container service 的 memory ceiling 不同；把 server 上的設定複製到 mobile 或 edge runtime 會讓風險轉移到 OOM 或 OS reclaim。</p>
<h2 id="vacuum-與檔案大小治理">Vacuum 與檔案大小治理</h2>
<p>Vacuum 設定的核心責任是控制 delete 後的空間回收。SQLite delete row 後，database file 不會自然縮小；<code>auto_vacuum</code> 要在 database 建立早期決定，後續切換通常需要 <code>VACUUM</code> 重整整個 database。</p>
<table>
  <thead>
      <tr>
          <th>設定 / 操作</th>
          <th>適合情境</th>
          <th>風險 / 成本</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><code>auto_vacuum=NONE</code></td>
          <td>資料量穩定、delete 少</td>
          <td>檔案可能長期保持高水位</td>
      </tr>
      <tr>
          <td><code>auto_vacuum=INCREMENTAL</code></td>
          <td>需要逐步回收空間</td>
          <td>需要排程 <code>incremental_vacuum</code></td>
      </tr>
      <tr>
          <td><code>VACUUM</code></td>
          <td>maintenance window、重整資料庫</td>
          <td>需要額外空間與 I/O，可能影響服務</td>
      </tr>
      <tr>
          <td><code>VACUUM INTO</code></td>
          <td>compact copy / backup</td>
          <td>產出新檔，適合 restore drill 或 export</td>
      </tr>
  </tbody>
</table>
<p>檔案大小治理要接到 backup 成本。Database file 長期膨脹會放大備份時間、restore 時間與 edge deploy artifact size；若服務有大量 delete / churn，vacuum policy 要被寫進 runbook。</p>
<h2 id="production-踩雷">Production 踩雷</h2>
<h3 id="case-1pragma-只在某個-connection-設定">Case 1：PRAGMA 只在某個 connection 設定</h3>
<p>Connection-level PRAGMA 的核心風險是不同程式路徑行為不一致。Application 啟動時設了 <code>foreign_keys=ON</code>，migration tool 或 test runner 沒設，就會出現 production / migration / test 三種語意。</p>
<p>修正方向是把 baseline PRAGMA 放進 shared DB open path，並在 startup health check 印出設定值。Migration CLI、background worker、test fixture 都要共用同一份 connection initialization。</p>
<h3 id="case-2synchronousoff-從測試環境流到正式資料">Case 2：<code>synchronous=OFF</code> 從測試環境流到正式資料</h3>
<p>快速測試設定外流的核心風險是資料損失只在 crash 後出現。平常 query 都正常，直到 power loss、container kill 或 host crash 後，資料庫出現落差。</p>
<p>修正方向是設定分層。Test / benchmark 可以用 faster profile；formal state profile 要用 <code>NORMAL</code> 或 <code>FULL</code>，並要求 restore drill。</p>
<h3 id="case-3wal-growth-被誤判成資料成長">Case 3：WAL growth 被誤判成資料成長</h3>
<p>WAL growth 的核心風險是 checkpoint 問題被當成容量問題。Disk alert 看到 <code>db-wal</code> 變大，若只擴 disk，長 reader 或 checkpoint starvation 仍會持續。</p>
<p>修正方向是把 WAL size、checkpoint return 與 long reader 一起看。先找 reader lifecycle，再調 checkpoint cadence。</p>
<h3 id="case-4vacuum-在高峰期執行">Case 4：Vacuum 在高峰期執行</h3>
<p>Vacuum 的核心風險是把 maintenance I/O 放到使用者路徑。檔案縮小是好事，但 full vacuum 會消耗 I/O 與時間，對 mobile / desktop / small backend 都可能造成卡頓。</p>
<p>修正方向是把 vacuum 當 maintenance job。大檔案用 <code>incremental_vacuum</code> 或低流量窗口；備份前的 compact copy 可考慮 <code>VACUUM INTO</code>。</p>
<h2 id="操作檢查清單">操作檢查清單</h2>
<p>SQLite PRAGMA runbook 至少要記錄：</p>
<ol>
<li>所有 connection 初始化時執行的 baseline PRAGMA。</li>
<li><code>journal_mode</code> 實際回傳值與 sidecar file 位置。</li>
<li><code>synchronous</code> profile 與資料風險接受者。</li>
<li><code>busy_timeout</code> 值、busy wait metric、timeout threshold。</li>
<li><code>wal_autocheckpoint</code>、manual checkpoint cadence 與 WAL size alert。</li>
<li><code>cache_size</code> / <code>mmap_size</code> 對 memory budget 的影響。</li>
<li><code>auto_vacuum</code> / <code>VACUUM</code> / <code>VACUUM INTO</code> 的 maintenance window。</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>前置：<a href="/blog/backend/01-database/vendors/sqlite/wal-concurrency-locking/" data-link-title="SQLite WAL Concurrency and Locking" data-link-desc="SQLite WAL mode 如何降低 reader / writer 衝突、保留 single writer boundary，並用 SQLITE_BUSY、WAL growth、checkpoint 訊號判斷 production 上限">WAL concurrency / locking</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/01-database/vendors/sqlite/observability-runbook/" data-link-title="SQLite Observability and Runbook" data-link-desc="SQLite production runbook、backup evidence、WAL growth、busy errors、disk usage、restore drill 與 incident route">Observability / runbook</a></li>
<li>官方：<a href="https://www.sqlite.org/pragma.html">SQLite PRAGMA</a>、<a href="https://www.sqlite.org/lang_vacuum.html">SQLite VACUUM</a>、<a href="https://www.sqlite.org/wal.html">SQLite WAL</a></li>
</ul>
]]></content:encoded></item></channel></rss>