SQLite 完全指南

零設定、嵌入式、單檔案的關聯式資料庫,把整個資料庫塞進一個 .db 檔,無需獨立伺服器程序。


目錄


什麼是 SQLite?

SQLite 是一個 嵌入式(embedded)關聯式資料庫引擎。它最特別的地方在於:它不是一個「伺服器」,而是一個 C 語言函式庫,直接被編譯進你的應用程式裡,整個資料庫就是磁碟上的 一個檔案

一句話理解

MySQL / PostgreSQL = 一台「資料庫伺服器」,你的程式透過網路連線去問它
SQLite             = 一個「函式庫」,你的程式直接讀寫一個 .db 檔案

它沒有 daemon、沒有 port、沒有帳號密碼、不需要安裝服務。fopen() 開檔案多簡單,SQLite 用起來就多簡單。

為什麼它無所不在?

SQLite 是世界上 部署量最大 的資料庫引擎(沒有之一),因為它藏在:

  • 每一支 Android / iOS 手機(App 的本地儲存)
  • 每一個瀏覽器(Chrome、Firefox 的歷史紀錄、Cookie)
  • macOS、Windows 的系統元件
  • Python(內建 sqlite3 模組)、各種桌面軟體的設定檔
  • 飛機的黑盒子、IoT 裝置、車載系統

核心定位

SQLite = 嵌入式 + 零設定 + 單檔案 + 完整 SQL + 公領域授權
  • 不是 用來取代 MySQL/PostgreSQL 的「大型應用後端資料庫」
  • 而是 取代 fopen():當你需要結構化儲存、查詢、交易時的「應用程式內建資料層」

官方自己的定位:「SQLite 不跟 client/server 資料庫競爭,它跟 fopen() 競爭。」


核心特性

1. Serverless(無伺服器)

沒有獨立的伺服器程序。資料庫的讀寫邏輯全部在 應用程式的行程內 執行,直接對檔案做系統呼叫。

傳統資料庫:App 程序 ──網路/Socket──► DB 伺服器程序 ──► 磁碟
SQLite:    App 程序 ──函式呼叫──► SQLite 函式庫 ──► 磁碟檔案

2. Zero-Configuration(零設定)

不用安裝、不用啟動服務、不用設定檔、不用建立使用者帳號。下載一個檔案就能用。

3. Single-File(單檔案)

整個資料庫——所有資料表、索引、觸發器、view——都在 一個檔案 裡(通常是 .db / .sqlite)。要備份就複製這個檔案,要搬移就 scp 這個檔案。

4. Self-Contained(自包含)

不依賴任何外部函式庫,整個引擎約幾百 KB,可以靜態編譯進任何程式。

5. Cross-Platform(跨平台檔案格式)

.db 檔案格式跨平台相容:在 macOS 寫的檔案,搬到 Windows、Linux、ARM、x86 都能直接讀。大端序/小端序、32/64 位元都無痛。

6. Public Domain(公領域)

授權是 public domain,商用、修改、散布完全免費,無任何限制。

7. 完整的 SQL 支援

支援交易(ACID)、索引、view、trigger、外鍵、CTE、視窗函數(window functions)、全文檢索(FTS5)、JSON 函數等現代 SQL 功能。


架構與運作原理

嵌入式 vs Client-Server 架構

這是理解 SQLite 一切特性的根源。

┌─────────────────── Client-Server(MySQL/PostgreSQL)──────────────────┐
│                                                                      │
│   App 1 ─┐                                                           │
│   App 2 ─┼──TCP──► [DB Server 程序] ──► 資料檔案                       │
│   App 3 ─┘            ↑ 獨立程序、管理連線池、權限、並發                  │
│                                                                      │
└──────────────────────────────────────────────────────────────────────┘

┌─────────────────────────── SQLite(嵌入式)────────────────────────────┐
│                                                                      │
│   ┌─ App 程序 ──────────────────┐                                     │
│   │  你的程式碼                  │                                     │
│   │    ↓ 函式呼叫               │                                      │
│   │  SQLite 函式庫(同一程序)    │ ──► database.db(磁碟檔案)           │
│   └────────────────────────────┘                                     │
│                                                                      │
└──────────────────────────────────────────────────────────────────────┘

關鍵差異:SQLite 沒有「伺服器程序」這個角色。SQL 的解析、最佳化、執行,全部發生在你呼叫 SQLite 函式的那個行程裡。

SQLite 內部分層

SQL 字串
① Tokenizer(分詞)
② Parser(語法解析 → 語法樹)
③ Code Generator(產生 bytecode)
④ VDBE 虛擬機(執行 bytecode,這是 SQLite 的核心)
⑤ B-Tree 模組(資料/索引以 B-tree 組織)
⑥ Pager(分頁快取、交易、鎖定、journal)
⑦ OS Interface(VFS,把分頁讀寫對應到實際檔案系統呼叫)
  • VDBE(Virtual Database Engine):SQLite 把 SQL 編譯成自訂 bytecode,再用虛擬機執行。這讓查詢計畫可預測、可用 EXPLAIN 檢視。
  • Pager:負責把資料庫檔案切成固定大小的 page(預設 4096 bytes),做快取、處理交易與 rollback、實作鎖定。這是並發與耐久性的核心。
  • VFS(Virtual File System):抽象層,讓 SQLite 可以跑在不同 OS、甚至記憶體、加密檔案系統上。

連線機制

這是 SQLite 和傳統資料庫差最多、也最容易誤解的地方。

「連線」其實是「開檔案」

在 MySQL 你會寫 mysql://user:pass@host:3306/dbname——有主機、port、帳號。SQLite 的「連線字串」就是 一個檔案路徑

import sqlite3
conn = sqlite3.connect("app.db")        # 開啟(或建立)這個檔案
conn = sqlite3.connect("/data/app.db")  # 絕對路徑
conn = sqlite3.connect(":memory:")      # 純記憶體資料庫,程式結束就消失

「建立連線」實際上是 SQLite 開啟那個檔案、讀取檔頭、準備好 page cache。沒有網路、沒有握手、沒有認證——權限完全由 作業系統的檔案權限 決定(誰能讀寫這個檔案,誰就能讀寫這個資料庫)。

沒有網路存取(這是設計,不是缺陷)

SQLite 不提供 透過網路遠端連線的機制。要讓多台機器共用,唯一的方式是共享底層檔案系統(如 NFS),但官方 強烈不建議——網路檔案系統的鎖定(locking)實作常有 bug,容易造成資料庫損毀。

結論:SQLite 是給 「資料和程式在同一台機器」 的場景用的。需要跨機器存取 → 用 client-server 資料庫。

鎖定模型(Locking):理解並發的關鍵

因為沒有伺服器來協調,SQLite 用 檔案層級的鎖 來保證一致性。經典模式(rollback journal)的鎖狀態:

UNLOCKED → SHARED(多個讀取者可同時持有)
         RESERVED(我準備要寫,但讀取者還能讀)
         PENDING(不准新的讀取者進來,等現有讀取者離開)
         EXCLUSIVE(獨佔,開始寫入;此時無人能讀寫)

核心規則:「多個讀取者可以並存,但寫入是整個資料庫獨佔的。」 同一時間只能有 一個寫入者,而且寫入當下會擋住所有讀取。這就是為什麼 SQLite 在「高並發寫入」場景會撞牆,也是著名錯誤 database is locked 的來源。

兩種日誌模式:Rollback Journal vs WAL

SQLite 怎麼保證「交易到一半當機,資料不會壞」?靠日誌。有兩種機制:

Rollback Journal(預設、傳統)

寫入前:先把「原始 page」備份到 -journal 檔
修改主資料庫檔案
成功 → 刪掉 journal(commit)
當機 → 下次開檔時用 journal 把舊資料復原(rollback)

缺點:寫入時會獨佔,讀取者完全被擋住。

WAL(Write-Ahead Logging,強烈推薦開啟)

寫入時:新資料先寫到 -wal 檔(不動主檔)
讀取者:繼續讀主檔的舊版本(不被擋)
之後再把 -wal 的內容「checkpoint」回主檔
conn.execute("PRAGMA journal_mode=WAL")

WAL 最大的好處:讀取和寫入可以同時進行(讀不擋寫、寫不擋讀)。對 Web 應用、桌面 App 的並發體驗是質變等級的改善。代價是多了 -wal-shm 兩個附屬檔案,且仍然 只能有一個寫入者

連線的附屬檔案

開啟一個 WAL 模式的 app.db,你會在磁碟上看到:

app.db        ← 主資料庫
app.db-wal    ← Write-Ahead Log
app.db-shm    ← 共享記憶體索引(協調多個連線)

備份時要注意:不能只複製主檔,否則可能漏掉 WAL 裡還沒 checkpoint 的資料。正確做法是用 VACUUM INTO.backup 指令。

程式一直開關連線,SQLite 真的一直開開關關嗎?(連線池)

常見現象:應用程式碼每次請求都 connect() / close(),但底層的 SQLite 連線其實沒有真的一直開關。 這要分成「兩個層次」才講得清楚,否則會混淆:

層次一:SQLite 原生的 open / close —— 其實會開關,只是「便宜」

呼叫 sqlite3_open() / sqlite3_close(),SQLite 確實 做了實體的開啟與關閉。但因為它沒有伺服器、沒有網路握手、沒有認證流程,「開一個連線」本質上只是:

開檔案(open syscall)→ 讀檔頭 → 準備 page cache

所以成本很低——大約比「重用既有連線直接執行」慢 一個數量級,但最壞也只是 毫秒級。這跟 MySQL 開連線要走 TCP 三次握手 + 認證完全不是同個量級。

所以:SQLite 原生層「開關連線很便宜」是真的,但「不會一直開關」不是 SQLite 自己做的——是下面這層做的。

層次二:連線池(Connection Pool)—— 真正讓連線不重開的機制

「程式一直開關、但 SQLite 不會一直開關」的現象,幾乎都是驅動 / 框架層的連線池在做的,不是 SQLite 引擎本身:

你的程式碼              連線池                  SQLite 實體連線
─────────              ─────                  ──────────────
conn = connect()  ──►  借出一個既有連線    ──►  (早就開好,重用,不重開)
...執行 SQL...
conn.close()      ──►  歸還到池子          ──►  (沒有真的 close,留著下次用)
                       (reset:rollback 清掉交易狀態)

關鍵:應用程式的 open / close 只是「向池子借出 / 歸還」,對應到的實體 SQLite 連線一直活著、被重複使用。常見實作:

環境 連線池機制
Go database/sql*sql.DB 本身就是一個連線池,不是單一連線
Python SQLAlchemy 檔案 DB 預設用 QueuePool:memory:SingletonThreadPool(每執行緒一條)
Java HikariCP 等 JDBC 連線池保留實體連線
不想池化時 SQLAlchemy 的 NullPool 就會「每次真的開、真的關」

連線歸還時,池子通常會做 「reset on return」——對連線執行一次 rollback(),清掉殘留的交易狀態與鎖,確保下一個借用者拿到乾淨的連線。

層次三(加碼):作業系統的檔案快取

就算實體連線真的被關掉,作業系統的 page cache 仍會把 .db 檔的分頁留在記憶體裡。所以下次重開時,讀取常常直接命中 OS 快取,不必真的碰磁碟。這是「重複開關感覺不慢」的另一層原因(但這是 OS 的功勞,不是 SQLite 或連線池)。

實務建議

  • SQLite 因為連線便宜,最佳模式通常是「每執行緒保留一條長壽命連線」,而不是每個查詢開關一次——這樣 page cache 保持溫熱,也能持續享受 WAL 的好處。
  • 用連線池時要記得 SQLite 是單寫入者:寫入用的池子開很大沒有意義,反而可能加劇鎖競爭。讀取池可以大,寫入實際上會被序列化。

資料型別系統(重要陷阱)

SQLite 一個一定要知道的「個性」。

SQLite 用的是 動態型別(dynamic typing),跟幾乎所有其他 SQL 資料庫都不一樣。

型別親和性(Type Affinity)

在 MySQL,欄位宣告 INT 就只能存整數。在 SQLite,型別是綁在「值」上,而不是綁在「欄位」上。欄位宣告的型別只是一個「建議的親和性」,你其實可以往 INTEGER 欄位塞字串:

CREATE TABLE t (id INTEGER, name TEXT);
INSERT INTO t VALUES ('hello', 123);  -- 不會報錯!id 存了字串,name 存了數字

SQLite 只有 5 種儲存類別(storage class):

儲存類別 說明
NULL 空值
INTEGER 帶號整數(1~8 bytes)
REAL 浮點數(8 bytes)
TEXT 字串(UTF-8/16)
BLOB 二進位原始資料

STRICT 表(新版解法)

SQLite 3.37+(2021)加入 STRICT 表,讓型別檢查變嚴格,行為更接近傳統資料庫:

CREATE TABLE t (id INTEGER, name TEXT) STRICT;
INSERT INTO t VALUES ('hello', 123);  -- 這次會報錯!

建議:新專案優先用 STRICT 表,避免動態型別帶來的隱性 bug。

沒有原生的這些型別

SQLite 沒有 專屬的 BOOLEANDATEDATETIME 型別:

  • 布林 → 用 0 / 1(INTEGER)
  • 日期時間 → 存成 TEXT('2026-06-08 12:00:00')、Unix timestamp(INTEGER),或 Julian Day(REAL),搭配內建的 date() / datetime() 函數處理

交易與並發控制

ACID 完整支援

SQLite 是 完全符合 ACID 的(Atomicity、Consistency、Isolation、Durability)。即使作業系統當機或斷電,交易也保證原子性——靠的就是前面提到的 journal/WAL 機制。

BEGIN;
  UPDATE accounts SET balance = balance - 100 WHERE id = 1;
  UPDATE accounts SET balance = balance + 100 WHERE id = 2;
COMMIT;   -- 兩個都成功,或都不發生

並發模型總結

場景 Rollback Journal WAL
多個讀取者同時讀 ✅ 可以 ✅ 可以
讀取時可否寫入 ❌ 寫會被擋 ✅ 可以
寫入時可否讀取 ❌ 讀會被擋 ✅ 可以
多個寫入者同時寫 ❌ 不行 ❌ 不行(仍是單寫入者)

一句話:SQLite 的並發瓶頸永遠是 「同時只能一個寫入者」。讀取可以非常高並發,寫入是序列化的。

busy_timeout:對付 database is locked

當一個連線想寫、但鎖被別人佔住時,預設會立刻回 database is locked 錯誤。設定 busy timeout 讓它「等一下再試」:

conn.execute("PRAGMA busy_timeout = 5000")  # 最多等 5 秒

什麼情境適合用 SQLite?

✅ 適合的場景

場景 為什麼適合
行動 App / 桌面軟體本地儲存 嵌入式、零設定、跨平台檔案,完美
應用程式設定檔 / 狀態 比 JSON/INI 多了查詢與交易能力
網站初期 / 中小流量網站 讀多寫少時效能極佳,省去架 DB 的成本
快取 / 暫存資料 :memory: 模式或本地檔案快取
資料分析 / 資料交換格式 單檔案好搬移,SQL 比 CSV 強大太多
單元測試 / CI :memory: DB 快速建立又免清理
IoT / 嵌入式裝置 體積小、無依賴、耐用
原型開發 / MVP 馬上能跑,之後再換 client-server DB

❌ 不適合的場景

場景 為什麼不適合
高並發寫入 單寫入者瓶頸,寫入會排隊
多台機器共用同一資料庫 無網路存取,共享檔案系統不安全
超大資料量(數百 GB+) 雖支援到 281 TB,但管理與運維工具弱
需要細緻權限控制 沒有使用者/角色概念,只有檔案權限
需要熱備援 / 主從複製 / 叢集 原生不支援(需第三方方案如 Litestream)
多服務同時直連同一份資料 client-server 架構才適合

官方的判斷準則

資料和應用程式在同一台機器 + 寫入並發不高 → SQLite 是絕佳選擇。 一旦需要 跨機器存取高並發寫入 → 改用 PostgreSQL / MySQL。


SQLite vs 其他資料庫

SQLite vs MySQL / PostgreSQL

特性 SQLite MySQL / PostgreSQL
架構 嵌入式函式庫 Client-Server
伺服器程序 需獨立 daemon
安裝設定 零設定,一個檔案 需安裝、設定、啟動服務
連線方式 開啟檔案 TCP/Socket + 帳密
網路存取 ❌ 不支援 ✅ 支援
並發寫入 單寫入者 高並發(MVCC)
並發讀取 高(WAL 模式)
使用者權限 檔案權限 完整角色/權限系統
型別系統 動態型別(可選 STRICT) 靜態強型別
複製/叢集 原生不支援 內建支援
儲存上限 281 TB(理論) 視設定,通常更大且好管理
適合資料量 GB 等級最舒適 TB 等級
運維成本 幾乎為零 需 DBA / 運維

SQLite vs Redis

兩者常被一起問,但定位完全不同:

特性 SQLite Redis
資料模型 關聯式(資料表 + SQL) Key-Value + 多種資料結構
儲存位置 磁碟檔案(持久) 記憶體為主
架構 嵌入式 Client-Server
查詢能力 完整 SQL(JOIN、聚合) 簡單 key 操作
典型用途 本地結構化儲存 快取、即時、佇列

SQLite vs JSON / CSV 檔案

這才是 SQLite 真正的競爭對手(取代 fopen()):

特性 SQLite JSON / CSV 檔
查詢 SQL(索引、JOIN、聚合) 全部讀進記憶體自己過濾
部分更新 只改一筆,不動其他 通常要整檔重寫
交易/原子性 ✅ ACID ❌ 寫到一半當機就壞
並發 有鎖定保護 容易互相覆蓋
大檔案 不用全載入記憶體 通常要全部載入

快速開始

安裝

大多數系統已內建。沒有的話:

# macOS
brew install sqlite

# Ubuntu/Debian
sudo apt install sqlite3

# 驗證
sqlite3 --version

Python、PHP、Node(better-sqlite3)、Go(mattn/go-sqlite3)等多數語言已內建或有成熟套件,通常不需另外裝。

命令列第一步

sqlite3 app.db          # 開啟(不存在就建立)app.db

sqlite> CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT, age INTEGER);
sqlite> INSERT INTO users (name, age) VALUES ('Alice', 30), ('Bob', 25);
sqlite> SELECT * FROM users;
1|Alice|30
2|Bob|25

sqlite> .mode column     -- 表格化輸出
sqlite> .headers on      -- 顯示欄位名
sqlite> SELECT * FROM users;
id  name   age
--  -----  ---
1   Alice  30
2   Bob    25

sqlite> .tables          -- 列出所有資料表
sqlite> .schema users    -- 看資料表定義
sqlite> .quit            -- 離開

常用「點指令」(dot commands)

.tables           # 列出資料表
.schema [表名]    # 顯示建表 SQL
.mode column      # 輸出格式:column / csv / json / table
.headers on       # 顯示欄位標題
.import data.csv t  # 匯入 CSV 到資料表 t
.output out.csv   # 把後續查詢結果導到檔案
.backup backup.db # 安全備份(含 WAL)
.quit             # 離開

實戰範例

範例 1:Python(內建 sqlite3,無需安裝)

import sqlite3

# 連線(檔案不存在會自動建立)
conn = sqlite3.connect("app.db")
conn.execute("PRAGMA journal_mode=WAL")      # 開啟 WAL,提升並發
conn.execute("PRAGMA foreign_keys=ON")        # 預設外鍵是關的,要手動開
conn.row_factory = sqlite3.Row                # 讓查詢結果能用欄位名存取

cur = conn.cursor()
cur.execute("""
    CREATE TABLE IF NOT EXISTS users (
        id   INTEGER PRIMARY KEY,
        name TEXT NOT NULL,
        age  INTEGER
    ) STRICT
""")

# 一定要用參數化查詢,絕不要用字串拼接(防 SQL injection)
cur.execute("INSERT INTO users (name, age) VALUES (?, ?)", ("Alice", 30))
conn.commit()

# 查詢
for row in cur.execute("SELECT id, name, age FROM users WHERE age > ?", (20,)):
    print(row["id"], row["name"], row["age"])

conn.close()

範例 2:交易(轉帳,保證原子性)

import sqlite3

conn = sqlite3.connect("bank.db")
try:
    with conn:  # with 區塊:正常結束自動 commit,拋例外自動 rollback
        conn.execute("UPDATE accounts SET balance = balance - 100 WHERE id = 1")
        conn.execute("UPDATE accounts SET balance = balance + 100 WHERE id = 2")
    # 離開 with 區塊,兩筆一起 commit
except sqlite3.Error as e:
    print("交易失敗,已自動回滾:", e)
finally:
    conn.close()

範例 3:用索引加速查詢

-- 沒索引:每次查 email 都要全表掃描
SELECT * FROM users WHERE email = 'alice@example.com';

-- 建索引後變成 O(log n)
CREATE INDEX idx_users_email ON users(email);

-- 確認查詢計畫有用到索引
EXPLAIN QUERY PLAN SELECT * FROM users WHERE email = 'alice@example.com';
-- 看到 "USING INDEX idx_users_email" 就對了

範例 4:記憶體資料庫(測試 / 暫存超快)

import sqlite3

conn = sqlite3.connect(":memory:")  # 整個 DB 在記憶體,程式結束即消失
conn.execute("CREATE TABLE cache (k TEXT PRIMARY KEY, v TEXT)")
conn.execute("INSERT INTO cache VALUES ('key1', 'value1')")
print(conn.execute("SELECT v FROM cache WHERE k='key1'").fetchone())
# 非常適合單元測試:不留檔案、不用清理、速度極快

範例 5:查詢 JSON 欄位(SQLite 內建 JSON 函數)

CREATE TABLE events (id INTEGER PRIMARY KEY, payload TEXT);
INSERT INTO events (payload) VALUES ('{"user":"alice","action":"login"}');

-- 直接從 JSON 字串取欄位
SELECT json_extract(payload, '$.user') AS user FROM events;
-- 或用簡寫運算子(3.38+)
SELECT payload ->> '$.action' AS action FROM events;

最佳實踐

1. 開啟 WAL 模式(除非有特殊理由)

conn.execute("PRAGMA journal_mode=WAL")

讀寫不互相阻塞,對任何有並發的應用都是必開。

2. 設定 busy_timeout 避免立即報鎖錯

conn.execute("PRAGMA busy_timeout = 5000")  # 等 5 秒再放棄

3. 新專案用 STRICT 表

CREATE TABLE t (id INTEGER, name TEXT) STRICT;

避免動態型別塞錯資料造成的隱性 bug。

4. 永遠用參數化查詢

# ❌ 危險:SQL injection
cur.execute(f"SELECT * FROM users WHERE name = '{name}'")

# ✅ 安全
cur.execute("SELECT * FROM users WHERE name = ?", (name,))

5. 記得手動開啟外鍵約束

conn.execute("PRAGMA foreign_keys = ON")  # 基於相容性,預設是關的!

6. 批次寫入包進一個交易

# ❌ 慢:每筆 INSERT 都自動 commit(各自一次 fsync)
for row in rows:
    conn.execute("INSERT INTO t VALUES (?)", (row,))
    conn.commit()

# ✅ 快數十~數百倍:整批包成一個交易
with conn:
    conn.executemany("INSERT INTO t VALUES (?)", [(r,) for r in rows])

7. 定期整理碎片

VACUUM;            -- 重整檔案、回收空間
ANALYZE;           -- 更新統計資訊,幫助查詢最佳化器
PRAGMA optimize;   -- 連線關閉前執行,自動做必要的優化

8. 正確備份(含 WAL 內容)

# ❌ 不要只複製主檔(可能漏掉 WAL 未 checkpoint 的資料)
cp app.db backup.db

# ✅ 用內建備份指令
sqlite3 app.db ".backup backup.db"
# 或 SQL:
# VACUUM INTO 'backup.db';

常見問題

Q1:為什麼一直出現 database is locked

A:因為有另一個連線正持有寫入鎖。解法:

  1. 開啟 WAL 模式(讀寫不互鎖)
  2. 設定 PRAGMA busy_timeout
  3. 縮短交易時間,別把交易開著做一堆其他事
  4. 檢查是否忘了 commit() / close(),讓連線一直佔著鎖

Q2:SQLite 可以多人同時用嗎?

A:可以「多讀」,但「寫入永遠是單一序列」。對 讀多寫少 的應用(多數網站、App)綽綽有餘;對 高並發寫入(如大型交易系統)就不適合,該換 client-server DB。

Q3:SQLite 能撐多大?適合正式上線嗎?

A:單檔理論上限 281 TB,實務上 GB 等級非常舒適。完全可以正式上線——許多中小型網站、知名產品(如 Expensify)就直接用 SQLite。關鍵不是資料量,而是 並發寫入需求是否需要跨機器存取

Q4:SQLite 跟 MySQL,學哪個的 SQL 比較通用?

A:SQL 核心語法(SELECT/INSERT/JOIN/GROUP BY)幾乎共通,學會可互通。差異在:SQLite 動態型別、缺少部分 ALTER TABLE 功能、無 stored procedure、日期型別處理不同。學概念兩者皆可,正式環境再注意方言差異。

Q5:可以讓多台伺服器連同一個 SQLite 檔嗎?

A不建議。SQLite 沒有網路層,跨機器只能靠共享檔案系統(NFS),但網路檔案系統的鎖定常出問題,容易資料庫損毀。需要跨機器 → 用 PostgreSQL/MySQL,或看 Litestream / rqlite / Turso 這類「把 SQLite 分散式化」的方案。

Q6:.db.sqlite.sqlite3 有差嗎?

A:沒有。副檔名只是慣例,SQLite 靠檔頭(前 16 bytes 的 magic string)辨識格式,叫什麼名字都能讀。

Q7:程式每次請求都開關連線,SQLite 會一直重開嗎?

A:分兩層看。SQLite 原生的 open/close 確實會做,但很便宜(只是開檔案,沒有網路握手),毫秒級。而「不會一直開開關關」的機制,是 驅動/框架的連線池——應用程式的 close() 只是把連線歸還池子,底層實體連線被重複使用而非真的關閉(如 Go 的 database/sql、SQLAlchemy QueuePool、Java HikariCP)。詳見 連線機制 → 程式一直開關連線,SQLite 真的一直開開關關嗎?


總結

核心要點

SQLite = 嵌入式 + 零設定 + 單檔案 + 完整 SQL + ACID

本質:它不跟 MySQL 競爭,它跟 fopen() 競爭
      ── 是「應用程式內建的資料層」,不是「資料庫伺服器」

連線:開檔案,不是連網路;權限 = 檔案權限
並發:多讀可並行,寫入永遠單一序列(瓶頸在此)
型別:動態型別(建議用 STRICT 表變嚴格)

選用決策樹

資料和程式在同一台機器?
├─ 否 → 用 PostgreSQL / MySQL(需要網路存取)
└─ 是 → 寫入並發高嗎?
        ├─ 高 → 用 client-server DB
        └─ 不高 → ✅ SQLite(記得開 WAL)

三個關鍵問題

  1. 要跨機器存取嗎? → 要的話別用 SQLite
  2. 寫入並發高嗎? → 高的話別用 SQLite
  3. 以上都不是? → SQLite 是最省事、最可靠的選擇

快速參考

項目 內容
連線 sqlite3.connect("app.db") / :memory:
開 WAL PRAGMA journal_mode=WAL
防鎖等待 PRAGMA busy_timeout=5000
開外鍵 PRAGMA foreign_keys=ON
嚴格型別 CREATE TABLE t(...) STRICT
備份 .backup file.db / VACUUM INTO 'file.db'
整理 VACUUM / ANALYZE / PRAGMA optimize

記憶口訣

SQLite 三個「單」

  1. 單檔案 — 整個資料庫就一個檔
  2. 單行程 — 沒伺服器,跑在你的程式裡
  3. 單寫入 — 並發瓶頸永遠是「同時只能一個寫」

建立日期:2026-06-08 最後更新:2026-06-08

🔗相關文章