REPACK در Postgres 19: خداحافظی با قفل کامل جدول
خلاصهٔ کاملتر
پستگرس هیچوقت راه خوبی برای جمعوجور کردن جدولهای بادکرده نداشت. VACUUM فضای مرده رو شناسایی میکنه ولی به سیستمعامل برنمیگردونه؛ VACUUM FULL و CLUSTER این کارو میکنن ولی کل مدت بازنویسی، جدول رو قفل میکنن. برای همین یه دههست اپراتورها مجبور بودن سراغ اکستنشنهایی مثل pg_repack و pg_squeeze برن. Postgres 19 این قابلیتو با REPACK وارد هسته میکنه، با یه حالت آنلاین که همزمان با بازنویسی، خوندن و نوشتن رو هم قبول میکنه.
سه حالت برای REPACK هست: بدون ترتیب (شبیه VACUUM FULL)، مرتبشده بر اساس یه ایندکس (شبیه CLUSTER)، و حالت آنلاین (CONCURRENTLY) که همزمان با بازنویسی هم خوندن و نوشتن رو قبول میکنه:
REPACK t; -- unordered, like VACUUM FULL
REPACK t USING INDEX t_pkey; -- ordered by an index, like CLUSTER
REPACK (CONCURRENTLY) t; -- online: reads and writes continueهر سه حالت فضای مرده رو پس میگیرن؛ تو تست نویسنده یه جدول ۱۸۸۴ مگابایتی با REPACK معمولی به ۱۰۸۵ مگابایت رسید و با حالت آنلاین به ۱۰۸۶ مگابایت، با این تفاوت که تو حالت آنلاین جدول فقط لحظهی سواپ نهایی قفل میشه.
REPACK (CONCURRENTLY) در واقع همون pg_squeezeست که وارد هستهی Postgres شده؛ طراحی و بیشتر کدش هم کار آنتونین هوسکاست، همون کسی که pg_squeeze رو ساخته. با یه قفل سبک (SHARE UPDATE EXCLUSIVE) شروع میکنه که خوندن و نوشتن رو آزاد میذاره، یه اسنپشات از جدول میگیره، یه کپی جدید از روش میسازه، و نوشتههای تازه رو از WAL دیکد میکنه تا کپی آماده بشه. فقط مرحلهی آخر - یعنی جایگزینی فایلها - به قفل کامل (ACCESS EXCLUSIVE) نیاز داره؛ زمانش هم به حجم ترافیک نوشتاری بستگی داره، نه به اندازهی جدول.
این حالت چندتا محدودیت هم داره: به wal_level روی replica یا بالاتر نیاز داره و رو جدولهای unlogged یا بدون primary key کار نمیکنه. چون کپی قدیم و جدید همزمان رو دیسک میمونن، به فضای اضافه نیاز داری؛ از نظر MVCC هم امن نیست، چون بعد از کامیت شدن سواپ، جدول برای تراکنشهایی که اسنپشات قدیمیتر دارن خالی به نظر میرسه. اگه یه تراکنش دیگه همزمان قفل جدول رو نگه داشته باشه، ممکنه پستگرس برای شکستن بنبست خود REPACK رو لغو کنه و کل بازنویسی رولبک بشه.
تغییر بزرگ دیگه خاموش شدن JIT بهصورت پیشفرضه؛ از Postgres 12 به بعد همیشه روشن بود. JIT برای کوئریهای تحلیلی سنگین که میلیونها ردیف رو پردازش میکنن واقعاً کمک میکنه، ولی پستگرس همیشه نمیتونه از قبل تشخیص بده کدوم کوئریها اونجورین. چون تصمیم فقط بر اساس هزینهی تخمینی کوئریه، حتی چندتا ردیف اضافه هم میتونه یه کوئری سریع و کوچیک رو بیخودی وارد مسیر کامپایل JIT کنه و کندش کنه - مخصوصاً تو کوئریهای OLTP که تخمینشون نزدیک آستانهست.
برنامهریز کوئری هم چندتا بهبود گرفته که مهمترینش eager aggregationه: وقتی هر ردیف order فقط به یه customer وصله، پستگرس میتونه جمعها رو قبل از join انجام بده، نه بعدش:
SELECT c.region, sum(o.total)
FROM orders o
JOIN customers c ON c.id = o.customer_id
GROUP BY c.region;اینجوری مثلاً ۲۰۰هزار ردیف order اول به هزار جمع جزئی برای هر مشتری تبدیل میشن و join فقط با هزار ردیف انجام میشه. این رفتار پیشفرض فعاله ولی فقط وقتی به کار میآد که پیشبینی بشه تعداد ردیفها حداقل ۸ برابر کم میشه. NOT IN هم وقتی مطمئن باشه NULL توش نیست، حالا بهجای روش قدیمی، به شکل anti-join اجرا میشه که رو جدولهای بزرگ خیلی سریعتره.
نکات کلیدی:
- REPACK جایگزین pg_repack و pg_squeeze میشه و با حالت CONCURRENTLY تقریباً بدون قفل جدول رو جمعوجور میکنه
- JIT از این به بعد پیشفرض خاموشه و باید دستی روشنش کنی
- eager aggregation و بهبود NOT IN باعث سریعتر شدن بعضی کوئریها میشه
- تغییرات کوچیکتر دیگه: فشردهسازی پیشفرض TOAST با lz4، لاگ شدن پیشفرض انتظارهای قفل، سریعتر شدن اینسرتهای فارینکی، عریضتر شدن MultiXact به ۶۴ بیت، و اضافه شدن autovacuum موازی (پیشفرض خاموش)
- Postgres 19 الان تو مرحلهی Beta 2ه و انتشار نهاییش سپتامبر یا اکتبر همین امسال (۲۰۲۶) پیشبینی میشه




