سفر یک کوئری Postgres در دل یک دیتابیس شارد شده
خلاصهٔ کاملتر
PlanetScale تو این مقاله مسیر یه کوئری سادهی SELECT رو تو دیتابیس شارد شدهی خودشون به اسم Neki دنبال میکنه. فرض کن جدول customers و orders، هر کدوم بر اساس id خودشون، رو ۴ تا شارد (یعنی چند سرور جدا که هرکدوم فقط بخشی از داده رو نگه میداره) پخش شدن. باید این کوئری جوری اجرا بشه که برای اپلیکیشن انگار همهچیز رو یه Postgres تنها داره اتفاق میافته؛ اولین قدمش احراز هویته، جایی که router کل exchange احراز هویت Postgres (شامل SCRAM-SHA-256) رو با Go پیادهسازی کرده.
بعدش نوبت پروتکل و پارس کردنه. Postgres دو جور کوئری میگیره: پروتکل Simple که کل SQL یهجا فرستاده میشه، و پروتکل Extended که تو ۵ مرحله (Parse، Bind، Describe، Execute، Sync) انجام میشه و برای اجرای تکراری یه کوئری بهینهتره. تیم Neki یه پارسر SQL حدود ۱۸ هزار خط با Go نوشتن که دقیقاً مثل پارسر خود Postgres رفتار میکنه و SQL رو به یه درخت syntax تبدیل میکنه؛ اگه پلن کششدهای موجود باشه، این مرحله رد میشه.
سختترین بخش پلنریزیه: router باید بفهمه کدوم بخش کوئری رو کدوم شارد اجرا کنه، چون سفارشهای یه مشتری رو همهی شاردها پخش میشن ولی خود مشتری فقط رو یکی از شاردهاست. برای جوین زدنشون، planner بین nested-loop join (کند و پرهزینه) و hash join انتخاب میکنه؛ تو hash join که بهینهتره، router یه بار مشتریها رو میگیره، جدولی تو حافظه میسازه و با یه scatter-gather (فرستادن همزمان درخواست به شاردها) سفارشها رو match میکنه. برای ۱۰۰ هزار مشتری و ۱ میلیون سفارش، hash join انتخاب میشه.
وقتی پلن آماده شد، router مستقیم به Postgres وصل نمیشه؛ درخواست رو از طریق gRPC (پروتکل ارتباط سریع بین سرویسها) به یه sidecar (پروسهی جدا کنار هر Postgres) میفرسته. sidecar یه pool از کانکشنهای گرم Postgres رو بین چند router به اشتراک میذاره و مطمئن میشه role و تنظیمات session درست اعمال شده. برای چیزی مثل میانگین سفارش هر مشتری، Eval Engine وارد عمل میشه: هر شارد جمع و تعداد رو جدا حساب میکنه و router در آخر میانگین رو میسازه.
نویسنده در پایان میگه اگه از اول orders رو بر اساس customer_id شارد میکردن بهجای id خودش، هر مشتری با سفارشهاش رو یه شارد میموندن و Postgres میتونست جوین رو محلی انجام بده؛ یعنی بهترین جوین همونیه که اصلاً لازم نیست اجرا بشه.
نکات کلیدی:
- router به اسم Neki احراز هویت Postgres (SCRAM-SHA-256) رو کامل تو Go پیادهسازی کرده
- پارسر SQL این سیستم حدود ۱۸ هزار خط کده و رفتار Postgres رو دقیق شبیهسازی میکنه
- برای جوین بین شاردها از nested-loop join یا hash join استفاده میشه؛ انتخاب بر اساس تخمین حجم دادهست
- ارتباط با هر شارد از طریق یه sidecar و gRPC انجام میشه تا کانکشنهای Postgres به اشتراک گذاشته بشن
- شارد کردن orders بر اساس customer_id بهجای id خودش میتونه جوین رو کامل محلی و بدون کار اضافه تو router کنه




