پشتیبانگیری آنلاین از پایگاه داده SQLite در محیط .NET با استفاده از VACUUM INTO
نویسنده: وحید نصیری
تاریخ: ۱۴۰۴/۱۱/۰۹ ۱۰:۵۰
آدرس: www.dntips.ir
| مطالب | ۳۶۹۴ |
| نویسندگان | ۲۷۶ |
| گروههای مطالب | ۱۰۲۴ |
| نقشههای راه | ۱۱۹ |
| دورهها | ۱۴ |
| اشتراکها | ۱۷۹۱۴ |
VACUUM INTO در SQLite، که از نسخه 3.27.0 (سال 2019) به بعد پشتیبانی میشود، یک راهحل رسمی و قدرتمند برای ایجاد پشتیبان آنلاین (Online Backup) ارائه میدهد. این دستور اجازه میدهد تا یک کپی کامل و سازگار از پایگاه داده را بدون مسدود کردن عملیات خواندن ایجاد کنید.VACUUM INTO در محیط .NET با تمرکز بر Providerهای ADO.NET میپردازیم. هدف ما ارائه یک راهنمای عملی برای توسعهدهندگان است که بتوانند این ویژگی را در محیطهای تولید (Production) پیادهسازی کنند. با توجه به اهمیت امنیت و کارایی، نکات کلیدی مانند تنظیمات پیشنیاز، محدودیتها و بهترین شیوهها را پوشش میدهیم. اگر شما توسعهدهندهای هستید که با SQLite کار میکنید، این مقاله به شما کمک میکند تا پشتیبانگیری را به شیوهای ایمن و کارآمد انجام دهید.VACUUM INTO به صورت زیر استفاده میشود:VACUUM INTO 'path/to/backup.db';
VACUUM INTO بدون مشکل اجرا میشود. نکته مهم: مسیر فایل پشتیبان باید قابل نوشتن باشد و پایگاه داده نباید در حالت تراکنش (Transaction) فعال باشد.VACUUM INTO قدرتمند است، اما محدودیتهایی دارد که باید به آنها توجه کرد:Data Source=main.db;Pooling=False;
@path) استفاده کرد، زیرا SQLite اجازه Bind کردن مسیر فایل را نمیدهد. بنابراین، کد زیر کار نمیکند:VACUUM INTO @path
PRAGMA journal_mode = WAL;
connection.Execute("PRAGMA busy_timeout = 10000;");backup_20260129_021300.db.VACUUM INTO پایگاه داده را کامل بازنویسی میکند، بنابراین برای پایگاههای بزرگ (بیش از 10 گیگابایت)، بهتر است در ساعات کمبار (Off-Peak) اجرا شود یا از کارهای زمانبندیشده (Cron Jobs) استفاده کنید.VACUUM INTO ممکن است مناسب نباشد. در چنین مواردی، جایگزینهایی مانند API پشتیبانگیری SQLite (sqlite3_backup API) یا پشتیبانگیری افزایشی (Incremental Backup) پیشنهاد میشود.PRAGMA quick_check;
integrity_check است و در محیط تولید مناسبتر. اگر نتیجه "ok" نباشد، پشتیبان نامعتبر است.using Microsoft.Data.Sqlite;
using System.Globalization;
public static class SqliteBackupHelper
{
public static async Task CreateOnlineSqliteBackupAsync(
string sourceConnectionString,
string backupFilePath,
CancellationToken cancellationToken = default)
{
ArgumentException.ThrowIfNullOrWhiteSpace(sourceConnectionString);
ArgumentException.ThrowIfNullOrWhiteSpace(backupFilePath);
// VACUUM INTO پارامتر نمیپذیرد
backupFilePath = backupFilePath.Replace("'", "''");
var connectionString =
sourceConnectionString.Contains("Pooling=", StringComparison.OrdinalIgnoreCase)
? sourceConnectionString
: $"{sourceConnectionString};Pooling=False";
await using var connection = new SqliteConnection(connectionString);
await connection.OpenAsync(cancellationToken);
await using var command = connection.CreateCommand();
command.CommandTimeout = 0; // vacuum ممکنه طولانی باشه
command.CommandText = $"""
-- 1. اطمینان از WAL
PRAGMA journal_mode = WAL;
-- 2. جلوگیری از fail زیر load
PRAGMA busy_timeout = 10000;
-- 3. بکاپ آنلاین
VACUUM INTO '{backupFilePath}';
""";
await command.ExecuteNonQueryAsync(cancellationToken);
// 🔍 Health Check بکاپ
await ValidateBackupAsync(backupFilePath, cancellationToken);
}
private static async Task ValidateBackupAsync(
string backupFilePath,
CancellationToken cancellationToken)
{
var backupConnectionString =
$"Data Source={backupFilePath};Mode=ReadOnly;Pooling=False";
await using var backupConnection = new SqliteConnection(backupConnectionString);
await backupConnection.OpenAsync(cancellationToken);
await using var command = backupConnection.CreateCommand();
command.CommandText = "PRAGMA quick_check;";
command.CommandTimeout = 30;
var result = await command.ExecuteScalarAsync(cancellationToken);
if (!string.Equals(result?.ToString(), "ok", StringComparison.OrdinalIgnoreCase))
{
throw new InvalidOperationException(
$"SQLite backup integrity check failed: {result}");
}
}
}var sourceConnectionString = "Data Source=/var/app/data/main.db;Cache=Shared;";
var backupPath = Path.Combine("/var/app/backups", $"main_{DateTime.UtcNow:yyyyMMdd_HHmmss}.db");
await SqliteBackupHelper.CreateOnlineSqliteBackupAsync(sourceConnectionString, backupPath);CancellationToken اجازه میدهد تا در صورت خاموش شدن سرویس، عملیات به طور ایمن متوقف شود بدون ایجاد فایل ناقص.VACUUM INTO همراه با حالت WAL، یک گزینه عالی برای پشتیبانگیری آنلاین از SQLite در محیط .NET است، به ویژه در سرورهای در حال سرویسدهی. این روش atomic، non-blocking برای خواندن و سازگار با WAL است، که آن را برای کاربردهای تولید مناسب میکند. با رعایت تنظیماتی مانند خاموش کردن Pooling، تنظیم Busy Timeout و بررسی سلامت، میتوانید از ایمنی دادهها اطمینان حاصل کنید. اگر پایگاه داده شما بزرگ یا نوشتاری سنگین است، جایگزینهایی مانند API پشتیبانگیری را در نظر بگیرید. این رویکرد نه تنها کارایی را حفظ میکند، بلکه توسعهدهندگان را به سمت شیوههای پایدار و امن هدایت مینماید.