uuidv7 در برابر uuidv4 و bigint توی PostgreSQL 18
خلاصهٔ کاملتر
تو این مقاله یه جدول 10 میلیون ردیفی روی PostgreSQL 18.6 سه بار ساخته شد و فقط نوع کلید اصلی عوض شد: bigint identity، uuidv4() تصادفی و uuidv7() جدید که بر اساس زمان مرتبه. بارگذاری با bigint حدود 29.7 ثانیه، با v7 حدود 32.3 و با v4 حدود 60.7 ثانیه طول کشید. خود تولید کلید مقصر نیست؛ ده میلیون فراخوانی بدون جدول، فقط چند دهم ثانیه با هم فرق داشتن.
مشکل v4 توی ایندکسه. جدول توی PostgreSQL یه heap هست (یعنی ردیفها همیشه ته جدول اضافه میشن)، برای همین هر دو جدول UUID دقیقاً 888 مگ شدن. ولی کلیدهای مرتب همیشه به لبهٔ راست B-tree میرسن و صفحهها 90 درصد پر میمونن. کلیدهای تصادفی وسط صفحههای پر میافتن و اونا رو نصف میکنن؛ نتیجه ایندکس 387 مگی با تراکم 70 درصد در برابر 301 مگ برای v7.
نویسنده میگه مهمترین عدد، حجم WAL هست (یعنی لاگ تغییراتی که replicaها و آرشیو دریافت میکنن). با full_page_writes، اولین تغییر هر صفحه بعد از checkpoint کل صفحهٔ 8 کیلوبایتی رو توی WAL مینویسه. با کلید تصادفی تقریباً هر insert یه صفحهٔ جدید رو لمس میکنه، پس برای هر ردیف 664 تا 974 بایت WAL نوشته شد، در حالی که v7 حدود 228 بایت بود. این چرخه خودش رو تشدید میکنه چون WAL بیشتر یعنی checkpoint زودتر.
خوندن ردیفهای تازه جاییه که v4 بیشتر از همه ضربه میزنه. گرفتن 100 هزار ردیف جدید با buffer pool خالی روی v7 حدود 85 میلیثانیه طول کشید و روی v4 حدود 3.4 ثانیه، چون کلیدهای تازهٔ v4 روی کل ایندکس پخش شدن. البته مقاله تأکید میکنه که برای lookupهای تکردیفی فرق خیلی کمتره و فاصله وقتی باز میشه که یه کوئری ردیفهای زیادی بخونه یا حافظه کم باشه.
برای جدولی که الان روی v4 هست لازم نیست چیزی بازنویسی بشه، چون نوع ستون در هر دو حالت uuid هست:
alter table t_v4 alter column id set default uuidv7();این دستور روی جدول 14 میلیون ردیفی 5 میلیثانیه طول کشید و WAL هر ردیف فوراً به 250 بایت رسید. آسیب قدیمی ایندکس تا rebuild باقی میمونه؛ REINDEX INDEX CONCURRENTLY ایندکس رو از 695 به 499 مگ رسوند. جمعبندی نویسنده: اگه فقط دیتابیس ID میسازه bigint هنوز کوچیکترینه، اگه UUID لازم داری v7 رو بردار. v4 رو فقط برای جایی نگه دار که زمان ساخت ID نباید لو بره، چون از v7 میشه timestamp رو بیرون کشید.
نکات کلیدی:
- بارگذاری 10 میلیون ردیف: bigint حدود 29.7، uuidv7 حدود 32.3 و uuidv4 حدود 60.7 ثانیه.
- ایندکس کلید اصلی: 214 مگ برای bigint، 301 مگ برای v7 و 387 مگ برای v4.
- WAL هر insert تکردیفی با v4 بین 664 تا 974 بایت بود، در برابر حدود 228 بایت برای v7.
- uuidv7() فقط از PostgreSQL 18 به بعد وجود داره و ترتیبش فقط داخل یه backend تضمین شده.
- عوض کردن default به uuidv7() روی جدول موجود 5 میلیثانیه طول کشید و نیازی به بازنویسی جدول نداشت.




