SQLite 相關筆記

因為需求碰觸到 SQLite,才知道他的好與強。試著整理一下。

  1. SQLite 是一個輕量級且遵守 ACID 的關聯式資料庫管理系統,主要特點如下:
    • 嵌入式架構:
    • 它不是獨立運行的伺服器/客戶端程序,而是直接包裝在小巧的 C 程式庫中,嵌入到應用程式內運行。

    • 免設定與單一檔案:
    • 整個資料庫儲存在單一磁碟檔案中,不需繁瑣的安裝、配置或管理伺服器程序。

    • 標準 SQL 支援:
    • 使用標準結構式查詢語言(SQL)以表格形式儲存及查詢資料,有效避免純檔案(如 CSV)資料重複與不同步的問題。

    • 應用廣泛:
    • 常用於瀏覽器歷程紀錄、行動裝置 App、小型網站或現代 SaaS 專案等場景。

  2. ACID 是什麼?
  3. ACID 是指資料庫管理系統(DBMS)在寫入或更新資料時,為確保交易(Transaction)正確可靠所必須具備的四個核心特性:

    • 原子性(Atomicity):
    • 交易中的所有操作視為單一整體,要麼全部成功完成,要麼全部失敗回滾,不允許停留於中間狀態。

    • 一致性(Consistency):
    • 交易完成前後,資料庫必須始終符合預設的完整性限制與規則。

    • 隔離性(Isolation):
    • 多個並行交易同時執行時彼此互不干擾,避免資料讀寫衝突。

    • 耐久性(Durability):
    • 交易一旦成功提交,其對資料的修改即永久保存,即使系統崩潰也不會遺失。

    ACID 常見於關聯式資料庫,部分 NoSQL 資料庫也能遵循此規則。

  4. SQLite 執行時,會有以下三個檔案:xxxx.sqlite,xxxx.sqlite-shm,xxxx.sqlite-wal
  5. 這三個檔案代表 SQLite 在開啟 WAL(Write-Ahead Logging,預寫式日誌)模式下運作時產生的結構:

    • xxxx.sqlite(主資料庫檔案):
    • 儲存資料表、綱要與索引的主要磁碟檔案。

    • xxxx.sqlite-wal(預寫式日誌檔):
    • 寫入新變更時會優先追加記錄於此檔,讓讀取與寫入可並行運作而不互斥,提升多工效能與日誌回滾能力。日誌資料會在執行檢查點(Checkpoint)時合併回主檔案。

    • xxxx.sqlite-shm(共享記憶體索引檔):
    • 當多個連線存取資料庫時產生,作為 WAL 檔案的快速索引與協調共享記憶體之用。

  6. 如何備份資料庫
  7. 在 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 檔案的內容寫回主檔案:

      • 手動觸發 Checkpoint:
      • TRUNCATE 會把 WAL 內容完全合併回 homework.sqlite,並將 homework.sqlite-wal 截斷歸零(大小變為 0)。

        
        sqlite3 /path/to/homework.sqlite "PRAGMA wal_checkpoint(TRUNCATE);"  
                        
      • 複製三個檔案(即使 WAL 已經歸零,連同複製最保險):
      • 
        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'"
      
                     
  8. 觀察資料檔案,發現三個檔案(xxxx.sqlite,xxxx.sqlite-shm,xxxx.sqlite-wal)時間戳印分別不同,是否代表資料還沒真正寫入資料庫?
  9. 這不代表資料沒有寫入資料庫,資料已經確實、安全地被持久化了。

    在 SQLite 的 WAL(Write-Ahead Logging)模式下,資料寫入的生命週期與傳統模式不同:

    • 資料已經安全寫入磁碟
    • 當應用程式執行 COMMIT 且事務成功時,SQLite 會將最新資料與變更完整寫入 xxxx.sqlite-wal 檔案,並呼叫系統的 fsync() 確保落盤。

      只要 WAL 檔案更新(例:17:06),就代表資料庫的 ACID 特性(特別是耐久性 Durability)已獲得保證。

      任何連線此時發起查詢,SQLite 內部會自動合併主檔案與 WAL 檔案的內容,讀取到的絕對是(例:17:06)的最新資料。

    • 主資料庫檔案時間停在 (例:11:03) 的原因
    • 主資料庫檔案(xxxx.sqlite)只有在觸發 Checkpoint(檢查點)機制時,SQLite 才會將 WAL 檔中的變更同步回主檔。在 Checkpoint 發生之前:「主檔案的時間戳印與大小會保持不變」以及「所有的增修都在 WAL 檔案中累加」。

    • 何時會同步回主檔案?
    • SQLite 預設會在以下時機自動執行 Checkpoint:

      • WAL 頁面達到門檻:預設通常為 1000 個頁面(約 4MB 左右,正好接近目前的檔案大小)。
      • 最後一個連線關閉:當所有開啟該資料庫的連線完全關閉時,SQLite 會嘗試自動執行 Checkpoint 並清空/重置 WAL。

       

      備份注意事項
      如果要冷備份資料庫,切勿只複製 xxxx.sqlite,否則會遺失(例: 11:03 到 17:06 )之間的所有資料。備份時必須將 .sqlite、.sqlite-wal、.sqlite-shm 三個檔案一起複製,或者使用 SQLite 內建的備份 API / .backup 指令。

  10. 如何復原 SQLite 資料庫
  11. 復原 SQLite 資料庫時,為了確保資料完整並避免寫入衝突,請依序執行以下步驟:

    • 停止相關服務:
    • 先關閉所有正在存取該 SQLite 的應用程式或容器,避免檔案遭鎖定或在讀寫中產生毀損。

    • 清理現有檔案:
    • 將目標目錄現存的主檔(.sqlite)以及暫存記錄檔(如 -wal 和 -shm)移出或移除,切勿只覆蓋主檔而殘留舊的 WAL 檔案。

    • 放入備份檔案:
    • 將備份的 .sqlite 檔案複製至目標資料庫目錄路徑中。

    • 設定權限與驗證:
    • 確認服務執行帳戶具備讀寫權限,並透過 CLI 執行 PRAGMA integrity_check; 確認結構無誤後再重啟服務。

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *

*