تنها DELETE مقیاسپذیر تو Postgres، در واقع DROP TABLE هست
خلاصهٔ کاملتر
ادعای اصلی این مقاله از تیم PlanetScale اینه که مقیاسپذیرترین راههای حذف داده تو Postgres حول حذف کل جدول میچرخن. حذف تکی یه ردیف تو مقیاس کوچیک مشکلی نداره، ولی DELETEهای دستهای بزرگ فوری فضای فیزیکی دیسک رو آزاد نمیکنن، سربار نوشتن و replication اضافه میکنن و در نهایت برای پاکسازی انبوه ردیف خوب نیستن.
به گفتهی نویسنده دلیلش طراحی MVCC ـه (کنترل همزمانی چندنسخهای). Postgres ردیفهای تغییریافته و حذفشده رو کنار ردیفهای فعلی نگه میداره و با تکیه بر transaction id ها و visibility map از روی «dead tuple»ها رد میشه. بعداً فرایند vacuum میآد و میگه این بایتها تو این heap page آزادن و میشه روشون بازنویسی کرد. مهمه بدونی که خود DELETE و حتی autovacuum معمولاً فضا رو به سیستمعامل برنمیگردونن؛ فقط میگن این صفحهها قابل بازنویسی شدن. این یه انتخاب عمدیه چون پسدادن فضا و دوباره گرفتنش گرونه. VACUUM FULL این کارو میکنه ولی یه قفل سنگین و طولانی میگیره.
نویسنده اضافه میکنه که DELETE ها هم باید کامل replicate بشن و خودشون نوعی نوشتن حساب میشن، پس حذفهای بزرگ میتونن نویسندههای دیگهی برنامه رو معطل کنن. یه نکتهی ظریف دیگه اینم هست که دادهی index موقع DELETE اصلاً دست نمیخوره و خوانندهها باید موقع خوندن index خودشون تشخیص بدن که این tuple مرده. در کل بهقول نویسنده DELETE بیشتر «کارِ اضافهشده»ست تا «کارِ انجامشده». حواست هم باشه که foreign key و CASCADE میتونن یه حذف تکردیفی رو به حذف گیگابایتها داده تبدیل کنن.
در مقابل، DROP TABLE و TRUNCATE یه AccessExclusiveLock سنگین میگیرن ولی تقریباً مستقل از حجم دادهان. تو لایهی فیزیکی فایلها رو مستقیم از سیستمعامل پاک میکنن و buffer cache رو هم جارو میکنن. این جارو فقط یه سوییپ متادیتاست: Postgres برای هر بافر ۸ کیلوبایتی یه هدر کوچیک ثابت ۶۴ بایتی (BufferDesc) نگه میداره، یعنی فقط ۱/۱۲۸ حجم کش رو پشتسرهم میخونه که روی سختافزار امروزی خیلی سریعه. این دوتا صفر dead tuple، صفر بدهی vacuum و صفر کار برای خوانندهها تولید میکنن و فضا رو فوری به سیستمعامل برمیگردونن.
برای یه حذف یکباره مثلاً وقتی یه باگ جدول رو پر از آشغال کرده، نویسنده روشی رو که خودشون بهکار بردن توضیح میده: تو یه تراکنش، جدول رو با ACCESS EXCLUSIVE قفل میکنی، دادهی موردنظر برای نگهداشتن رو با یه CREATE TEMP TABLE ... AS SELECT توی یه جدول موقت میریزی، بعد جدول اصلی رو TRUNCATE میکنی و دوباره از جدول موقت INSERT میکنی. اینجوری فقط ردیفهای نگهداشتهشده دوباره توی WAL نوشته میشن. این تقریباً همون کاریه که اکستنشن pg_squeeze (نسخهی مدرنتر pg_repack) انجام میده.
برای حذفهای مداوم هم پیشنهادشون پارتیشنبندیه. از نسخهی ۱۰ به بعد Postgres پارتیشنبندی خوبی داره و مثلاً با پارتیشنبندی بر اساس تاریخ میتونی برای هر روز یه جدول فرزند داشته باشی و یه فرایند دورهای پارتیشنهای قدیمی رو DROP کنه (یا با pg_partman). اینجوری یه بار کاری پر از «DELETE زیاد» تبدیل میشه به «گاهبهگاه DROP TABLE».
نکات کلیدی:
- DELETE انبوه بهخاطر MVCC فضا رو فوری آزاد نمیکنه و dead tuple، بدهی vacuum و سربار replication میسازه.
- DROP TABLE و TRUNCATE تقریباً مستقل از حجم دادهان، فایلها رو مستقیم پاک میکنن و فضا رو فوری برمیگردونن.
- foreign key و CASCADE میتونن یه حذف تکردیفی رو به حذف گیگابایتها داده تبدیل کنن.
- برای حذف یکباره: قفل ACCESS EXCLUSIVE، کپی دادهی موردنظر تو جدول موقت، TRUNCATE و دوباره INSERT.
- پارتیشنبندی (مثلاً بر اساس تاریخ) حذف مداوم رو به DROP TABLEهای دورهای تبدیل میکنه.




