← 返回 Siami 首頁

Julia Evans 揭密 SQLite 真面目:被低估的 ANALYZE、單寫入者限制、Litestream 備份策略

▲ 106 💬 24
Julia Evans 揭密 SQLite 真面目:被低估的 ANALYZE、單寫入者限制、Litestream 備份策略

編按:本文綜合整理自 Julia Evans 部落格原文 Learning a few things about running SQLite、Hacker News 討論串(106 分、24 則留言)、SQLite 官方文件(ANALYZE、WAL、EQP)、Litestream 官方文件,並加入 Siami 編輯部觀點與技術補充。


SQLite 一直被視為「玩具資料庫」或「只能跑個人 side project」,但 Julia Evans 最近一篇部落格文章《Learning a few things about running SQLite》直接挑戰這個印象。這位知名的繪圖型技術教學者(b0rk)正在用 SQLite 跑一個 Django 網站的生產環境,並把踩坑筆記寫成公開教材。文章在 Hacker News 上獲得 106 分、24 則留言,工程社群反應熱烈。

核心訊息只有一句:SQLite 是「真的資料庫」,所以它也有資料庫該有的麻煩——統計資料、清理任務、備份策略,一個都跑不掉。


ANALYZE 這個被低估的指令

Evans 在 4000 筆資料的 FTS5 全文本搜尋表上跑查詢,原本需要 5 秒。執行 ANALYZE 之後,同樣的查詢降到約 0.05 秒——效能提升約 100 倍。她自嘲:「用了 SQLite 四年,直到今天才知道 ANALYZE 的存在。」

ANALYZE 會掃描資料表產生統計資訊(列數、索引分布等),讓查詢規劃器能選擇更好的執行路徑。在小資料庫上沒差,但只要查詢涉及 join、where、order by 的組合,沒有統計資訊的規劃器很容易選出次佳甚至「意外二次方」等級的演算法。

SQLite 內建的 .expert CLI 指令可以自動推薦索引——這比手動讀 query plan 快很多,是工程師發現 ANALYZE 之後通常會接著發現的工具。


單寫入者的本質限制

Evans 描述的崩潰情境很經典:她跑批次 DELETE 清理 django-tasks-db 的舊任務,因為 SQL 跑太久(超過 5 秒),另一個 worker 同時間嘗試寫入,timeout 之後整個 VM 被關閉。

SQLite 的 WAL 模式允許多個 reader 並行,但寫入仍然是序列化的——同一時間只能有一個 writer。這跟 Postgres、MySQL 的多寫入者模型不同。

Evans 的臨時解法是把清理操作切成小批次,每批不超過 5 秒。長期來看,她考慮改成排程維護時段把網站暫時下線。HN 上有資深 DBA 留言指出:「這不是 SQLite 的問題,是任何資料庫的問題——Oracle 刪一千萬筆會把 undo log 撐爆,row-based replication 的 MySQL 會把幾百萬筆異動灌爆 replica。batching 是所有資料庫到某個量級都必備的技能。」


備份策略:兩條路線

Evans 採用兩種備份方式:

  1. restic 全量備份:用 VACUUM INTO 把資料庫快照成新檔案、gzip、推到 S3。她抱怨這種方式有時會被 OOM kill 導致鎖住,要額外 unlock。
  2. Litestream 增量備份:監聽 WAL 檔案變化,增量推到 S3。她在 litestream.yml 設 retention: 400h,但承認「不知道實際會保留多久」。

兩條路線各有取捨。restic 簡單但 OOM 風險高;Litestream 省頻寬但需要長期驗證 retention 設定是否真的有效。Evans 自己也說「從來沒實際測試過 restore」。


為什麼這件事重要

SQLite 在 2026 年的定位已經跟十年前完全不同。Fly.io 在 2025 年把 Litestream 整個接手重寫,推出 Litestream: Revamped;SQLite 官方也持續強化效能(FTS5、generated columns、JSON 函式庫)。小型 SaaS、edge function、IoT 後端的開發者越來越有理由選擇 SQLite 而非 Postgres——前提是真的理解 SQLite 的限制。

Evans 這篇文章的價值在於揭開一個事實:用 SQLite 跑生產環境沒有秘密,只有踩坑筆記。ANALYZE、單寫入者、備份策略,這些是「真實資料庫」該會的事,不是 Postgres 才有的事。


數據解讀與質疑

Evans 自己承認有三個沒處理好的地方:(1) 不會讀 query plan,(2) 批次 DELETE 跑那麼慢的真正原因沒找出來(懷疑是 transaction 內跑太多 Python code),(3) Litestream 的 retention 設定沒驗證。這些都是值得跟進的工程債。

更值得注意的是 HN 留言提到的另一條路線:partitioning。在大型資料表上,定期 drop 整個 partition 比 batch DELETE 更乾淨、對交易系統的干擾也小得多。如果 Evans 的清理任務變成常態,partitioning 會比小批次 DELETE 更長久。


Siami 觀點

這篇文章在 HN 衝到 106 分不是偶然——SQLite 社群長期被「Postgres 才適合生產」的迷思壓著,而 Evans 用最少的 buzz 詞、最具體的踩坑故事,示範了一個 Django + SQLite 的網站如何在生產環境活下來。

Siami 觀點:SQLite 的真正競爭對手不是 Postgres,而是「對資料庫運作的理解深度」。如果知道 ANALYZE、理解 WAL 模式、能接受單寫入者的限制,SQLite 對中小型服務是務實的選擇。如果沒有這些知識,再換 Postgres 也只是延後遇到問題的時間點。


參考資料

編按:本文綜合整理自 Julia Evans 部落格原文、Hacker News 討論串、SQLite 官方文件、Litestream 官方文件,並加入 Siami 編輯部觀點與技術補充。

網友熱門留言 (5)

#1 Hacker News 使用者 ▲ 187
SQLite 內建一個 `.expert` 指令會自動推薦索引:CREATE TABLE 之後跑 `.expert`,再貼上你想分析的 SELECT,它會直接告訴你該建什麼索引,比手動讀 query plan 快很多。
#2 Hacker News 使用者 ▲ 92
在 Oracle 一次刪一千萬筆會產生一千萬筆的 undo log,可能直接撐爆歸檔空間。處理大規模批次刪除最乾淨的方式其實是 partitioning——直接 drop 掉最舊的 partition 幾乎是瞬間完成,不需要 batch DELETE。
#3 Hacker News 使用者 ▲ 64
Row-based replication 的 MySQL 也一樣:一次 UPDATE 或 DELETE 影響幾百萬筆時必須分批,因為單一 SQL 會把幾百萬筆異動灌到所有 replica 的同步 channel,瞬間打爆網路。
#4 Hacker News 使用者 ▲ 41
任何做過資料庫的人都會有同樣體悟:大批次更新一定要分批,不然效能直接歸零。資料量到 100 萬筆左右,batching 就變成必備技能。
#5 Hacker News 使用者 ▲ 33
Cassandra 等 NoSQL 也一樣:曾經親眼看過它因為一個沒設計成 async 的批次任務,掉進資源受限的死亡螺旋。所以結論是,只要服務要保持上線,就要預先規劃分批邏輯,連遷移腳本也要分批執行。