PostgreSQL 18؛ سیزده اندازه‌گیری کوچک از index تا نوشتن و sort

چرا مهم است

اگر با PostgreSQL کار می‌کنی، این اندازه‌گیری‌ها تفاوت هزینهٔ انتخاب‌های ظاهراً کوچک را نشان می‌دهند. در همین آزمایش‌ها، اندازهٔ index،؜ وضعیت VACUUM و روش ورود داده با زمان اجرا و تعداد دسترسی‌ها همراه بودند.

قبل از خواندن، این‌ها را بدان۵ مفهوم
transaction

مجموعه‌ای از عملیات که به‌عنوان یک واحد انجام می‌شوند و تغییرات آن با commit نهایی یا با rollback لغو می‌شود.

بیشتر: transaction

partial index

نوعی index که فقط ردیف‌های مطابق یک شرط مشخص را نگه می‌دارد؛ استفاده از آن به شرط query وابسته است.

در حال نگارش: partial index

deduplication

حذف ذخیره‌سازی تکراری اطلاعات؛ در B-tree می‌تواند یک کلید تکراری را یک بار همراه با فهرست اشاره‌گرهای ردیف‌ها نگه دارد.

در حال نگارش: deduplication

index-only scan

روش اجرای query با استفاده از اطلاعات index؛ در PostgreSQL ممکن است برای بررسی قابل‌مشاهده‌بودن ردیف‌ها همچنان به جدول مراجعه کند.

در حال نگارش: index-only scan

foreign key

قیدی برای ارتباط ردیف‌های دو جدول که معتبر بودن ارجاع به ردیف جدول دیگر را بررسی می‌کند.

در حال نگارش: foreign key

Hollis Varden سیزده آزمایش کوچک روی PostgreSQL 18.6 با تنظیمات پیش‌فرض انجام داده است. دستگاه آزمایش MacBook Air M5 با ۱۰ core و ۲۴ GB حافظه بود. بیشتر آزمایش‌ها از جدولی با دو میلیون سفارش استفاده می‌کنند؛ زمان query‌ها میانهٔ پنج اجرا پس از یک اجرای آماده‌سازی است و برای موارد کندتر، سه اجرا محاسبه شده است. نویسنده تأکید می‌کند زمان‌ها با نوشته‌های قبلی او روی دستگاهی دیگر قابل مقایسه نیستند.

در بخش index،؜ یک partial index روی customer_id برای سفارش‌های open فقط ۰٫۷ MB فضا گرفت، در برابر ۱۴٫۲ MB برای همهٔ ردیف‌ها. تعداد buffer‌های query مربوط به سفارش‌های باز یک مشتری نیز از ۴۳ به ۲ رسید؛ استفاده از این index به تطابق شرط query با شرط index وابسته است. برای created_at که داده‌ها به ترتیب آن وارد شده بودند، BRIN با اندازهٔ ۲۴ kB شمارش یک روز را در ۰٫۶۳ ms انجام داد؛ B-tree با اندازهٔ ۴۴٫۹ MB همان کار را در ۰٫۵۵ ms انجام داد. داده‌های نامرتب آزمایش نشدند.

افزودن amount با INCLUDE،؜ زمان جمع سفارش‌های یک مشتری را از ۰٫۰۷۳ به ۰٫۰۳۸ ms رساند، اما اندازهٔ index از ۱۴٫۲ به ۴۵ MB رسید؛ چون index دارای INCLUDE نمی‌تواند از deduplication استفاده کند. همچنین عنوان index-only scan به‌تنهایی به معنای نخواندن جدول نبود: پس از VACUUM،؜ تعداد heap fetch‌ها از ۲۰۰٬۰۰۱ به صفر رسید. در آزمایش دیگری، حذف ۲۰۰ ردیف والد با ON DELETE CASCADE،؜ بدون index روی foreign key فرزند ۳٫۶ ثانیه و با آن ۰٫۰۱۳ ثانیه طول کشید.

برای ورود ۲۰۰ هزار ردیف در یک transaction،؜ روش ‎\copy در ۰٫۰۳۹ ثانیه تمام شد؛ INSERT هزارردیفی ۰٫۱۳۳ و INSERT تک‌ردیفی ۲٫۱ ثانیه زمان برد. جدول UNLOGGED سریع‌تر نوشته شد، اما پس از crash خالی می‌شود و replication ندارد. خاموش‌کردن synchronous_commit نیز نرخ transaction را بالا برد، با خطر ازدست‌رفتن چند commit آخر هنگام crash.

در UPDATE همهٔ ردیف‌ها، اندازهٔ جدول تقریباً دو برابر شد. VACUUM فضا را به سیستم‌عامل برنگرداند، اما امکان استفادهٔ دوباره از آن را در UPDATE بعدی فراهم کرد. در sort،؜ افزایش work_mem از ۴ به ۶۴ MB زمان را از ۴۳۳ به ۲۹۷ ms رساند؛ افزایش تا ۲۵۶ MB تقریباً تغییری نداد. این تنظیم برای هر sort یا hash در هر query جداگانه اعمال می‌شود. در آزمایش پایانی نیز دو parallel worker بیشترِ کاهش زمان مشاهده‌شده را ایجاد کردند.

نکته کلیدی

این زمان‌ها را نتیجهٔ همین دستگاه و داده‌ها بدان، نه عددی عمومی برای هر محیط. تفاوت محدودیت‌ها را هم به خاطر بسپار: UNLOGGED پس از crash خالی می‌شود و synchronous_commit خاموش ممکن است چند commit آخر را از دست بدهد.

خواندن مقاله اصلی (در زبانه‌ی جدید باز می‌شود)
برچسب‌ها (۳)