استفاده از SQLite در محیط عملیاتی روی VPS
استفاده از SQLite برای برنامههای کوچک روی یک VPS بهینه است. در این مطلب تنظیمات WAL mode، دستور busy_timeout و نحوه پشتیبانگیری با Litestream را بررسی میکنیم.
چه زمانی SQLite پایگاهداده مناسب برای محیط عملیاتی روی یک VPS است
اجرای SQLite در محیط عملیاتی روی یک VPS برای اکثر برنامههای کوچک انتخاب درستی است و دلیل آن ساده است: یک پردازش روی یک ماشین که در یک فایل مینویسد، نیازی به سرور پایگاهداده ندارد. هیچ daemonای برای نظارت، هیچ پورتی برای فایروال، هیچ رمز عبوری برای تغییر دورهای و هیچ ماشین دومی برای روشن نگهداشتن وجود ندارد. یک کوئری بهجای رفتوبرگشت شبکه، یک فراخوانی تابع است؛ بنابراین صفحهای که چهل کوئری اجرا میکند، تنها هزینه چهل فراخوانی تابع را برای شما دارد.
هزینه این کار محدود و مشخص است. SQLite در هر لحظه تنها به یک نویسنده در کل فایل پایگاهداده اجازه فعالیت میدهد و فایل نمیتواند بین دو ماشین به اشتراک گذاشته شود. هر دوی این محدودیتها برای یک VPS واحد که یک برنامه واحد را اجرا میکند، قابلقبول هستند. اما هر دوی آنها در لحظهای که از این مقیاس فراتر بروید، مشکلساز خواهند بود. این راهنما تنظیماتی را پوشش میدهد که SQLite را روی سرور ایمن میکند، پشتیبانگیری مداوم با Litestream را توضیح میدهد و نقطهای را مشخص میکند که باید از آن فراتر نروید.
ابتدا ابزار خط فرمان را نصب کنید. تمام موارد زیر روی Ubuntu 24.04 اجرا شدهاند.
sudo apt update
sudo apt install -y sqlite3
sqlite3 --versionاین دستور نسخهای را چاپ میکند که با 3. شروع شده و به دنبال آن تاریخ ساخت و هش سورسکد میآید. Ubuntu 24.04 تا ژوئیه 2026 همراه با SQLite 3.45.1 عرضه میشود. برنامه شما احتمالاً از این باینری استفاده نمیکند: اکثر runtimeهای زبانهای برنامهنویسی، نسخه مخصوص خود از کتابخانه SQLite را همراه دارند که اغلب جدیدتر است؛ بنابراین پیش از تکیه بر یک قابلیت جدید، نسخهای که درایور پایگاهداده شما گزارش میدهد را بررسی کنید.
چرا حالت WAL اولین تغییری است که باید اعمال کنید
SQLite بهصورت پیشفرض از rollback journal استفاده میکند. پیش از تغییر هر صفحه، ابتدا نسخه اصلی آن را در یک فایل -journal کپی کرده و سپس دیتابیس را در محل اصلی ویرایش میکند. برای انجام ایمن این کار، یک قفل انحصاری (exclusive lock) روی کل فایل میگیرد؛ بنابراین تا زمانی که عملیات نوشتن در جریان است، تمام خوانندهها منتظر میمانند. روی یک لپتاپ، کسی متوجه این تأخیر نمیشود. اما در یک وبسرور، یک عملیات نوشتنِ کُند، تمام درخواستهایی که با دیتابیس سروکار دارند را متوقف میکند.
حالت WAL (مخفف write-ahead log) این ترتیب را معکوس میکند. نویسنده، صفحات جدید را به یک فایل جداگانه -wal اضافه میکند و دیتابیس اصلی را دستنخورده باقی میگذارد. خوانندهها همچنان از فایل اصلی و از همان snapshot که در لحظه شروع خواندن داشتند، استفاده میکنند؛ بنابراین خوانندهها مانع نویسنده نمیشوند و نویسنده نیز مانع خوانندهها نمیشود. در نهایت، یک عملیات checkpoint صفحات انباشتهشده در WAL را به دیتابیس اصلی منتقل میکند. همین یک تغییر، بخش عمدهای از دلیلی است که SQLite را برای استفاده در پشت یک وباپلیکیشن مناسب میسازد.
فعالسازی حالت WAL و تأیید پایداری آن
mkdir -p ~/app
sqlite3 ~/app/app.db "PRAGMA journal_mode=WAL;"این دستور مقدار wal را چاپ میکند. این خروجی صرفاً جنبه تزئینی ندارد. PRAGMA journal_mode حالتی که دیتابیس واقعاً در آن قرار دارد را برمیگرداند، بنابراین پاسخ delete به این معناست که تغییر اعمال نشده و شما همچنان از rollback journal استفاده میکنید.
حالت WAL پایدار است. این حالت یک فلگ در هدر دیتابیس است و نه یک تنظیم مربوط به اتصال؛ بنابراین شما آن را یکبار برای هر فایل دیتابیس اجرا میکنید و تمام اتصالات بعدی، حتی پس از reboot، آن را به ارث میبرند. این موضوع را با یک اتصال جدید بررسی کنید.
sqlite3 ~/app/app.db "PRAGMA journal_mode;"اکنون یک جدول بسازید و محتویات روی دیسک را مشاهده کنید.
sqlite3 ~/app/app.db <<'SQL'
CREATE TABLE IF NOT EXISTS notes (id INTEGER PRIMARY KEY, body TEXT NOT NULL);
INSERT INTO notes (body) VALUES ('first row');
SQL
ls -l ~/app/اکنون سه فایل وجود دارد: app.db، app.db-wal و app.db-shm. فایل -wal صفحات commit شدهای را نگه میدارد که هنوز checkpoint نشدهاند. فایل -shm یک ایندکس حافظه مشترک است که تمام اتصالات آن را map میکنند تا همگی بر سر محتوای WAL توافق داشته باشند. هر دو متعلق به دیتابیس هستند و فایلهای موقت (scratch) محسوب نمیشوند. اگر در حین اجرای برنامه، فقط از app.db کپی بگیرید، فایلی خواهید داشت که تمام commitهای اخیر را ندارد. اگر app.db را حذف کنید و دو فایل دیگر را باقی بگذارید، SQLite آن صفحات قدیمی WAL را روی هر فایل جدیدی که با آن نام ایجاد شود اعمال میکند؛ این همان روشی است که افراد هنگام تلاش برای ریست کردن دیتابیس، باعث خرابی یک دیتابیس سالم میشوند.
The connection settings every production app needs
Only journal_mode is stored in the database. Every other setting below is per connection, which means your application has to run it on each connection it opens, including every connection a pool creates in the background.
PRAGMA journal_mode = WAL;
PRAGMA busy_timeout = 5000;
PRAGMA synchronous = NORMAL;
PRAGMA foreign_keys = ON;busy_timeout = 5000 tells SQLite to keep retrying a locked database for up to 5000 milliseconds before it returns database is locked. The default is 0, so by default SQLite fails instantly the first time two writers overlap. Setting this single value removes most of the lock errors that get blamed on SQLite itself.
synchronous = NORMAL is the right setting in WAL mode, and the trade is worth understanding. At FULL, SQLite calls fsync on the WAL at every commit. At NORMAL, it syncs at checkpoints instead. The SQLite documentation is blunt about what you give up: transactions are no longer durable after a power failure or a hard reset. The database cannot be corrupted by that power loss, you simply lose the last commits that had not reached the disk. On a VPS that is usually the right trade, because it takes an fsync out of the path of every single write.
foreign_keys = ON is off by default for backwards compatibility, and it is per connection. A schema full of REFERENCES clauses enforces nothing at all until each connection turns this on.
One more setting matters only later. SQLite checkpoints automatically once the WAL grows past 1000 pages, and the work is done by whichever connection happens to finish a transaction at that moment. That is fine on its own. It becomes a question when Litestream is running, because Litestream wants control over when checkpoints happen.
چرا database is locked حتی پس از تنظیم busy_timeout همچنان رخ میدهد
این همان خطایی است که کاربران را به سمت Postgres بازمیگرداند و یک دلیل مشخص دارد.
یک busy timeout، یک busy handler را نصب میکند، اما SQLite تضمین نمیکند که حتماً آن را فراخوانی کند.
اگر SQLite تشخیص دهد که فراخوانی busy handler ممکن است منجر به بنبست (deadlock) شود، به جای فراخوانی آن، مستقیماً خطای SQLITE_BUSY را به برنامه بازمیگرداند.
بنبستی که SQLite از آن اجتناب میکند، زمانی رخ میدهد که یک تراکنش ارتقا (upgrade) مییابد. یک BEGIN ساده در SQLite به معنای BEGIN DEFERRED است. اگر اولین دستور پس از آن یک SELECT باشد، شما در یک تراکنش خواندنی (read transaction) هستید. هنگامی که یک UPDATE در همان تراکنش نیاز دارد به یک تراکنش نوشتاری تبدیل شود و اتصال دیگری از زمان شروع خواندن شما دادهای را نوشته باشد، SQLite نمیتواند شما را در حالت انتظار قرار دهد؛ زیرا snapshot شما از قبل قدیمی شده است و انتظار کشیدن فقط باعث بنبست بین دو اتصال میشود. مستندات مستقیماً به این نتیجه اشاره دارند:
دستورات نوشتاری بعدی، تراکنش را در صورت امکان به یک تراکنش نوشتاری ارتقا میدهند یا خطای SQLITE_BUSY را برمیگردانند.
در این حالت، timeout پنج هزار میلیثانیهای شما هرگز بررسی نمیشود. خطا بلافاصله ظاهر میشود و به همین دلیل به نظر میرسد که تنظیمات هیچ تأثیری نداشتهاند.
راهحل تنها در یک کلمه است.
BEGIN IMMEDIATE;
UPDATE notes SET body = 'edited' WHERE id = 1;
COMMIT;BEGIN IMMEDIATE قفل نوشتاری را در همان ابتدا و پیش از خواندن هر چیزی دریافت میکند. در این حالت ارتقایی وجود ندارد، بنابراین بنبستی برای اجتناب ایجاد نمیشود و در نتیجه busy handler اعمال شده و اتصال به جای شکست خوردن، منتظر نوبت خود میماند. تراکنشهای فقطخواندنی را به صورت deferred نگه دارید. هر تراکنشی که شامل عملیات نوشتن است باید به صورت immediate باشد.
دلیل دوم خطاهای قفل، تشخیص دشوارتری دارد: باز نگه داشتن یک تراکنش نوشتاری در حین انجام کارهای زمانبر. SQLite نویسندهها را سریالسازی میکند، بنابراین تراکنشی که باز میشود، یک API خارجی را از طریق شبکه فراخوانی میکند و سپس commit میشود، تمام نویسندههای دیگر را به اندازه مدت زمان آن فراخوانی مسدود میکند. دادههای مورد نیاز خود را بخوانید، تراکنش را ببندید، کار زمانبر را انجام دهید و سپس یک تراکنش نوشتاری کوتاه برای ذخیره نتیجه باز کنید.
پشتیبانگیری مداوم با Litestream
کپی شبانه باعث از دست رفتن تا یک روز از دادههای نوشتهشده میشود و اجرای cp روی یک دیتابیس SQLite در حال اجرا، ممکن است منجر به ایجاد نسخهای شود که باز نمیشود. دو روش ایمن وجود دارد. sqlite3 app.db ".backup /path/to/backup.db" از رابط پشتیبانگیری آنلاین SQLite استفاده میکند و روی دیتابیسی که در حال استفاده است، کار میکند. Litestream فراتر میرود: این ابزار فایل WAL را زیر نظر میگیرد و تغییرات را بهطور مداوم به فضای ذخیرهسازی شیء (object storage) ارسال میکند؛ این کار بدترین حالت از دست رفتن داده را از یک روز به حدود یک ثانیه کاهش میدهد.
Litestream یک فایل باینری Go است که در کنار برنامه شما اجرا میشود. این ابزار بین برنامه و دیتابیس قرار نمیگیرد. برنامه شما دقیقاً مانند قبل در SQLite مینویسد و Litestream فایل WAL را میخواند و تغییرات را آپلود میکند.
cd /tmp
curl -fsSL -O https://github.com/benbjohnson/litestream/releases/download/v0.5.14/litestream-0.5.14-linux-x86_64.deb
sudo dpkg -i litestream-0.5.14-linux-x86_64.deb
litestream versionنسخه v0.5.14 همان نسخهای است که صفحه رسمی نصب لینوکس تا ژوئیه 2026 مستند کرده است و v0.5.15 در 21 ژوئیه 2026 منتشر شد. نسخه را در هر دو خط تغییر دهید تا با تگ فعلی در صفحه releases مطابقت داشته باشد و اگر VPS شما از معماری arm64 استفاده میکند، پکیج arm64 متناظر را دریافت کنید.
فایل پیکربندی در مسیر /etc/litestream.yml قرار دارد. با یک replica فایل محلی شروع کنید، زیرا این کار کل چرخه را بدون نیاز به اعتبارنامههای ابری اثبات میکند.
dbs:
- path: /home/appuser/app/app.db
replica:
type: file
path: /var/backups/litestream/appتوجه داشته باشید که فیلد مورد نظر replica است، بهصورت مفرد. Litestream 0.5 آرایه replicas از سری 0.3 را با یک بلوک replica واحد جایگزین کرد و پیکربندیای که شامل دو ورودی باشد، اکنون در زمان شروع با خطا مواجه میشود. بسیاری از راهنماهای شخص ثالث هنوز آرایه قدیمی را نشان میدهند، بنابراین بهجای اولین مثالی که در جستجو پیدا میکنید، از ساختار بالا کپیبرداری کنید. سری 0.5 همچنین زیردستور litestream wal را به litestream ltx تغییر نام داد، زیرا فرمت پشتیبانگیری روی دیسک تغییر کرده است.
پیش از فعالسازی هر چیزی، بررسی کنید که پیکربندی بهدرستی تحلیل (parse) میشود.
sudo litestream databases -config /etc/litestream.ymlسپس رفتوبرگشت داده را بهصورت دستی اثبات کنید. این فرمت از فایل پیکربندی صرفنظر کرده و یک دیتابیس را به یک مسیر کپی میکند.
mkdir -p /tmp/replica
litestream replicate ~/app/app.db file:///tmp/replica/appاین دستور در پیشزمینه اجرا شده و فعال میماند. در یک ترمینال دوم، یک ردیف بنویسید و replica را در یک فایل جدید بازیابی کنید.
sqlite3 ~/app/app.db "INSERT INTO notes (body) VALUES ('written after replication started');"
litestream restore -o /tmp/restored.db file:///tmp/replica/app
sqlite3 /tmp/restored.db "SELECT count(*) FROM notes;"شمارش شامل ردیف جدید است. اگر چنین نیست، تغییر هنوز همگامسازی نشده است: Litestream بر اساس یک sync-interval که بهصورت پیشفرض 1 ثانیه است، دادهها را ارسال میکند؛ بنابراین صبر کنید و دوباره بازیابی را انجام دهید. همان یک ثانیه، نقطه بازیابی شماست. یک کرش حداکثر باعث از دست رفتن نوشتههای آخرین بازه همگامسازی میشود و هیچ پیکربندیای این مقدار را به صفر نمیرساند.
برای ذخیرهسازی واقعی، بلوک replica را با یک URL از نوع S3 جایگزین کنید. این کار روی Amazon S3 و همچنین فضای ذخیرهسازی شیء سازگار با S3 از سایر ارائهدهندگان کار میکند.
dbs:
- path: /home/appuser/app/app.db
replica:
url: s3://your-bucket-name/app
region: us-east-1
snapshot:
interval: 24h
retention: 24hاعتبارنامهها را در آن فایل قرار ندهید. Litestream مقادیر LITESTREAM_ACCESS_KEY_ID و LITESTREAM_SECRET_ACCESS_KEY را از محیط (environment) میخواند، بنابراین آنها را در یک فایل drop-in مربوط به systemd قرار دهید که مالک آن root بوده و دسترسی آن روی 600 تنظیم شده باشد.
مقادیر snapshot بالا مقادیر پیشفرض هستند و پیشفرض نگهداری (retention) برای بسیاری تعجبآور است. Retention مشخص میکند که Litestream تا چه مدت snapshotها و فایلهای متعلق به آنها را نگه میدارد، بنابراین تعیین میکند که تا چه زمانی در گذشته میتوانید دادهها را بازیابی کنید. بیست و چهار ساعت به این معنی است که یک مهاجرت (migration) ناموفق که صبح چهارشنبه متوجه آن میشوید، از وضعیت دوشنبه قابل بازیابی نخواهد بود. مقدار retention: 168h را روی یک هفته تنظیم کنید و هزینه فضای ذخیرهسازی اضافی را بپردازید.
پیش از نیاز به بازیابی، آن را تست کنید
litestream restore -o /tmp/check.db /home/appuser/app/app.db
sqlite3 /tmp/check.db "PRAGMA integrity_check;"
sqlite3 /tmp/check.db "SELECT count(*) FROM notes;"با داشتن مسیر دیتابیس، litestream restore نسخه replica منطبق را در /etc/litestream.yml جستجو کرده و آن را دریافت میکند. PRAGMA integrity_check در صورت سلامت فایل، ok را چاپ میکند و هر خروجی دیگری به این معناست که نسخه بازیابیشده قابل استفاده نیست. این عملیات را طبق یک زمانبندی با استفاده از یک سرویس و تایمر systemd اجرا کرده و خروجی آن را بررسی کنید. تا زمانی که یک بار عملیات بازیابی را انجام نداده باشید، از صحت عملکرد بکآپ خود اطمینان نخواهید داشت.
اجرای Litestream تحت systemd
بسته Debian یک unit از نوع litestream نصب میکند که /etc/litestream.yml را میخواند.
sudo systemctl enable litestream
sudo systemctl start litestream
sudo journalctl -u litestream -fخروجی سالم، نام هر پایگاهداده را از فایل پیکربندی ذکر میکند و سپس بهجز خطوط همگامسازی دورهای، ساکت میماند. خطای no such file or directory برای مسیر پایگاهداده شما به این معناست که مسیر در فایل پیکربندی اشتباه است یا پردازش امکان خواندن آن را ندارد. این unit بهصورت پیشفرض با کاربر root اجرا میشود که دسترسی بیشتری از نیاز این وظیفه دارد. Litestream باید بتواند هم پایگاهداده و هم دایرکتوری حاوی آن را بخواند و بنویسد، زیرا با فایلهای -wal و -shm در کنار پایگاهداده شما کار میکند؛ بنابراین، دسترسی آن را به همان حسابی که برنامه شما استفاده میکند، محدود کنید.
# /etc/systemd/system/litestream.service.d/override.conf
[Service]
User=appuser
Group=appuserتغییرات را با sudo systemctl daemon-reload و sudo systemctl restart litestream اعمال کنید. راهاندازی یک حساب کاربری اختصاصی با حداقل دسترسی تنها چند دقیقه زمان میبرد و تفاوت بین یک عامل پشتیبانگیری و یک پردازش root دوم روی سرور است.
اگر زمانی قصد دارید ماشین را از صفر بازسازی کنید، یک نکته در مورد ترتیب اجرا اهمیت دارد. شما میخواهید پایگاهداده پیش از شروع برنامه بازیابی شود. دستور litestream restore پارامتر -if-db-not-exists را میپذیرد که اگر فایل از قبل موجود باشد، با کد خروج 0 پایان مییابد؛ بنابراین اجرای آن در هر بار boot ایمن است. آن را در یک خط ExecStartPre در unit برنامه خود قرار دهید تا یک VPS جدید پایگاهداده را دانلود کند، در حالی که یک VPS موجود هیچ کاری انجام نمیدهد. دستور litestream replicate نیز یک فلگ مشابه به نام -restore-if-db-not-exists دارد، اگر ترجیح میدهید همه تنظیمات را در یک مکان نگه دارید.
مواردی که SQLite در VPS با مشکل مواجه میشود
فایلسیستمهای شبکه. این محدودیتی است که نمیتوانید با پیکربندی از آن عبور کنید. حالت WAL مستلزم آن است که تمام پردازشهایی که از دیتابیس استفاده میکنند، یک ناحیه کوچک از حافظه را به اشتراک بگذارند؛ این همان کاری است که فایل -shm انجام میدهد. مستندات SQLite این قانون را بدون هیچ قید و شرطی بیان میکند:
تمام پردازشهایی که از یک دیتابیس استفاده میکنند باید روی یک کامپیوتر میزبان واحد باشند؛ WAL روی فایلسیستم شبکه کار نمیکند.
بنابراین، دیتابیسی که روی یک NFS (فایلسیستم شبکه) یا SMB share سوار (mount) شده باشد، میتواند دچار خرابی شود و هیچ pragmaای نمیتواند از آن جلوگیری کند. در اینجا تفاوتی وجود دارد که افراد اغلب نادیده میگیرند. یک دستگاه بلاک شبکه (Network block device) که اکثر ارائهدهندگان VPS بهعنوان فضای ذخیرهسازی اضافی متصل میکنند، برای لینوکس مانند یک دیسک معمولی با یک فایلسیستم معمولی به نظر میرسد و این مشکلی ندارد. اما یک اشتراک فایل (File share) سوار شده، اینطور نیست.
سرور اپلیکیشن دوم. هیچ تنظیمی باعث نمیشود این حالت کار کند. زمانی که نیاز دارید دو ماشین به دادههای یکسانی سرویسدهی کنند، به دیتابیسی نیاز دارید که از طریق شبکه ارتباط برقرار کند. تا زمانی که فرصت دارید برای این تغییر برنامهریزی کنید، در مورد آن تصمیم بگیرید.
بارهای کاری با نوشتن سنگین. امکان نوشتن توسط یک نویسنده در هر لحظه، ویژگی فرمت فایل است، نه یک پارامتر قابل تنظیم. نوشتنهای کوتاه کمهزینه هستند، زیرا هر commit یک الحاق (append) به WAL است؛ بنابراین توان عملیاتی (throughput) بیشتر از آنکه به CPU وابسته باشد، از تأخیر نوشتنهای کوچک دیسک شما پیروی میکند. برای مشاهده تفاوت این دو، به مقایسه NVMe با ذخیرهسازی SATA SSD در VPS مراجعه کنید. تراکنشهای طولانی مشکل اصلی هستند، زیرا تمام نویسندگان دیگر را در صف انتظار قرار میدهند.
پرسوجوهای تحلیلی. SQLite یک ذخیرهساز ردیفی (row store) است که برای تراکنشها ساخته شده است. داشبوردی که صد میلیون ردیف را اسکن میکند، وظیفهای متفاوت برای ابزاری متفاوت است و مقایسه DuckDB با SQLite برای کارهای سروری مشخص میکند که این مرز کجا قرار دارد.
VACUUM تحت replication. یک VACUUM کامل، کل فایل دیتابیس را بازنویسی میکند؛ این یعنی Litestream باید تمام آن را دوباره آپلود کند. مستندات Litestream توصیه میکند که در حین فعال بودن replication، این عملیات را بهصورت درجا (in-place) اجرا نکنید. replicator را متوقف کنید، vacuum را انجام دهید، دوباره آن را شروع کنید و انتظار یک snapshot کامل و تازه را داشته باشید.
دو replicator روی یک دیتابیس. هرگز دو پردازش Litestream را روی یک دیتابیس یا یک مقصد replica یکسان اجرا نکنید. مستندات بهصراحت بیان میکند که جلوگیری از این اتفاق بر عهده شماست و نتیجه آن، داشتن یک replica است که نمیتوانید آن را بازیابی کنید.
مواردی که Litestream پوشش نمیدهد
ابزار Litestream فقط از فایل دیتابیس محافظت میکند و هیچ چیز دیگری را شامل نمیشود. مدیریت فایلهای آپلود شده، پیکربندی برنامه، گواهیهای TLS (امنیت لایه انتقال) و فایلهای unit همچنان بر عهده شماست. برای پوشش کامل، آن را با پشتیبانگیری رمزنگاریشده خارج از سرور با استفاده از restic طبق یک زمانبندی مشخص ترکیب کنید. اگر از سرور جدیدی استفاده میکنید، ده دقیقه اول در یک VPS جدید شامل کارهای مربوط به حساب کاربری و فایروال است که این راهنما فرض میکند قبلاً انجام شدهاند.
FAQ
آیا SQLite برای یک برنامه در محیط production کافی است؟
برای یک برنامه روی یک سرور، بله؛ به شرطی که حالت WAL را فعال کنید، یک busy timeout تنظیم کنید و بهطور مداوم از آن نسخه پشتیبان بگیرید. محدودیتهای مهم، ساختاری هستند: امکان نوشتن توسط یک پردازش در هر لحظه و محدودیت به یک میزبان (host). برنامهای که در این محدودیتها جای میگیرد، از دیتابیسی بهرهمند میشود که نیازی به پرش شبکه یا پردازش جداگانه برای مانیتورینگ ندارد. برنامهای که در این چارچوب نمیگنجد، به یک دیتابیس کلاینت-سرور نیاز دارد و هیچ میزان بهینهسازی این موضوع را تغییر نمیدهد.
چرا با وجود تنظیم busy_timeout همچنان خطای database is locked دریافت میکنم؟
زیرا SQLite زمانی که انتظار ممکن است منجر به بنبست (deadlock) شود، از busy handler صرفنظر میکند. تراکنشی که با یک BEGIN ساده شروع میشود، به تعویق میافتد: یک SELECT اولیه، آن را در یک تراکنش خواندن قرار میدهد و عملیات نوشتن بعدی باید آن را ارتقا دهد. اگر در این فاصله اتصال دیگری عملیات نوشتن انجام داده باشد، SQLite بلافاصله SQLITE_BUSY را برمیگرداند و busy handler شما را فراخوانی نمیکند، زیرا snapshot خواندن شما قدیمی شده است. هر تراکنشی که قرار است در آن عملیات نوشتن انجام شود را با BEGIN IMMEDIATE شروع کنید تا قفل نوشتن از همان ابتدا گرفته شود و timeout اعمال گردد.
آیا میتوانم دیتابیس SQLite خود را روی فضای ذخیرهسازی شبکه نگه دارم؟
خیر، روی فایلسیستمهای شبکهای مانند NFS یا SMB امکانپذیر نیست. حالت WAL نیاز دارد که تمام پردازشها حافظه را از طریق فایل -shm به اشتراک بگذارند و مستندات SQLite تصریح میکنند که هر پردازشی که از دیتابیس استفاده میکند باید روی همان کامپیوتر میزبان باشد. یک network block device که توسط ارائهدهنده سرویس شما متصل شده، موضوع متفاوتی است: لینوکس آن را به عنوان یک دیسک معمولی با یک فایلسیستم معمولی میبیند و SQLite در آنجا کار میکند.
اگر از قبل بکآپهای شبانه دارم، آیا باز هم به Litestream نیاز دارم؟
این بستگی به این دارد که چه مقدار از دست دادن داده برای شما قابلقبول است. یک job شبانه به معنای از دست دادن حداکثر بیست و چهار ساعت از دادههای نوشتهشده است. Litestream تقریباً هر ثانیه همگامسازی را انجام میدهد، بنابراین در صورت کرش، شما تقریباً آخرین ثانیه از دادهها را از دست میدهید. این روش همچنین از کپی کردن فایل دیتابیس با cp ایمنتر است، زیرا کپی دستی ممکن است دیتابیس را در حین نوشتن ثبت کند. Litestream فقط دیتابیس را پوشش میدهد، بنابراین در کنار آن، یک بکآپ کلی از فایلها را نیز حفظ کنید.