最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

使用Python備份SQLite數(shù)據(jù)庫(kù)的完整過(guò)程(附詳細(xì)代碼)

 更新時(shí)間:2026年02月10日 10:45:26   作者:weixin_pk138132  
SQLite數(shù)據(jù)庫(kù)的遷移是指將SQLite數(shù)據(jù)庫(kù)中的數(shù)據(jù)轉(zhuǎn)移到另一個(gè)數(shù)據(jù)庫(kù)系統(tǒng)(如MySQL、PostgreSQL等)中的過(guò)程,這篇文章主要介紹了使用Python備份SQLite數(shù)據(jù)庫(kù)的相關(guān)資料,需要的朋友可以參考下

1. 引言:為什么備份 SQLite 數(shù)據(jù)庫(kù)至關(guān)重要?

數(shù)據(jù)是任何應(yīng)用程序的核心資產(chǎn)。無(wú)論是個(gè)人項(xiàng)目還是商業(yè)應(yīng)用,數(shù)據(jù)的丟失都可能帶來(lái)災(zāi)難性后果。對(duì)于 SQLite 數(shù)據(jù)庫(kù)而言,備份更是不可或缺的實(shí)踐。

1.1 SQLite 數(shù)據(jù)庫(kù)的特點(diǎn)

  • 文件型數(shù)據(jù)庫(kù):SQLite 數(shù)據(jù)庫(kù)以單個(gè)文件形式存儲(chǔ)在文件系統(tǒng)中(通常是 .db.sqlite 擴(kuò)展名)。
  • 零配置、嵌入式:無(wú)需獨(dú)立的服務(wù)器進(jìn)程,直接嵌入到應(yīng)用程序中。
  • 高可用性:在應(yīng)用程序內(nèi)部直接訪問(wèn)數(shù)據(jù),但在數(shù)據(jù)丟失時(shí)恢復(fù)復(fù)雜。

1.2 備份的常見(jiàn)場(chǎng)景與挑戰(zhàn)

  • 數(shù)據(jù)損壞:硬件故障、操作系統(tǒng)崩潰、程序錯(cuò)誤等都可能導(dǎo)致數(shù)據(jù)庫(kù)文件損壞。
  • 意外刪除或修改:用戶或應(yīng)用程序的錯(cuò)誤操作可能導(dǎo)致數(shù)據(jù)丟失或錯(cuò)誤修改。
  • 遷移與升級(jí):在數(shù)據(jù)庫(kù)遷移到新系統(tǒng)或進(jìn)行 schema 升級(jí)前,備份是安全保障。
  • 版本控制:為數(shù)據(jù)庫(kù)的不同狀態(tài)創(chuàng)建快照。

對(duì)于 SQLite 這種文件型數(shù)據(jù)庫(kù),最簡(jiǎn)單的備份方式似乎是直接復(fù)制文件。然而,如果數(shù)據(jù)庫(kù)在復(fù)制過(guò)程中處于活躍寫入狀態(tài),直接復(fù)制可能導(dǎo)致備份文件不一致或損壞。這就是 sqlite3.Connection.backup() 方法發(fā)揮作用的地方。

2. 核心方法:sqlite3.Connection.backup()

Python sqlite3 模塊提供了一個(gè)名為 backup() 的強(qiáng)大方法,它允許你在數(shù)據(jù)庫(kù)運(yùn)行時(shí)進(jìn)行安全、一致的備份。

2.1backup()方法的工作原理

backup() 方法實(shí)現(xiàn)了 SQLite 數(shù)據(jù)庫(kù)的在線備份 API。它通過(guò)以下方式工作:

  1. 它在一個(gè)源數(shù)據(jù)庫(kù)連接和一個(gè)目標(biāo)數(shù)據(jù)庫(kù)連接之間進(jìn)行操作。
  2. 它會(huì)逐頁(yè)地將源數(shù)據(jù)庫(kù)的數(shù)據(jù)復(fù)制到目標(biāo)數(shù)據(jù)庫(kù)。
  3. 在復(fù)制過(guò)程中,它會(huì)確保數(shù)據(jù)的一致性,這意味著即使在備份期間源數(shù)據(jù)庫(kù)有寫入操作,備份文件也是一個(gè)在某個(gè)時(shí)間點(diǎn)上一致的快照。
  4. 這個(gè)過(guò)程是非阻塞的,源數(shù)據(jù)庫(kù)在備份期間可以繼續(xù)進(jìn)行讀寫操作。

2.2backup()的優(yōu)勢(shì):原子性、一致性、非阻塞

  • 原子性:備份操作要么完全成功,要么不成功(如果失敗則不會(huì)留下?lián)p壞的備份文件)。
  • 一致性:生成的備份文件是源數(shù)據(jù)庫(kù)在備份開始時(shí)的一個(gè)完整、一致的副本,即使在備份過(guò)程中源數(shù)據(jù)庫(kù)被修改。
  • 非阻塞:源數(shù)據(jù)庫(kù)可以繼續(xù)接受讀寫請(qǐng)求,不會(huì)因?yàn)閭浞莶僮鞫绘i定。

2.3 方法簽名與參數(shù)

source_connection.backup(target_connection, *, pages=-1, progress=None, name='main', sleep=0.250)

  • target_connection: 目標(biāo)數(shù)據(jù)庫(kù)的 sqlite3.Connection 對(duì)象。備份的數(shù)據(jù)將寫入這個(gè)連接。
  • pages: 每次迭代復(fù)制的最大頁(yè)數(shù)。默認(rèn)為 -1,表示一次性復(fù)制所有剩余頁(yè)。對(duì)于大型數(shù)據(jù)庫(kù),可以設(shè)置一個(gè)較小的正整數(shù)來(lái)分批復(fù)制,以便在備份過(guò)程中進(jìn)行其他操作或更新進(jìn)度。
  • progress: 一個(gè)可選的 callable 對(duì)象 (函數(shù)或方法),用于報(bào)告?zhèn)浞葸M(jìn)度。它會(huì)在每次復(fù)制一批頁(yè)后被調(diào)用。該 callable 接受三個(gè)整數(shù)參數(shù):
    1. status: 當(dāng)前已復(fù)制的頁(yè)數(shù)。
    2. remaining: 剩余未復(fù)制的頁(yè)數(shù)。
    3. total: 數(shù)據(jù)庫(kù)的總頁(yè)數(shù)。
  • name: 要備份的源數(shù)據(jù)庫(kù)的名稱。默認(rèn)為 'main'。這在處理附加數(shù)據(jù)庫(kù) (attached databases) 時(shí)有用。
  • sleep: 在每次 pages 批次復(fù)制后,進(jìn)程休眠的時(shí)間(秒)。默認(rèn)為 0.250 秒。這可以減少備份操作對(duì)源數(shù)據(jù)庫(kù)性能的影響。

3. 分步實(shí)現(xiàn):使用backup()進(jìn)行備份

3.1準(zhǔn)備:創(chuàng)建示例源數(shù)據(jù)庫(kù)

首先,我們需要一個(gè)包含一些數(shù)據(jù)的 SQLite 數(shù)據(jù)庫(kù)文件作為源,以便進(jìn)行備份。

import sqlite3
import os

SOURCE_DB = "my_app.db"
BACKUP_DB = "my_app_backup.db"

def create_sample_db(db_filepath):
    """創(chuàng)建并填充一個(gè)示例SQLite數(shù)據(jù)庫(kù)。"""
    print(f"\n--- Creating sample database: {db_filepath} ---")
    conn = sqlite3.connect(db_filepath)
    cursor = conn.cursor()
    cursor.execute('''
        CREATE TABLE IF NOT EXISTS users (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            name TEXT NOT NULL,
            email TEXT UNIQUE,
            age INTEGER
        );
    ''')
    # 插入一些數(shù)據(jù),如果表為空
    cursor.execute("SELECT COUNT(*) FROM users;")
    if cursor.fetchone()[0] == 0:
        cursor.execute("INSERT INTO users (name, email, age) VALUES ('Alice', 'alice@example.com', 30);")
        cursor.execute("INSERT INTO users (name, email, age) VALUES ('Bob', 'bob@example.com', 25);")
        cursor.execute("INSERT INTO users (name, email, age) VALUES ('Charlie', 'charlie@example.com', 35);")
        conn.commit()
        print(f"  Inserted 3 sample users into '{db_filepath}'.")
    else:
        print(f"  '{db_filepath}' already contains data. Skipping insertion.")
    conn.close()

def cleanup_files(*filenames):
    """刪除指定的文件。"""
    print("\n--- Cleaning up files ---")
    for filename in filenames:
        if os.path.exists(filename):
            os.remove(filename)
            print(f"  Deleted file: {filename}")

3.2建立源數(shù)據(jù)庫(kù)連接

打開源數(shù)據(jù)庫(kù)文件,獲得一個(gè) sqlite3.Connection 對(duì)象。

source_conn = sqlite3.connect(SOURCE_DB)

3.3建立目標(biāo)備份數(shù)據(jù)庫(kù)連接

打開目標(biāo)備份文件(如果文件不存在,sqlite3.connect() 會(huì)自動(dòng)創(chuàng)建它)。

backup_conn = sqlite3.connect(BACKUP_DB)

3.4執(zhí)行備份操作

調(diào)用源連接對(duì)象的 backup() 方法,并傳入目標(biāo)連接。

try:
    source_conn.backup(backup_conn)
    print(f"Backup of '{SOURCE_DB}' to '{BACKUP_DB}' completed successfully.")
except sqlite3.Error as e:
    print(f"Error during backup: {e}")

3.5關(guān)閉連接與資源管理

無(wú)論備份成功與否,都應(yīng)該關(guān)閉兩個(gè)數(shù)據(jù)庫(kù)連接,釋放資源。

finally:
    if source_conn:
        source_conn.close()
    if backup_conn:
        backup_conn.close()

4. Python 代碼示例

import sqlite3
import os

# --- Configuration ---
SOURCE_DB = "my_application.db"
BACKUP_DB = "my_application_backup.db"

# --- Helper Functions (defined above, repeated for clarity) ---
def create_sample_db(db_filepath):
    """創(chuàng)建并填充一個(gè)示例SQLite數(shù)據(jù)庫(kù)。"""
    print(f"\n--- Creating sample database: {db_filepath} ---")
    conn = sqlite3.connect(db_filepath)
    cursor = conn.cursor()
    cursor.execute('''
        CREATE TABLE IF NOT EXISTS users (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            name TEXT NOT NULL,
            email TEXT UNIQUE,
            age INTEGER
        );
    ''')
    cursor.execute("SELECT COUNT(*) FROM users;")
    if cursor.fetchone()[0] == 0:
        cursor.execute("INSERT INTO users (name, email, age) VALUES ('Alice', 'alice@example.com', 30);")
        cursor.execute("INSERT INTO users (name, email, age) VALUES ('Bob', 'bob@example.com', 25);")
        cursor.execute("INSERT INTO users (name, email, age) VALUES ('Charlie', 'charlie@example.com', 35);")
        conn.commit()
        print(f"  Inserted 3 sample users into '{db_filepath}'.")
    else:
        print(f"  '{db_filepath}' already contains data. Skipping insertion.")
    conn.close()

def cleanup_files(*filenames):
    """刪除指定的文件。"""
    print("\n--- Cleaning up files ---")
    for filename in filenames:
        if os.path.exists(filename):
            os.remove(filename)
            print(f"  Deleted file: {filename}")

# --- Core Backup Function ---
def perform_backup(source_db_path, backup_db_path):
    """
    使用 sqlite3.Connection.backup() 方法備份 SQLite 數(shù)據(jù)庫(kù)。
    """
    print(f"\n--- Starting backup from '{source_db_path}' to '{backup_db_path}' ---")
    source_conn = None
    backup_conn = None
    try:
        source_conn = sqlite3.connect(source_db_path)
        backup_conn = sqlite3.connect(backup_db_path)
        
        # 執(zhí)行備份操作
        source_conn.backup(backup_conn)
        
        print(f"Backup of '{source_db_path}' to '{backup_db_path}' completed successfully.")
        return True
    except sqlite3.Error as e:
        print(f"Error during backup: {e}")
        return False
    except Exception as e:
        print(f"An unexpected error occurred: {e}")
        return False
    finally:
        if source_conn:
            source_conn.close()
        if backup_conn:
            backup_conn.close()

# --- Verification Function ---
def verify_backup(db_filepath):
    """驗(yàn)證備份數(shù)據(jù)庫(kù)是否存在并包含數(shù)據(jù)。"""
    print(f"\n--- Verifying backup: {db_filepath} ---")
    if not os.path.exists(db_filepath):
        print(f"  Error: Backup file '{db_filepath}' does not exist.")
        return False

    conn = None
    try:
        conn = sqlite3.connect(db_filepath)
        cursor = conn.cursor()
        cursor.execute("SELECT COUNT(*) FROM users;")
        row_count = cursor.fetchone()[0]
        print(f"  Backup file '{db_filepath}' exists and contains {row_count} rows in 'users' table.")
        
        # 打印一些示例數(shù)據(jù)
        cursor.execute("SELECT id, name, email, age FROM users LIMIT 2;")
        sample_data = cursor.fetchall()
        print("  Sample data from backup:")
        for row in sample_data:
            print(f"    ID: {row[0]}, Name: {row[1]}, Email: {row[2]}, Age: {row[3]}")
        return True
    except sqlite3.Error as e:
        print(f"  Error accessing backup database '{db_filepath}': {e}")
        return False
    finally:
        if conn:
            conn.close()

# --- Main execution ---
# cleanup_files(SOURCE_DB, BACKUP_DB) # Clean up previous runs
# create_sample_db(SOURCE_DB)
# if perform_backup(SOURCE_DB, BACKUP_DB):
#     verify_backup(BACKUP_DB)
# cleanup_files(SOURCE_DB, BACKUP_DB) # Clean up after execution

5. 最佳實(shí)踐與注意事項(xiàng)

5.1錯(cuò)誤處理 (try-except)

始終將備份操作包裝在 try...except sqlite3.Error 塊中,以捕獲可能發(fā)生的數(shù)據(jù)庫(kù)錯(cuò)誤。如果使用 with 語(yǔ)句管理連接,它可以簡(jiǎn)化資源釋放,但在 backup() 方法中,由于涉及兩個(gè)連接,手動(dòng) finally 塊確保關(guān)閉所有連接更為穩(wěn)妥。

5.2備份進(jìn)度監(jiān)控 (使用progress回調(diào))

對(duì)于大型數(shù)據(jù)庫(kù),備份可能需要一段時(shí)間。提供一個(gè) progress 回調(diào)函數(shù)可以向用戶顯示備份進(jìn)度。

def backup_progress(status, remaining, total):
    """
    備份進(jìn)度回調(diào)函數(shù)。
    :param status: 已復(fù)制的頁(yè)數(shù)。
    :param remaining: 剩余未復(fù)制的頁(yè)數(shù)。
    :param total: 數(shù)據(jù)庫(kù)的總頁(yè)數(shù)。
    """
    print(f"\r  Copied: {status} pages, Remaining: {remaining} pages, Total: {total} pages "
          f"({(status/total)*100:.2f}%)", end='')

# 在 perform_backup 函數(shù)中調(diào)用
# source_conn.backup(backup_conn, progress=backup_progress)
# print() # 備份完成后換行

5.3文件路徑管理

  • 唯一文件名:為備份文件添加時(shí)間戳或版本號(hào),以防止覆蓋,并能夠回溯到不同時(shí)間點(diǎn)的備份。例如 my_app_backup_2023-10-27_10-30-00.db。
  • 備份目錄:將所有備份存儲(chǔ)在一個(gè)專門的備份目錄中,方便管理和清理。

5.4替代方案:簡(jiǎn)單的文件復(fù)制 (shutil.copyfile)

如果你確定在備份時(shí)數(shù)據(jù)庫(kù)沒(méi)有被任何進(jìn)程寫入,或者數(shù)據(jù)庫(kù)是一個(gè)只讀文件,那么直接使用 shutil.copyfile() 是一種簡(jiǎn)單快速的方法。

import shutil

def simple_file_copy_backup(source_path, backup_path):
    """
    簡(jiǎn)單的文件復(fù)制備份,僅在源數(shù)據(jù)庫(kù)未被寫入時(shí)安全。
    """
    print(f"\n--- Performing simple file copy backup from '{source_path}' to '{backup_path}' ---")
    if not os.path.exists(source_path):
        print(f"  Error: Source database '{source_path}' does not exist.")
        return False
    try:
        shutil.copyfile(source_path, backup_path)
        print(f"  Successfully copied '{source_path}' to '{backup_path}'.")
        return True
    except Exception as e:
        print(f"  Error during file copy: {e}")
        return False

# --- 嚴(yán)重警告 ---
# 僅當(dāng)確定源數(shù)據(jù)庫(kù)在復(fù)制期間不會(huì)有任何寫入操作時(shí)才使用此方法。
# 否則,你可能會(huì)得到一個(gè)損壞或不一致的備份文件。
# 對(duì)于正在使用的數(shù)據(jù)庫(kù),始終優(yōu)先使用 sqlite3.Connection.backup()。

5.5備份壓縮(zipfile,shutil.make_archive)

SQLite 數(shù)據(jù)庫(kù)文件可能很大。為了節(jié)省存儲(chǔ)空間和方便傳輸,可以考慮對(duì)備份文件進(jìn)行壓縮。

import zipfile

def compress_backup(db_filepath, zip_filepath):
    """將數(shù)據(jù)庫(kù)文件壓縮成zip文件。"""
    print(f"\n--- Compressing '{db_filepath}' to '{zip_filepath}' ---")
    try:
        with zipfile.ZipFile(zip_filepath, 'w', zipfile.ZIP_DEFLATED) as zf:
            zf.write(db_filepath, os.path.basename(db_filepath))
        print(f"  Successfully compressed '{db_filepath}' to '{zip_filepath}'.")
        return True
    except Exception as e:
        print(f"  Error compressing backup: {e}")
        return False

# Example usage:
# if perform_backup(SOURCE_DB, BACKUP_DB):
#     compress_backup(BACKUP_DB, BACKUP_DB + ".zip")

5.6自動(dòng)化與調(diào)度

在生產(chǎn)環(huán)境中,備份通常是自動(dòng)化任務(wù)。你可以使用:

  • Linux/macOS: cron 任務(wù)調(diào)度器來(lái)定時(shí)執(zhí)行 Python 備份腳本。
  • Windows: 任務(wù)計(jì)劃程序 (Task Scheduler) 來(lái)定時(shí)執(zhí)行 Python 備份腳本。

5.7存儲(chǔ)位置與恢復(fù)策略

  • 異地存儲(chǔ):將備份文件存儲(chǔ)在與源數(shù)據(jù)庫(kù)不同的物理位置(例如,網(wǎng)絡(luò)共享、云存儲(chǔ)、外部硬盤),以防止整個(gè)系統(tǒng)故障導(dǎo)致數(shù)據(jù)丟失。
  • 多個(gè)備份版本:保留多個(gè)時(shí)間點(diǎn)的備份,以便在需要時(shí)可以選擇恢復(fù)到某個(gè)特定的歷史狀態(tài)。
  • 測(cè)試恢復(fù):定期測(cè)試備份文件的可恢復(fù)性,確保備份有效。

5.8從備份恢復(fù)

恢復(fù)通常只是將備份文件復(fù)制回原始數(shù)據(jù)庫(kù)的位置。但在復(fù)制之前,請(qǐng)確保原始數(shù)據(jù)庫(kù)文件被關(guān)閉或刪除,并且備份文件是正確的。

def restore_from_backup(backup_db_path, target_db_path):
    """
    從備份文件恢復(fù)數(shù)據(jù)庫(kù)。
    警告:這將覆蓋目標(biāo)路徑的現(xiàn)有數(shù)據(jù)庫(kù)!
    """
    print(f"\n--- Restoring from '{backup_db_path}' to '{target_db_path}' ---")
    if not os.path.exists(backup_db_path):
        print(f"  Error: Backup file '{backup_db_path}' does not exist.")
        return False
    try:
        # 確保目標(biāo)數(shù)據(jù)庫(kù)連接已關(guān)閉
        if os.path.exists(target_db_path):
            os.remove(target_db_path) # 刪除舊的(或損壞的)數(shù)據(jù)庫(kù)
            print(f"  Deleted existing database at '{target_db_path}'.")
        shutil.copyfile(backup_db_path, target_db_path)
        print(f"  Successfully restored '{backup_db_path}' to '{target_db_path}'.")
        return True
    except Exception as e:
        print(f"  Error during restoration: {e}")
        return False

6. 綜合代碼示例:包含進(jìn)度監(jiān)控和驗(yàn)證

import sqlite3
import os
import shutil # For file copy operations and cleanup
import datetime # For timestamping backup files

# --- Configuration ---
SOURCE_DB = "production_app.db"
BACKUP_DIR = "backups" # Directory to store backups
ZIP_DIR = "compressed_backups" # Directory to store compressed backups

# --- 1. Helper Functions ---
def create_sample_db(db_filepath):
    """Creates and populates a sample SQLite database."""
    print(f"\n--- Creating sample database: {db_filepath} ---")
    conn = None
    try:
        conn = sqlite3.connect(db_filepath)
        cursor = conn.cursor()
        cursor.execute('''
            CREATE TABLE IF NOT EXISTS inventory (
                id INTEGER PRIMARY KEY AUTOINCREMENT,
                item_name TEXT NOT NULL,
                quantity INTEGER DEFAULT 0,
                last_updated TEXT
            );
        ''')
        cursor.execute("SELECT COUNT(*) FROM inventory;")
        if cursor.fetchone()[0] == 0:
            cursor.execute("INSERT INTO inventory (item_name, quantity, last_updated) VALUES ('Laptop', 150, '2023-10-26 10:00:00');")
            cursor.execute("INSERT INTO inventory (item_name, quantity, last_updated) VALUES ('Monitor', 200, '2023-10-26 11:30:00');")
            cursor.execute("INSERT INTO inventory (item_name, quantity, last_updated) VALUES ('Keyboard', 300, '2023-10-26 12:00:00');")
            conn.commit()
            print(f"  Inserted 3 sample items into '{db_filepath}'.")
        else:
            print(f"  '{db_filepath}' already contains data. Skipping insertion.")
    except sqlite3.Error as e:
        print(f"  Database error during sample DB creation: {e}")
    finally:
        if conn:
            conn.close()

def cleanup_dirs(*dirs):
    """Deletes directories and their contents."""
    print("\n--- Cleaning up directories ---")
    for dir_path in dirs:
        if os.path.exists(dir_path):
            shutil.rmtree(dir_path)
            print(f"  Deleted directory: {dir_path}")

def cleanup_files(*filenames):
    """Deletes specified files."""
    for filename in filenames:
        if os.path.exists(filename):
            os.remove(filename)
            print(f"  Deleted file: {filename}")

def get_backup_filename(base_name, timestamp=True, extension=".db"):
    """生成帶時(shí)間戳的備份文件名。"""
    if timestamp:
        current_time = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
        return f"{base_name}_{current_time}{extension}"
    return f"{base_name}{extension}"

# --- 2. Backup Progress Callback ---
def backup_progress_callback(status, remaining, total):
    """Prints backup progress to the console."""
    percentage = (status / total) * 100 if total > 0 else 0
    print(f"\r  Progress: {status}/{total} pages ({percentage:.2f}%) "
          f"Remaining: {remaining} pages", end='', flush=True)

# --- 3. Core Backup Function ---
def perform_db_backup(source_db_path, backup_dir_path):
    """
    使用 sqlite3.Connection.backup() 方法備份 SQLite 數(shù)據(jù)庫(kù),并包含進(jìn)度監(jiān)控。
    備份文件將存儲(chǔ)在 backup_dir_path 目錄下,并帶有時(shí)間戳。
    """
    if not os.path.exists(backup_dir_path):
        os.makedirs(backup_dir_path)
        print(f"  Created backup directory: {backup_dir_path}")

    base_name = os.path.basename(source_db_path).replace(".db", "")
    backup_filename = get_backup_filename(base_name)
    target_backup_path = os.path.join(backup_dir_path, backup_filename)
    
    print(f"\n--- Starting online backup from '{source_db_path}' to '{target_backup_path}' ---")
    source_conn = None
    backup_conn = None
    try:
        source_conn = sqlite3.connect(source_db_path)
        backup_conn = sqlite3.connect(target_backup_path)
        
        # 執(zhí)行備份操作,包含進(jìn)度回調(diào)
        source_conn.backup(backup_conn, pages=1, progress=backup_progress_callback, sleep=0.05) # Small pages for visible progress
        
        print("\nBackup completed successfully.")
        return target_backup_path
    except sqlite3.Error as e:
        print(f"\nError during backup: {e}")
        if os.path.exists(target_backup_path):
            os.remove(target_backup_path) # Clean up partial backup on error
            print(f"  Removed incomplete backup file: {target_backup_path}")
        return None
    except Exception as e:
        print(f"\nAn unexpected error occurred during backup: {e}")
        return None
    finally:
        if source_conn:
            source_conn.close()
        if backup_conn:
            backup_conn.close()

# --- 4. Verification Function ---
def verify_db_backup(db_filepath):
    """Verifies the backup database exists and contains data."""
    print(f"\n--- Verifying backup: {db_filepath} ---")
    if not os.path.exists(db_filepath):
        print(f"  Error: Backup file '{db_filepath}' does not exist.")
        return False

    conn = None
    try:
        conn = sqlite3.connect(db_filepath)
        cursor = conn.cursor()
        cursor.execute("SELECT COUNT(*) FROM inventory;")
        row_count = cursor.fetchone()[0]
        print(f"  Backup file '{db_filepath}' exists and contains {row_count} rows in 'inventory' table.")
        
        cursor.execute("SELECT id, item_name, quantity FROM inventory LIMIT 2;")
        sample_data = cursor.fetchall()
        print("  Sample data from backup:")
        for row in sample_data:
            print(f"    ID: {row[0]}, Item: {row[1]}, Quantity: {row[2]}")
        return True
    except sqlite3.Error as e:
        print(f"  Error accessing backup database '{db_filepath}': {e}")
        return False
    except Exception as e:
        print(f"  An unexpected error occurred during verification: {e}")
        return False
    finally:
        if conn:
            conn.close()

# --- 5. Compression Function ---
def compress_db_backup(db_filepath, compressed_dir_path):
    """Compresses a database file into a .zip archive."""
    if not os.path.exists(compressed_dir_path):
        os.makedirs(compressed_dir_path)
        print(f"  Created compressed backup directory: {compressed_dir_path}")

    zip_filename = os.path.basename(db_filepath) + ".zip"
    target_zip_path = os.path.join(compressed_dir_path, zip_filename)
    
    print(f"\n--- Compressing '{db_filepath}' to '{target_zip_path}' ---")
    try:
        with zipfile.ZipFile(target_zip_path, 'w', zipfile.ZIP_DEFLATED) as zf:
            zf.write(db_filepath, os.path.basename(db_filepath)) # Write with just filename inside zip
        print(f"  Successfully compressed '{db_filepath}' to '{target_zip_path}'.")
        return target_zip_path
    except Exception as e:
        print(f"  Error compressing backup: {e}")
        return None

# --- Main Program Execution ---
def main():
    cleanup_dirs(BACKUP_DIR, ZIP_DIR)
    cleanup_files(SOURCE_DB) # Ensure source DB is clean if it was left from previous run

    create_sample_db(SOURCE_DB) # Create the source database with some data

    # Perform backup using sqlite3.Connection.backup()
    backup_file_path = perform_db_backup(SOURCE_DB, BACKUP_DIR)
    
    if backup_file_path:
        verify_db_backup(backup_file_path) # Verify the created backup
        
        # Optionally, compress the backup file
        compressed_file_path = compress_db_backup(backup_file_path, ZIP_DIR)
        if compressed_file_path:
            print(f"\nCompressed backup available at: {compressed_file_path}")
            
    print("\n--- Program finished ---")
    # cleanup_dirs(BACKUP_DIR, ZIP_DIR) # Uncomment to delete backup files after run
    # cleanup_files(SOURCE_DB) # Uncomment to delete source DB after run

if __name__ == "__main__":
    main()

7. 總結(jié)

為您詳盡解析了在 Python 中使用 sqlite3 模塊備份 SQLite 數(shù)據(jù)庫(kù)的方法。

核心要點(diǎn)回顧:

  • sqlite3.Connection.backup() 是在數(shù)據(jù)庫(kù)活躍時(shí)進(jìn)行安全、一致備份的最佳實(shí)踐,它保證了原子性和非阻塞性。
  • 進(jìn)度監(jiān)控:使用 progress 回調(diào)函數(shù)可以為大型數(shù)據(jù)庫(kù)備份提供用戶反饋。
  • 錯(cuò)誤處理:始終捕獲 sqlite3.Error 以確保程序的健壯性。
  • 文件名和目錄管理:為備份文件添加時(shí)間戳,并組織到專門的備份目錄中,便于管理。
  • 文件復(fù)制 (shutil.copyfile) 僅適用于確定數(shù)據(jù)庫(kù)未被寫入的場(chǎng)景,否則存在數(shù)據(jù)損壞風(fēng)險(xiǎn)。
  • 壓縮:使用 zipfile 等模塊可以有效減小備份文件大小。
  • 自動(dòng)化和異地存儲(chǔ):考慮將備份過(guò)程自動(dòng)化,并將備份文件存儲(chǔ)在安全、獨(dú)立的位置。

通過(guò)掌握這些方法和最佳實(shí)踐,您將能夠構(gòu)建一個(gè)可靠的備份策略,有效保護(hù)您的 SQLite 數(shù)據(jù)庫(kù)數(shù)據(jù)免受意外丟失。

到此這篇關(guān)于使用Python備份SQLite數(shù)據(jù)庫(kù)的文章就介紹到這了,更多相關(guān)Python備份SQLite數(shù)據(jù)庫(kù)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評(píng)論

肃北| 富蕴县| 平舆县| 沾化县| 涞水县| 许昌市| 象山县| 新宁县| 梁河县| 九寨沟县| 安阳县| 阳信县| 苏州市| 巴东县| 手游| 花莲县| 兴业县| 蓬溪县| 祁连县| 静宁县| 南溪县| 浮梁县| 达日县| 双牌县| 东辽县| 乌拉特前旗| 长治县| 苗栗市| 江安县| 山阴县| 镇坪县| 嘉峪关市| 东源县| 花莲市| 八宿县| 高清| 乐至县| 贵溪市| 岳西县| 靖宇县| 宣恩县|