因為需求碰觸到 SQLite,才知道他的好與強。試著整理一下。
- SQLite 是一個輕量級且遵守 ACID 的關聯式資料庫管理系統,主要特點如下:
- 嵌入式架構:
- 免設定與單一檔案:
- 標準 SQL 支援:
- 應用廣泛:
- ACID 是什麼?
- 原子性(Atomicity):
- 一致性(Consistency):
- 隔離性(Isolation):
- 耐久性(Durability):
- SQLite 執行時,會有以下三個檔案:xxxx.sqlite,xxxx.sqlite-shm,xxxx.sqlite-wal
- xxxx.sqlite(主資料庫檔案):
- xxxx.sqlite-wal(預寫式日誌檔):
- xxxx.sqlite-shm(共享記憶體索引檔):
- 如何備份資料庫
- 方法一:
- 方法二:
- 手動觸發 Checkpoint:
- 複製三個檔案(即使 WAL 已經歸零,連同複製最保險):
- 方法三:
- 自動化排程
- 觀察資料檔案,發現三個檔案(xxxx.sqlite,xxxx.sqlite-shm,xxxx.sqlite-wal)時間戳印分別不同,是否代表資料還沒真正寫入資料庫?
- 資料已經安全寫入磁碟
- 主資料庫檔案時間停在 (例:11:03) 的原因
- 何時會同步回主檔案?
- WAL 頁面達到門檻:預設通常為 1000 個頁面(約 4MB 左右,正好接近目前的檔案大小)。
- 最後一個連線關閉:當所有開啟該資料庫的連線完全關閉時,SQLite 會嘗試自動執行 Checkpoint 並清空/重置 WAL。
- 如何復原 SQLite 資料庫
- 停止相關服務:
- 清理現有檔案:
- 放入備份檔案:
- 設定權限與驗證:
它不是獨立運行的伺服器/客戶端程序,而是直接包裝在小巧的 C 程式庫中,嵌入到應用程式內運行。
整個資料庫儲存在單一磁碟檔案中,不需繁瑣的安裝、配置或管理伺服器程序。
使用標準結構式查詢語言(SQL)以表格形式儲存及查詢資料,有效避免純檔案(如 CSV)資料重複與不同步的問題。
常用於瀏覽器歷程紀錄、行動裝置 App、小型網站或現代 SaaS 專案等場景。
ACID 是指資料庫管理系統(DBMS)在寫入或更新資料時,為確保交易(Transaction)正確可靠所必須具備的四個核心特性:
交易中的所有操作視為單一整體,要麼全部成功完成,要麼全部失敗回滾,不允許停留於中間狀態。
交易完成前後,資料庫必須始終符合預設的完整性限制與規則。
多個並行交易同時執行時彼此互不干擾,避免資料讀寫衝突。
交易一旦成功提交,其對資料的修改即永久保存,即使系統崩潰也不會遺失。
ACID 常見於關聯式資料庫,部分 NoSQL 資料庫也能遵循此規則。
這三個檔案代表 SQLite 在開啟 WAL(Write-Ahead Logging,預寫式日誌)模式下運作時產生的結構:
儲存資料表、綱要與索引的主要磁碟檔案。
寫入新變更時會優先追加記錄於此檔,讓讀取與寫入可並行運作而不互斥,提升多工效能與日誌回滾能力。日誌資料會在執行檢查點(Checkpoint)時合併回主檔案。
當多個連線存取資料庫時產生,作為 WAL 檔案的快速索引與協調共享記憶體之用。
在 SQLite 開啟 WAL 模式的情況下,若直接用 cp 複製單一主檔案(xxxx.sqlite),會遺失 WAL 檔內的最新資料;若直接 cp 正在讀寫中的三個檔案,也可能遇到資料寫入到一半不一致的風險。
有三種標準且安全的備份方式,可依需求選擇:
使用 SQLite CLI 的 .backup 指令(最推薦、最安全)。
SQLite 命令列工具內建的 .backup 會在底層調用專門的備份 API,能在不停止服務(線上熱備份)、保證事務一致性的情況下,直接產出一份已整合 WAL 資料的單一完整 .sqlite 檔案。
此法優點:不需停機、不鎖死讀寫、備份出來的檔案直接是乾淨完整的主檔案(不會有 -wal 和 -shm)。
# 備份到指定路徑,產生乾淨的單一檔案
sqlite3 /path/to/xxxx.sqlite ".backup '/path/to/backup/xxxx_$(date +%Y%m%d_%H%M%S).sqlite'"
在應用程式內執行 Checkpoint 後再複製。
如果希望直接使用檔案系統工具(如 cp、rsync 或定期快照),建議先強制讓 SQLite 將 WAL 檔案的內容寫回主檔案:
TRUNCATE 會把 WAL 內容完全合併回 homework.sqlite,並將 homework.sqlite-wal 截斷歸零(大小變為 0)。
sqlite3 /path/to/homework.sqlite "PRAGMA wal_checkpoint(TRUNCATE);"
cp /path/to/homework.sqlite* /path/to/backup/
在 Python (Flask / Web 後端) 程式碼中執行熱備份。
如果想在系統管理介面加上「下載備份」按鈕,或透過後端排程定時備份,可以使用 Python 原生 sqlite3 模組的 backup() API:
import sqlite3
import datetime
def backup_db(source_db_path, backup_dir):
timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
target_db_path = f"{backup_dir}/xxxx_backup_{timestamp}.sqlite"
# 連線至來源資料庫與備份目標
src_conn = sqlite3.connect(source_db_path)
dst_conn = sqlite3.connect(target_db_path)
# 執行熱備份(執行時依然支援並行讀寫,且保證資料一致性)
with dst_conn:
src_conn.backup(dst_conn)
dst_conn.close()
src_conn.close()
print(f"備份完成:{target_db_path}")
# 呼叫範例
backup_db('/path/to/xxxx.sqlite', '/path/to/backup')
若要每天自動定時備份(例如每天凌晨 2:00),將以下內容寫入 /etc/crontab 檔案:
0 2 * * * (account) sqlite3 /path/to/xxxx.sqlite ".backup '/backup/dir/xxxx_$(date +\%Y\%m\%d).sqlite'"
這不代表資料沒有寫入資料庫,資料已經確實、安全地被持久化了。
在 SQLite 的 WAL(Write-Ahead Logging)模式下,資料寫入的生命週期與傳統模式不同:
當應用程式執行 COMMIT 且事務成功時,SQLite 會將最新資料與變更完整寫入 xxxx.sqlite-wal 檔案,並呼叫系統的 fsync() 確保落盤。
只要 WAL 檔案更新(例:17:06),就代表資料庫的 ACID 特性(特別是耐久性 Durability)已獲得保證。
任何連線此時發起查詢,SQLite 內部會自動合併主檔案與 WAL 檔案的內容,讀取到的絕對是(例:17:06)的最新資料。
主資料庫檔案(xxxx.sqlite)只有在觸發 Checkpoint(檢查點)機制時,SQLite 才會將 WAL 檔中的變更同步回主檔。在 Checkpoint 發生之前:「主檔案的時間戳印與大小會保持不變」以及「所有的增修都在 WAL 檔案中累加」。
SQLite 預設會在以下時機自動執行 Checkpoint:
備份注意事項
如果要冷備份資料庫,切勿只複製 xxxx.sqlite,否則會遺失(例: 11:03 到 17:06 )之間的所有資料。備份時必須將 .sqlite、.sqlite-wal、.sqlite-shm 三個檔案一起複製,或者使用 SQLite 內建的備份 API / .backup 指令。
復原 SQLite 資料庫時,為了確保資料完整並避免寫入衝突,請依序執行以下步驟:
先關閉所有正在存取該 SQLite 的應用程式或容器,避免檔案遭鎖定或在讀寫中產生毀損。
將目標目錄現存的主檔(.sqlite)以及暫存記錄檔(如 -wal 和 -shm)移出或移除,切勿只覆蓋主檔而殘留舊的 WAL 檔案。
將備份的 .sqlite 檔案複製至目標資料庫目錄路徑中。
確認服務執行帳戶具備讀寫權限,並透過 CLI 執行 PRAGMA integrity_check; 確認結構無誤後再重啟服務。