使用Python備份SQLite數(shù)據(jù)庫(kù)的完整過(guò)程(附詳細(xì)代碼)
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ò)以下方式工作:
- 它在一個(gè)源數(shù)據(jù)庫(kù)連接和一個(gè)目標(biāo)數(shù)據(jù)庫(kù)連接之間進(jìn)行操作。
- 它會(huì)逐頁(yè)地將源數(shù)據(jù)庫(kù)的數(shù)據(jù)復(fù)制到目標(biāo)數(shù)據(jù)庫(kù)。
- 在復(fù)制過(guò)程中,它會(huì)確保數(shù)據(jù)的一致性,這意味著即使在備份期間源數(shù)據(jù)庫(kù)有寫入操作,備份文件也是一個(gè)在某個(gè)時(shí)間點(diǎn)上一致的快照。
- 這個(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ù):status: 當(dāng)前已復(fù)制的頁(yè)數(shù)。remaining: 剩余未復(fù)制的頁(yè)數(shù)。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)文章
Python利用海龜畫圖turtle庫(kù)做一個(gè)籃球比賽計(jì)時(shí)畫面示例代碼
這篇文章主要介紹了Python利用海龜畫圖turtle庫(kù)做一個(gè)籃球比賽計(jì)時(shí)畫面的相關(guān)資料,文中通過(guò)代碼介紹的非常詳細(xì),代碼實(shí)現(xiàn)了一個(gè)基本的計(jì)時(shí)器功能,需要的朋友可以參考下2024-12-12
Python實(shí)現(xiàn)Linux的find命令實(shí)例分享
本文給大家分享的是使用python簡(jiǎn)單實(shí)現(xiàn)模擬linux的find命令的實(shí)例代碼,推薦給大家,希望大家能夠喜歡2017-06-06
python實(shí)現(xiàn)年會(huì)抽獎(jiǎng)程序
這篇文章主要為大家詳細(xì)介紹了python實(shí)現(xiàn)年會(huì)抽獎(jiǎng)程序,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-01-01
pandas的object對(duì)象轉(zhuǎn)時(shí)間對(duì)象的方法
下面小編就為大家分享一篇pandas的object對(duì)象轉(zhuǎn)時(shí)間對(duì)象的方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-04-04
mac使用python識(shí)別圖形驗(yàn)證碼功能
這篇文章主要介紹了mac使用python識(shí)別圖形驗(yàn)證碼功能,本文給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-01-01
Python?print函數(shù):如何將對(duì)象打印輸出
這篇文章主要介紹了Python?print函數(shù):如何將對(duì)象打印輸出,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-05-05

