چرا حذف ردیف فوراً فایل را کوچک نمی کند؟
در PostgreSQL دیدن افزایش اندازه جدول پس از موجی از update یا delete لزوماً نشانه خرابی نیست. مدل کنترل هم زمانی چند نسخه ای یا MVCC اجازه می دهد تراکنش های مختلف، نسخه های مناسب خود را از داده ببینند. این ویژگی به خواندن و نوشتن هم زمان کمک می کند، اما هزینه نگه داری دارد: نسخه های قدیمی همیشه بلافاصله قابل حذف نیستند. VACUUM بخشی از سازوکار مدیریت همین چرخه است. برای فهم عملکرد و رشد ذخیره، باید عمر نسخه ها و عمر تراکنش ها را کنار هم دید.
یک جدول سفارش را در نظر بگیرید که وضعیت هر سفارش چند بار تغییر می کند: ثبت شده، تأیید شده، آماده و ارسال شده. از دید برنامه ممکن است هر بار همان ردیف عوض شود، ولی موتور باید امکان دیدن نسخه مناسب را برای تراکنش های هم زمان حفظ کند. بنابراین تعداد ردیف های منطقی تنها عامل اندازه فیزیکی نیست. بسامد تغییر، طول تراکنش ها و توان نگه داری پس زمینه نیز اثر دارند. پایش صرف تعداد سفارش ها برای توضیح رشد دیسک کافی نخواهد بود.
snapshot با قفل ردیف یک مفهوم نیست
snapshot تعیین می کند یک تراکنش یا دستور بر اساس سطح isolation چه داده ای را قابل مشاهده می داند. این مفهوم را نباید با گرفتن قفل روی همه ردیف های خوانده شده یکی گرفت. PostgreSQL می تواند خواننده را از نسخه مناسب پاسخ دهد، در حالی که نویسنده نسخه دیگری ایجاد می کند. البته تعارض های نوشتن و قفل های لازم همچنان وجود دارند. MVCC به معنی حذف همه انتظارها نیست؛ فقط مدل مشاهده داده را از تصور ساده یک نسخه مشترک جدا می کند.
برای یک گزارش مدیریتی، انتخاب سطح isolation باید با نیاز سازگاری گزارش هماهنگ باشد. چند SELECT مستقل در حالت معمول ممکن است وضعیت های مربوط به لحظه های متفاوت را ببینند. اگر قرار است جمع و جزئیات حتماً از یک تصویر باشند، قرارداد تراکنش را دقیق تعیین کنید. در مقابل، نگه داشتن همان تصویر برای مدت بسیار طولانی هزینه نگه داری ایجاد می کند. مسئله طراحی این است که چه مقدار سازگاری و برای چه مدت واقعاً لازم است، نه اینکه همیشه قوی ترین یا طولانی ترین تراکنش انتخاب شود.
نسخه مرده چه زمانی واقعاً قابل جمع آوری است؟
نسخه ای که دیگر برای وضعیت جاری استفاده نمی شود ممکن است هنوز برای یک تراکنش قدیمی لازم باشد. تا زمانی که چنین مصرف کننده ای وجود دارد، جمع آوری بی قید آن درست نیست. به همین دلیل یک نشست فراموش شده با تراکنش باز می تواند پیامدی فراتر از همان کاربر داشته باشد. برنامه ای که پس از خواندن داده منتظر تعامل انسان می ماند، ممکن است افق پاک سازی را عقب نگه دارد. این اثر در جدول پر تغییر به تدریج محسوس می شود.
برای تشخیص، طول عمر تراکنش و حالت نشست ها را با رشد نسخه های مرده مقایسه کنید. idle بودن اتصال به معنی بی اثر بودن آن نیست؛ حالت idle in transaction اهمیت خاصی دارد. قبل از قطع نشست در تولید، مالک و کار آن را شناسایی کنید، چون شاید عملیات مهمی باشد. راه حل پایدار اصلاح مرز تراکنش در برنامه است. قطع موردی نشست می تواند علائم را کم کند، اما اگر همان الگو در هر درخواست تکرار شود، مسئله دوباره برخواهد گشت.
VACUUM عادی چه کاری می کند؟
VACUUM عادی فضای نسخه های قابل جمع آوری را برای استفاده دوباره آماده می کند و کارهای نگه داری مرتبط را انجام می دهد. در بیشتر موارد انتظار نداریم پس از آن فایل جدول فوراً به همان نسبت کوچک شود و فضا به سیستم عامل برگردد. این نکته برای تفسیر نتیجه مهم است: ثابت ماندن اندازه فایل الزاماً به معنی بی اثر بودن VACUUM نیست. فضای آزاد داخلی می تواند برای نوشتن های بعدی استفاده شود و از رشد بیشتر جلوگیری کند.
اگر جدول سفارش در یک چرخه عادی به اندازه پایداری رسیده و فضای داخلی دوباره استفاده می شود، تلاش دائمی برای کوچک کردن فایل شاید هزینه بی دلیل ایجاد کند. باید روند چند روزه یا چند چرخه کاری را ببینید. رشد مداوم با اندازه منطقی ثابت مسئله متفاوتی از اندازه ثابت پس از یک اوج تاریخی است. هدف نگه داری، پایداری و کارایی است؛ همیشه کوچک ترین فایل ممکن هدف مناسبی نیست. بازنویسی جدول نیز خود به فضا، زمان و قفل نیاز دارد.
VACUUM FULL درمان روزمره نیست
VACUUM FULL جدول را بازنویسی می کند و می تواند فضای بلااستفاده را به سیستم عامل برگرداند، اما با هزینه و قفل متفاوتی همراه است. قفل ACCESS EXCLUSIVE آن، خواندن و نوشتن همزمان روی جدول را مسدود میکند. استفاده از آن به عنوان واکنش خودکار به هر رشد دیسک می تواند خدمت را مختل کند. پیش از چنین عملیاتی، میزان فضای موقت لازم، مدت اجرا، اثر بر query ها و پنجره نگه داری را بررسی کنید. همچنین باید علت رشد مشخص باشد؛ در غیر این صورت پس از بازنویسی، همان الگو دوباره فضا را مصرف خواهد کرد.
در یک محیط آزمایش با اندازه نزدیک به تولید، عملیات را اجرا و زمان و قفل ها را مشاهده کنید. اگر فضای کافی برای بازنویسی ندارید، شروع کار در لحظه بحران می تواند وضعیت را بدتر کند. راهکارهای جایگزین و شیوه نگه داری باید با نسخه و نیاز سرویس بررسی شوند. مهم این است که تفاوت میان استفاده دوباره داخلی از فضا و بازگرداندن فضا به سیستم عامل در تصمیم تیم روشن باشد. این دو هدف گاهی با ابزارهای متفاوت دنبال می شوند.
autovacuum یک خدمت ضروری است
autovacuum کار نگه داری را بر اساس وضعیت جدول ها و تنظیمات انجام می دهد. خاموش کردن آن برای کاهش موقت مصرف منابع، بدهی عملیاتی ایجاد می کند و می تواند پیامدهای جدی داشته باشد. اگر فعالیت آن زیاد به نظر می رسد، ابتدا علت تغییر داده، توان دیسک و تنظیمات متناسب با جدول ها را بررسی کنید. یک جدول کوچک و بسیار پرتغییر با یک جدول بزرگ و تقریباً ثابت نیاز یکسانی ندارد. تنظیم سراسری واحد همیشه بهترین پاسخ برای همه جدول ها نیست.
برای یک جدول صف که ردیف هایش مرتب درج و حذف می شوند، آستانه مبتنی بر نسبت ممکن است با انتظار شما از زمان نگه داری هماهنگ نباشد. در چنین حالتی تنظیمات سطح جدول و رفتار برنامه باید بررسی شود. اما تغییر پارامتر بدون معیار موفقیت ارزش کمی دارد. پیش از تغییر بنویسید چه می خواهید: کاهش نسخه مرده، جلوگیری از رشد فایل یا کاهش تأخیر query. سپس اثر تغییر را بر مصرف I/O و کارایی برنامه هم بسنجید تا مشکل فقط جابه جا نشده باشد.
ANALYZE با پاک سازی نسخه ها فرق دارد
برنامه ریز query برای انتخاب روش اجرا به آمار داده نیاز دارد. ANALYZE این آمار را به روز می کند، در حالی که VACUUM هدف اصلی دیگری دارد. اجرای ترکیبی آن ها رایج است، اما باید نقش ها جدا فهمیده شوند. یک query کند پس از تغییر بزرگ توزیع داده ممکن است بیشتر به آمار مربوط باشد تا فضای مرده. برعکس، به روز کردن آمار به تنهایی نسخه های قدیمی را جمع آوری نمی کند. تشخیص درست از انتخاب ابزار اشتباه جلوگیری می کند.
برای مثال اگر یک مشتری ناگهان بخش عمده سفارش ها را تولید کرده باشد، فرض های قبلی برنامه ریز درباره توزیع مقادیر ممکن است مناسب نباشد. برنامه اجرا و تفاوت تعداد ردیف تخمینی با واقعی را بررسی کنید. این مشاهده در کنار وضعیت نگه داری جدول معنی پیدا می کند. افزایش حافظه یا ساخت index جدید پیش از فهمیدن خطای تخمین شاید هزینه اضافی بسازد. هر مشکل عملکردی باید با شواهد همان query بررسی شود، نه با نسخه ثابت «ابتدا همه چیز را vacuum کنید».
freeze و افق شناسه تراکنش
شناسه تراکنش در PostgreSQL محدودیت چرخه دارد و نگه داری مربوط به freeze برای حفظ تفسیر صحیح قدمت داده مهم است. این بخش فقط یک بهینه سازی اختیاری نیست. بنابراین پایش عمر شناسه تراکنش و فعالیت های لازم نگه داری باید در برنامه عملیاتی باشد. عدد دقیق آستانه ها و گزینه ها را از مستندات نسخه نصب شده بگیرید. مقاله مفهومی نباید جای راهنمای همان نسخه و تنظیمات واقعی پایگاه داده را بگیرد.
برای تیم عملیات، مهم است هشدار این حوزه صاحب و اقدام مشخص داشته باشد. اگر هشدار فقط به یک کانال شلوغ ارسال شود و کسی نداند چه چیز باید بررسی شود، پایش ناقص است. رویه باید شامل شناسایی جدول یا پایگاه متاثر، تراکنش های طولانی و منابع محدودکننده باشد. تغییر عجولانه آستانه برای ساکت کردن هشدار، علت را حل نمی کند. ظرفیت نگه داری بخشی از ظرفیت پایگاه داده است و باید مانند ظرفیت پردازش درخواست های کاربر برنامه ریزی شود.
replication و کارهای طولانی را هم ببینیم
فقط نشست های عادی برنامه بر افق نگه داری اثر ندارند. برخی تنظیمات و اجزای replication و مصرف کننده های داده نیز می توانند نگه داری نسخه ها یا WAL را تحت تأثیر قرار دهند. باید میان نگه داری نسخه ردیف و نگه داری فایل WAL تفاوت گذاشت؛ هر دو ممکن است دیسک را بزرگ کنند، ولی علت و ابزار بررسی یکسان نیست. نگاه کردن به مجموع مصرف پوشه داده بدون تفکیک محل رشد می تواند تشخیص را منحرف کند.
در بررسی رخداد، ابتدا مشخص کنید رشد مربوط به فایل جدول، index یا WAL است. سپس به سراغ تراکنش ها، slot ها یا عملیات مناسب همان مسیر بروید. یک گزارش طولانی روی replica نیز بسته به تنظیمات ممکن است با تصمیم های نگه داری primary ارتباط داشته باشد. این موضوع باید در طراحی گزارش گیری دیده شود. انتقال query به replica همیشه به معنی حذف همه اثرهای آن از primary نیست؛ قرارداد consistency و تنظیمات بازخورد باید دقیق بررسی شوند.
آزمایش کوچک برای فهم رفتار
یک جدول ساختگی ایجاد کنید و اندازه و تعداد ردیف را ثبت کنید. در یک نشست تراکنش خواندن با snapshot مناسب باز نگه دارید و در نشست دیگر موجی از تغییرها انجام دهید. سپس رفتار پاک سازی را پیش و پس از پایان نشست اول مشاهده کنید. هدف آزمایش دیدن رابطه عمر snapshot و امکان جمع آوری نسخه هاست. روی داده تولید چنین تجربه ای انجام ندهید؛ یک محیط محدود و قابل حذف برای یادگیری کافی است و نتیجه را روشن تر می کند.
در مرحله بعد، همان جدول را پس از VACUUM عادی دوباره پر کنید و ببینید آیا فضای داخلی استفاده می شود. این تجربه تفاوت فضای آزاد داخل فایل و فضای آزاد سیستم عامل را ملموس می کند. اندازه گیری را با آمار و توضیح نسخه موتور همراه کنید. اگر نتیجه با انتظار متفاوت بود، پیش از تعمیم، عوامل دیگر مانند index، اندازه ردیف و رفتار update را بررسی کنید. آزمایش خوب یک فرضیه محدود را می سنجد و از آن نتیجه بیش از حد کلی نمی گیرد.
قرارداد نگه داری برای برنامه
تیم توسعه باید تراکنش ها را کوتاه و مرز آن ها را روشن نگه دارد، و تیم عملیات باید توان autovacuum و روند رشد را ببیند. هیچ کدام به تنهایی کافی نیست. یک برنامه با تراکنش های فراموش شده می تواند بهترین تنظیمات را تحت فشار بگذارد و یک سامانه بدون ظرفیت نگه داری نیز از بار عادی عقب می ماند. شاخص های مرتبط باید با استقرارهای برنامه مقایسه شوند تا تغییر الگوی داده زود دیده شود.
MVCC امکان مهمی برای هم زمانی فراهم می کند، اما نسخه های قدیمی باید چرخه عمر مدیریت شده داشته باشند. وقتی این چرخه را بفهمیم، VACUUM از یک دستور مرموز برای «سبک کردن دیتابیس» به بخشی روشن از عملیات تبدیل می شود. نتیجه مطلوب، داده صحیح، اندازه پایدار و زمان پاسخ قابل پیش بینی است. برای رسیدن به آن، عمر تراکنش، نرخ تغییر و ظرفیت نگه داری را با هم اندازه بگیرید و هر اقدام را به علت مشخص وصل کنید.