رفتن به محتوا
IROSG.
مقاله‌ها

SQLite WAL؛ خواننده، نویسنده و Checkpoint چگونه کنار هم کار می‌کنند؟

WAL در SQLite را با مثال تراکنش، قفل، خواننده طولانی و پشتیبان‌گیری سازگار بررسی می‌کنیم تا مرز واقعی هم‌زمانی روشن شود.

نشان رسمی SQLite در مستندات پروژه

چرا WAL فقط یک گزینه برای سرعت نیست؟

وقتی یک برنامه کوچک وب از SQLite استفاده می کند، اولین مسئله معمولاً ظرفیت فایل نیست. مسئله این است که درخواست های خواندن، ثبت فرم و گزارش گیری هم زمان به یک پایگاه داده می رسند. توسعه دهنده ممکن است با دیدن خطای قفل تصور کند باید فوراً به یک سرور پایگاه داده مهاجرت کند. تصمیم درست به الگوی کار بستگی دارد. اگر تراکنش های نوشتن کوتاه باشند و بیشتر درخواست ها فقط بخوانند، شناخت رفتار WAL می تواند به طراحی ساده تر و قابل پیش بینی تر کمک کند. این مقاله درباره همین رفتار است، نه وعده افزایش نامحدود تعداد نویسنده ها.

در مدل Write Ahead Logging، تغییر صفحه ها ابتدا در فایل ثبت تغییرات قرار می گیرد. فایل اصلی پایگاه داده بعدها در عملیات checkpoint به روز می شود. بنابراین لحظه تأیید تراکنش و لحظه انتقال صفحه ها به فایل اصلی الزاماً یکی نیست. خواننده می تواند تصویری سازگار از وضعیت قبلی ببیند، در حالی که نویسنده مشغول افزودن تغییر تازه است. اما نوشتن همچنان سریالی می شود: WAL چند نویسنده هم زمان و مستقل را به فایل واحد اضافه نمی کند. این تفاوت کوچک، علت بسیاری از تصمیم های درست یا نادرست درباره SQLite است.

تصویر ثابت خواننده چه معنایی دارد؟

فرض کنید یک سامانه رزرو، فهرست صندلی ها را برای گزارش مدیر می خواند. هم زمان یک مشتری آخرین صندلی را رزرو می کند. اگر گزارش در یک تراکنش خواندن باز باقی بماند، ادامه گزارش باید با همان تصویر اولیه سازگار بماند. در غیر این صورت ممکن است جمع تعداد صندلی ها با جزئیات گزارش متفاوت شود. خواندن از snapshot به معنی دریافت تازه ترین داده در هر لحظه نیست؛ به معنی داشتن یک تصویر داخلی سازگار در چارچوب آن تراکنش است. برای دیدن تغییرات جدید باید مرز تراکنش بعدی را آگاهانه انتخاب کرد.

از این موضوع می توان یک قاعده طراحی استخراج کرد: تراکنش خواندن را به زمان تعامل انسان گره نزنید. باز کردن صفحه مدیریتی نباید یک تراکنش را تا زمانی که مدیر پنجره را می بندد زنده نگه دارد. داده مورد نیاز را بخوانید، نتیجه را به ساختاری محدود تبدیل کنید و تراکنش را ببندید. اگر بعداً مدیر عملی انجام داد، آن عمل با تراکنش جدید و بررسی دوباره شرایط اجرا شود. این رویکرد هم عمر snapshot را کاهش می دهد و هم مانع تصمیم گیری بر اساس فرض های منقضی می شود.

نویسنده واحد چگونه به گلوگاه تبدیل می شود؟

دو درخواست می توانند در زمان نزدیک به هم بخواهند بنویسند، اما یکی باید منتظر دیگری بماند. مدت انتظار به زمان نگه داشتن قفل بستگی دارد، نه فقط تعداد رکوردها. اگر برنامه داخل تراکنش با یک سرویس پرداخت تماس بگیرد، کندی شبکه مستقیماً به انتظار سایر نویسنده ها تبدیل می شود. این طراحی حتی با چند تغییر کوچک در پایگاه داده نیز می تواند مشکل ساز باشد. بهتر است عملیات خارجی و آماده سازی داده پیش از شروع تراکنش انجام شود و فقط تصمیم نهایی و تغییرات لازم در بخش اتمیک باقی بماند.

کوتاه کردن تراکنش به معنای شکستن دلخواه یک عملیات تجاری نیست. اگر ثبت سفارش و کم شدن موجودی باید با هم رخ دهند، جدا کردن آن ها ممکن است داده ناسازگار بسازد. ابتدا مرز صحیح اتمیک بودن را تعیین کنید، سپس کارهای غیرضروری را از آن بیرون ببرید. برای مثال اعتبارسنجی ساختاری ورودی، ساخت متن پیام و محاسبه مقادیری که به وضعیت جاری وابسته نیستند می توانند بیرون تراکنش انجام شوند. بررسی موجودی و ثبت سفارش باید همچنان بر اساس یک تصمیم سازگار صورت گیرد.

checkpoint چه کاری انجام می دهد؟

وجود فایل WAL به معنی خرابی یا باقی ماندن فایل موقت بی مصرف نیست. بخشی از داده معتبر ممکن است هنوز فقط در همین فایل باشد. checkpoint صفحه های مناسب را از ثبت تغییرات به فایل اصلی منتقل می کند. خواننده طولانی ممکن است مانع پیشروی کامل این عملیات شود، چون هنوز به نسخه ای قدیمی از صفحه ها احتیاج دارد. در نتیجه افزایش اندازه WAL همیشه نشانه افزایش نرخ نوشتن نیست؛ گاهی نشانه تراکنش خواندن فراموش شده است. پاک کردن دستی فایل برای آزاد کردن فضا پاسخ درستی به این وضعیت نیست.

برای تشخیص، هم زمان چند نشانه را بررسی کنید: اندازه فایل، مدت تراکنش های خواندن، تعداد خطاهای busy و زمان تأیید نوشتن. اگر رشد فایل با اجرای گزارش طولانی شروع می شود و پس از پایان گزارش متوقف می گردد، مسیر بررسی مشخص تر است. اگر فایل رشد می کند ولی هیچ گزارش طولانی دیده نمی شود، connection های باز، cursor های مصرف نشده و رفتار کتابخانه دسترسی داده را بررسی کنید. نام متغیرها در برنامه کافی نیست؛ آنچه اهمیت دارد عمر واقعی تراکنش در موتور پایگاه داده است.

انتظار، تلاش دوباره و اتمیک بودن

تنظیم زمان انتظار می تواند برخوردهای کوتاه را جذب کند، اما درمان تراکنش چند دقیقه ای نیست. انتظار بسیار بلند نیز ممکن است کاربر را در صفی پنهان نگه دارد و ظرفیت worker های وب را مصرف کند. برای هر مسیر عملیاتی باید زمان پاسخ مطلوب و رفتار شکست روشن باشد. درخواست ثبت یک تنظیم مدیریتی با ورود انبوه داده یکسان نیست. اولی شاید باید سریع شکست بخورد و پیام قابل فهم بدهد، در حالی که دومی می تواند در صف کار پس زمینه با کنترل نرخ اجرا شود.

تلاش دوباره باید کل واحد منطقی کار را در نظر بگیرد. اگر بخشی از عملیات خارج از پایگاه داده انجام شده باشد، تکرار کورکورانه ممکن است ایمیل یا پرداخت تکراری ایجاد کند. شناسه یکتای درخواست و ثبت وضعیت پردازش می تواند این مسئله را کنترل کند، اما باید بر اساس نیاز واقعی برنامه طراحی شود. در آزمایش، عمداً دو درخواست هم زمان برای یک موجودی محدود بفرستید و بررسی کنید نتیجه فقط یکی از حالت های مجاز تجاری باشد. نبودن خطا در log به تنهایی صحت نتیجه را ثابت نمی کند.

پشتیبان گیری از یک فایل فعال

ظاهر تک فایلی SQLite گاهی باعث اعتماد بیش از حد به کپی ساده می شود. وقتی پایگاه داده فعال است و WAL دارد، کپی کردن فقط فایل اصلی ممکن است آخرین تراکنش های تأیید شده را شامل نشود. برای نسخه سازگار از API پشتیبان گیری یا روش مستند متناسب با برنامه استفاده کنید. اگر برنامه را متوقف می کنید، توقف باید واقعی و شامل همه نویسنده ها باشد. وجود یک worker پس زمینه یا وظیفه زمان بندی شده می تواند فرض توقف کامل را باطل کند.

آزمون بازیابی باید از فایل جداگانه و در مسیر جدا انجام شود. ابتدا قابلیت باز شدن پایگاه داده و سپس قواعد تجاری مهم را بررسی کنید. برای سامانه سفارش، شمارش رکوردها کافی نیست؛ ارتباط سفارش با اقلام، وضعیت پرداخت و مانده موجودی نیز اهمیت دارد. زمان تهیه نسخه و زمان بازیابی را ثبت کنید تا بدانید در یک رخداد واقعی چه مقدار داده و چه مدت خدمت ممکن است از دست برود. پشتیبان معتبر یک نتیجه قابل آزمایش است، نه صرفاً فایلی که نام تاریخ روی آن نوشته شده است.

فایل سیستم و مرز میزبان

WAL معمول SQLite برای هماهنگی connection ها به سازوکارهای محلی میزبان وابسته است و قرار دادن فایل روی یک اشتراک شبکه راهی برای ساخت سرور چند میزبان محسوب نمی شود. اگر چند ماشین باید هم زمان بنویسند، معماری را دوباره بررسی کنید. ممکن است یک سرویس مالک فایل باشد و بقیه از API آن استفاده کنند؛ ممکن است نیاز به موتور client server داشته باشید. انتخاب بین این گزینه ها باید بر اساس مرز خرابی، تعداد نویسنده ها و نیاز دسترس پذیری انجام شود، نه صرفاً راحتی mount کردن یک پوشه مشترک.

همچنین مجوز پوشه اهمیت دارد، چون موتور فقط با فایل اصلی کار نمی کند و ممکن است فایل های جانبی ایجاد کند. خطای دسترسی را با باز کردن عمومی همه مجوزها رفع نکنید. کاربر اجرایی برنامه، مالکیت مسیر و امکان ایجاد فایل در همان پوشه را بررسی کنید. اگر برنامه داخل کانتینر اجرا می شود، مسیر واقعی volume و هویت کاربر داخل و بیرون کانتینر را ثبت کنید. این اطلاعات هنگام جابه جایی سرویس یا بازیابی روی میزبان جدید ارزش عملی بیشتری از یک دستور نصب کوتاه دارند.

یک آزمایش کوچک اما معنادار

برای ارزیابی، یک نسخه آزمایشی با داده ساختگی بسازید. نخست یک خواننده طولانی را باز نگه دارید، سپس چند نوشتن کوتاه انجام دهید و رشد WAL را مشاهده کنید. بعد خواننده را ببندید و رفتار checkpoint را بررسی کنید. در آزمایش دوم، یک نویسنده را عمداً معطل کنید و زمان پاسخ نویسنده دیگر را بسنجید. در آزمایش سوم، برنامه را در نقطه کنترل شده متوقف و دوباره اجرا کنید و صحت داده تأیید شده را بسنجید. این سه تجربه رابطه قفل، snapshot و دوام را ملموس می کنند. این آزمون فقط بازیابی پس از توقف فرایند را می‌سنجد؛ دوام در قطع برق به PRAGMA synchronous و صحت ذخیره‌سازی وابسته است. در WAL، NORMAL ممکن است تراکنش تأییدشده را پس از قطع برق از دست بدهد؛ FULL در هر commit همگام‌سازی می‌کند.

نتیجه آزمایش را با شرایط واقعی مقایسه کنید. تعداد worker، نسخه کتابخانه SQLite، نوع دیسک و تنظیمات اتصال باید ثبت شوند. ممکن است زبان برنامه نویسی از نسخه ای متفاوت با ابزار خط فرمان استفاده کند؛ بنابراین خروجی نسخه shell به تنهایی کافی نیست. برای تصمیم تولید، نسخه امنیتی پشتیبانی شده و یادداشت های انتشار رسمی را هم بررسی کنید. مستندات WAL در طول زمان اصلاح می شوند و نباید یک مقاله آموزشی را جایگزین بررسی نسخه نصب شده و اصلاحیه های مربوط به همان نسخه کرد.

چه زمانی تغییر معماری منطقی است؟

اگر بیشتر زمان پاسخ صرف انتظار نویسنده واحد می شود، ابتدا طول تراکنش ها و الگوی ورود داده را اصلاح کنید. اگر پس از اصلاح هنوز چند نویسنده مستقل با بار پایدار و سنگین لازم است، مهاجرت می تواند تصمیم مناسبی باشد. نیاز به replication بین میزبان ها، مدیریت متمرکز دسترسی یا عملیات پیچیده نگه داری نیز ممکن است انتخاب را تغییر دهد. در مقابل، صرف وجود چند کاربر یا بزرگ شدن فایل دلیل کافی برای کنار گذاشتن SQLite نیست. تصمیم خوب بر شواهد اندازه گیری شده تکیه دارد و هزینه عملیاتی جایگزین را نیز حساب می کند.

برای یک سرویس کوچک، قرارداد عملیاتی روشن اغلب مهم تر از افزایش تعداد تنظیمات است: تراکنش کوتاه، مالک مشخص فایل، پشتیبان قابل بازیابی، کنترل نسخه و پایش زمان انتظار. برای یک سرویس بزرگ تر، همین اصول در مهاجرت نیز حفظ می شوند. WAL ابزار جداسازی مسیر خواندن و نوشتن در یک معماری مشخص است. وقتی نقش آن را دقیق بشناسید، هم از استفاده بیش از ظرفیت جلوگیری می کنید و هم یک راه حل ساده و مناسب را زودتر از موعد پیچیده نخواهید کرد.

منابع رسمی

TRACEABLE KNOWLEDGE

منابع و مطالعهٔ بیشتر

01
SQLite: Write Ahead Logginghttps://www3.sqlite.org/wal.html
02
SQLite: Isolationhttps://www3.sqlite.org/isolation.html
03
SQLite: Online Backup APIhttps://www3.sqlite.org/backup.html

آخرین بررسی تحریریه:

IROSG / EDITORIAL

خبرها و معرفی مشاهیر با نگارش مستقل و ذکر منبع تهیه شده‌اند. اعتبار و مجوز تصاویر در بخش منابع همین صفحه نگه‌داری می‌شود تا متن زیر عکس‌ها شلوغ نشود. داده‌های مرجع CVE با اعتباردهی به منابع اصلی ارائه می‌شوند.

سیاست تألیف و حقوق تصاویر ↗

ادامهٔ کشف.

مقاله‌ها ↗