تصور کنید یک گزارش تحلیلی که ۳۲ ثانیه زمان میبرد، ناگهان در ۳۷ میلیثانیه آماده شود. این جهش ۱۰۰۰ برابری در عملکرد، هسته مرکزی نسخه ۰.۱۰.۰ افزونه pg_clickhouse است که در ۱۱ اوت ۲۰۲۶ منتشر شد و شیوه تعامل PostgreSQL با ClickHouse را برای بارهای کاری تحلیلی بهطور بنیادین تغییر داد.
برای سالها، بزرگترین مانع در رابطهای داده خارجی (Foreign Data Wrappers)، چرخه «بازیابی و ارزیابی» (fetch-and-evaluate) بود. وقتی کاربر یک پرسوجوی پیچیده را در PostgreSQL روی یک جدول دوردست در ClickHouse اجرا میکند، سیستم اغلب هر ردیف را بهصورت تکتک فراخوانی میکند تا زیرپرسوجوها بهصورت محلی ارزیابی کند. این فرآیند — شبیه به این است که برای خواندن یک کتاب، هر بار برای هر کلمه به کتابخانه بروید و برگردید — باعث ایجاد ترافیک شدید شبکه و فشار به پردازنده میشود که در مقیاس بالا، عملکرد را بهکل نابود میکند.
ClickHouse برای حل این مشکل، قابلیت «Pushdown» یا انتقال پردازش را پیاده کرده است. Pushdown تضمین میکند که کارهای سنگین مثل فیلتر کردن، تجمیع و ارزیابی زیرپرسوجوها مستقیماً روی سرور ClickHouse انجام شود و فقط نتیجه نهایی به PostgreSQL بازگردد. این قابلیت برای کاربرانی که همزمان پایداری تراکنشی Postgres و سرعت ستونی ClickHouse را میخواهند، حیاتی است. همانطور که در تحلیلهای پیشین ما درباره بهینهسازی پایگاهدادههای توزیعشده اشاره کردیم، جابهجایی محاسبات به نزدیکی دادهها، تنها راه عبور از سد تأخیر شبکه است. این رویکرد بهینهسازی در راستای استراتژیهای گستردهتر این شرکت برای پیشتازی در زیرساختهای داده است، مشابه آنچه در مدل جدید ClickHouse برای توسعه AI و ایجاد آزمایشگاه تحقیقاتی صنعتی مشاهده میکنیم.
جهش در بنچمارک TPC-H
به نقل از گزارش رسمی clickhouse.com، تیم توسعه از مجموعه محک TPC-H به عنوان معیار اصلی موفقیت استفاده کرد. در نسخه ۰.۱۰.۰، تعداد پرسوجوهایی که بهطور کامل منتقل (Pushdown) شدند، از ۱۲ مورد به ۱۶ مورد از ۲۲ پرسوجوی کل رسید. این پیشرفت ادامه مسیری است که از دسامبر سال گذشته، یعنی زمانی که تیم برای اولین بار این پروژه را معرفی کرد، آغاز شده بود.
سه پرسوجوی خاص بیشترین بهبود را داشتند چون پیش از این نیاز به بازیابی ردیفبهردیف داشتند. در این موارد، pg_clickhouse مجبور بود هر ردیف را بهصورت مجزا از ClickHouse بگیرد و زیرپرسوجو را محلی ارزیابی کند:
- پرسوجوی ۱۷: از ۳۲,۷۰۹ میلیثانیه به ۳۷ میلیثانیه رسید. این پرسوجوی «قهرمان» است: یک زیرپرسوجوی مرتبط (correlated subquery) که میانگین
l_quantityرا برای هر بخش محاسبه میکند. در مقیاس ۱ (Scale Factor 1)، این پرسوجو پیش از این برای هر ردیف بیرونی، یکبار در برابر ۶ میلیون آیتم خطی ارزیابی میشد. - پرسوجوی ۲۵: از ۳,۴۴۶ میلیثانیه به ۲۴ میلیثانیه کاهش یافت.
- پرسوجوی ۲۲: از ۱,۴۱۵ میلیثانیه به ۴۵ میلیثانیه رسید. (نکته: این مورد منتقل شده است، اما به جای یک پرسوجوی دوردست، معمولاً شامل یک اسکن بیرونی به علاوه یک اسکن InitPlan است).
نکته جالب این است که pg_clickhouse اکنون در پرسوجوی ۱۷ حتی از برنامه اجرای داخلی خودِ PostgreSQL (که ۲.۱ ثانیه زمان میبرد) سریعتر است و تنها ۳۷ میلیثانیه زمان میبرد.
حل معمای SubPlan
تا پیش از این نسخه، برنامهریز PostgreSQL اغلب یک 'SubPlan' ایجاد میکرد؛ یعنی یک برنامه پرسوجوی مجزا که به عنوان بخشی از اجرای پرسوجوی کامل، معمولاً برای هر ردیف یکبار اجرا میشد. بهروزرسانی ۰.۱۰.۰ (بهویژه در Issue #289) به برنامهریز اجازه میدهد این زیرپرسوجوها را در یک دستور SQL واحد و دوردست ادغام (Fold) کند.
بهعنوان مثال، پرسوجویی که مبلغ فروش را با میانگین مقایسه میکند، بهجای هزاران درخواست مجزا، بهعنوان یک دستور واحد به ClickHouse ارسال میشود. خروجی EXPLAIN همچنان گره SubPlan را برای ثبتهای داخلی PostgreSQL نشان میدهد، اما بخش Remote SQL شامل کل مقایسه است. این سازوکار همچنین انتقال عملگرهای NOT IN را از طریق تبدیل به LEFT ANTI JOIN ممکن میکند، مشروط بر اینکه برنامهریز بتواند ثابت کند این تبدیل ایمن است.
البته این قابلیت نیازمند ClickHouse نسخه ۲۵.۸ یا بالاتر است، زیرا نسخههای قدیمیتر از ساختار SQL زیرپرسوجوهای مرتبط پشتیبانی نمیکنند. اگر افزونه در زمان برنامهریزی متوجه شود سرور قدیمیتر است، بهطور خودکار به ارزیابی محلی بازمیگردد تا اجرای پرسوجو تضمین شود، هرچند با سرعت کمتر.
چالش منطق NULL
انتقال SQL ساده است، اما تضمین صحت پاسخ دشوار است. توسعهدهندگان متوجه تفاوت بحرانی در نحوه مدیریت مقادیر NULL در عملیات IN و NOT IN بین دو سیستم شدند (#315, #317).
PostgreSQL از منطق سهمقیمی (True, False, Null) استفاده میکند، در حالی که ClickHouse بهطور سنتی منطق دومقیمی دارد. برای مثال، عبارت x NOT IN (1, NULL) در Postgres میتواند FALSE یا NULL باشد، اما هرگز TRUE نمیشود. یک انتقال ساده (Naive Pushdown) میتوانست نتایج را بهطور خاموش وارونه کند و دادههای غلطی تولید کند که در تستهای اولیه شناسایی نمیشوند؛ مثلاً WHERE NOT IN ردیفهایی را برگرداند که Postgres باید آنها را فیلتر میکرد، یا GROUP BY یک گروه NULL را با FALSE ادغام کند.
برای حل این مشکل، نسخه ۰.۱۰.۰ سیستمی برای ردیابی نتایج عبارتها پیاده کرده است. افزونه تحلیل میکند که هر نتیجه چگونه مصرف میشود:
- شرایط فیلتر: در اینجا NULL را میتوان بهجای FALSE در نظر گرفت و رفتار بومی ClickHouse پذیرفتنی است، مگر در شرایط
NOT. - موقعیتهای مقدار/نفی: نیاز به بررسیهای اضافی برای مقادیر null دارند تا مقدار صحیح Postgres تزریق شود.
- بهینهسازی: اگر pg_clickhouse بتواند ثابت کند عملوندها نمیتوانند NULL باشند (مثلاً از طریق ثابتهای غیر-NULL، محدودیتهای
NOT NULLکه توسط Outer Joinها دوباره NULL نشدهاند، یا عملیاتهای غیر-nullable)، برای رسیدن به حداکثر سرعت، گاردها را حذف میکند.
برای پرسوجوهایی که نیاز به گارد دارند، Remote SQL به یک دستور CASE پیچیده تبدیل میشود که IS NULL ،notEmpty() و countEqual() را بررسی میکند. این رفتار برای کل خانواده IN تعمیم یافت، شامل NOT IN ،= ANY ،= ALL ،<> ANY و <> ALL در هر دو فرم اسکالر و آرایهای. برای جلوگیری از اینکه تنظیمات سطح سرور این گاردها را خراب کند، گزینه transform_null_in 0 به تنظیمات پیشفرض pg_clickhouse.session_settings اضافه شد.
گسترش کتابخانه توابع
علاوه بر زیرپرسوجوها، تعداد توابع پشتیبانیشده بهشدت افزایش یافته است. تیم توسعه به یک سیستم نگاشت اختیاری (Opt-in) منتقل شد (#245) تا از خطاهای خاموش جلوگیری کند. پیش از این، هر تابع داخلی Postgres که نامش با ClickHouse یکی بود بهطور پیشفرض منتقل میشد. این موضوع در توابع مثلثاتی مثل asin/acos/atanh/acosh مشکلساز بود؛ جایی که Postgres برای مقادیر خارج از دامنه خطا میداد، اما ClickHouse مقدار NaN برمیگرداند.
قابلیتهای جدید انتقال عبارتند از:
تجمیع و ریاضیات:
- تجمیعهای آماری (#290): پشتیبانی از
corr،covar_pop/samp،stddev_pop/sampوvar_pop/sampو همچنینany_value. - تجمیعهای مجموعه-ترتیبی (#291): توابع
percentile_contوpercentile_discاکنون به توابع پارامتریکquantile(s)وquantileExactLowدر ClickHouse متصل شدهاند. - تجمیع بر اساس پارتیشن (#298): پرسوجوها روی جداول پارتیشنبندی شده، اگر
enable_partitionwise_aggregateفعال باشد، سهم پارتیشن خارجی را روی ClickHouse محاسبه میکنند. این قابلیت از تجمیعهای تجزیهپذیر مثلcount،sum،min،maxوavgروی اعداد صحیح پشتیبانی میکند.
رشتهها، تاریخ و فرمت:
- فرمتبندی (#302): تابع
encode(bytea, 'hex'|'base64'|'base64url'). - رشتهها (#307): توابع سه آرگومانی
ltrim،rtrimوbtrim. - تاریخ و زمان (#301): محاسبات بازهای (Interval) برای عملوندهای تاریخ/timestamp و تفریق گسترش یافت. خانواده توابع
CURRENT_*/now()/clock_timestamp()برای منطقه زمانی نشست و دقت زیر-ثانیه بازتنظیم شدند. - سایر موارد: تابع
to_char()با اعتبارسنجی رشته فرمت (#244)، تابعsplit_part()(#206) و توابعfuzzystrmatchشاملsoundex()وlevenshtein()(#210).
JSON و آرایهها:
- JSON (#169, #176): عملگرهای
->و->>وjsonb_extract_path[_text]()به سینتکس زیر-ستون ClickHouse متصل میشوند. نوع JSON بومی ClickHouse اکنون بهjsonدر Postgres متصل است. - آرایهها: بیش از ۱۲ تابع اکنون منتقل میشوند، از جمله
array_cat،append،remove،to_string،lengthوhasAll/hasAny(برای عملگرهای@>،<@و&&). سینتکس برشarr[L:U]بهarraySlice()متصل شده است. - توابع پنجرهای (#175): مجموعه کامل توابع منتقل میشوند، شامل
ROW_NUMBER،RANK،LEAD/LAGوNTILE. - بولی/رشته (#184): توابع
bool_and،bool_orوstring_agg.
بازنگری معماری: درایور C
در لایه زیرین، این افزونه کتابخانه clickhouse-cpp را کنار گذاشته و از یک کتابخانه جدید به زبان C به نام clickhouse-c استفاده میکند (#254). این تغییر چندین مشکل پایداری و عملکرد را حل کرد:
- جلوگیری از کرش: تداخل بین مدیریت استثناهای C++ و سیستم خطای
setjmp/longjmpدر PostgreSQL از بین رفت. - بهرهوری حافظه: درایور جدید نتایج را بهصورت بلوک-به-بلوک استریم میکند و دیگر کل مجموعه نتایج را در حافظه بافر نمیکند.
- سرعت ساخت: زمان و حجم ساخت کتابخانه داخلی (vendored) بیش از ۷۵٪ کاهش یافت.
هر دو درایور باینری و HTTP اکنون از یک مسیر کدگذاری باینری واحد استفاده میکنند (#328). درایور HTTP اکنون بهجای مسیر قدیمی مبتنی بر TSV، از فرمت Native کلیکهاوس استفاده میکند. در نتیجه، گزینه fetch_size منسوخ شده است زیرا رمزگشای بومی دادهها را تکه-تکه (curl chunk) استریم میکند.
در بخش نوشتن، درایور باینری اکنون دادههای بافر شده INSERT/COPY FROM را پس از رسیدن به ۶۴ مگابایت تخلیه (Flush) میکند (#303) تا از اشغال کل دستهها در حافظه جلوگیری شود. هر دو درایور گزینههای صریح فشردهسازی (none ،lz4 ،zstd) (#268) و کنترلهای دقیقتر TLS (secure = on/off/auto و min_tls_version) (#272) را دریافت کردند.
پوشش انواع دادهها نیز گسترش یافته تا از آرایههای چندبعدی برای خواندن و نوشتن در هر دو درایور (#233) و درجهای Array(Nullable(T)) روی پروتکل باینری (#316) پشتیبانی کند.
پایداری و ابزارهای جدید
باگهای مربوط به همروندی (Concurrency) هدف اصلی این چرخه بودند. پیش از این، اسکنهای خارجی همزمان (مثل موارد موجود در Nested-loop joins یا زیرپرسوجوهای مرتبط) روی یک اتصال مشترک برخورد میکردند و باعث کرش میشدند. اکنون هر اسکن همزمان اتصال مخصوص به خود را دریافت میکند (#296)، که همچنین یک باگ use-after-free را در زمینههای حافظه (memory contexts) دستههای اسکن خارجی حل کرد.
سایر اصلاحات عبارتند از:
- تفکیک OID (#319): رفع خطایی در زمان اجرا که به دلیل انتخاب OID رابطه نامعتبر هنگام انتخاب کاربر برای اجرای پرسوجو رخ میداد.
- دقت (#300): رفع مشکل از دست رفتن دقت زیر-ثانیه هنگام درج timestampها از طریق HTTP.
- تحلیل استاتیک (#313): یک بررسی گسترده باعث شد چندین باگ پنهان پیش از ظهور در محیط عملیاتی اصلاح شوند.
برای توسعهدهندگانی که کنترل بیشتری میخواهند، توابع جدیدی اضافه شده است:
۱. clickhouse_query(server, sql): اجرای پرسوجوهای دلخواه و بازگرداندن نتایج تایپشده. این تابع اکنون از درایور باینری پشتیبانی میکند (#309). تعریف ستونها الزامی است زیرا Postgres باید شکل ردیف را پیش از بازیابی بداند.
۲. clickhouse_perform(server, sql): رویهای که از طریق CALL برای دستوراتی که ردیفی برنمیگردانند (مانند CREATE TABLE) فراخوانی میشود (#329).
۳. clickhouse_server_version(server): گزارش نسخه سرور متصل (#293).
توجه داشته باشید که clickhouse_raw_query() اکنون منسوخ شده و در نسخه بعدی حذف خواهد شد (#329). کاربران باید به clickhouse_query() یا clickhouse_perform() مهاجرت کنند تا از سرورهای خارجی پیکربندیشده و مدیریت صحیح اتصال بهرهمند شوند.
مسیر پیشرو
با وجود این پیشرفتها، ۶ پرسوجوی TPC-H هنوز منتقل نمیشوند: Q13, Q15, Q16, Q18, Q20 و Q21. مانع اصلی برای Q16 و Q18 این است که PostgreSQL زیرپرسوجوهای آنها را به anti/semi-joinهایی تبدیل میکند که ورودیهای آنها خودشان Join هستند. تحلیلگر (deparser) فعلی هنوز نمیتواند درخت Join را در هر دو طرف یک Join بررسی کند. Q15 و Q20 نیز با نسخههایی از همین مشکل مواجه هستند.
حل این محدودیت «درخت Join» هدف بعدی در نقشه راه است، در کنار پیادهسازی انتقال سبک DELETE/UPDATE و UNION. این تغییر به سمت Pushdown عمیقتر، pg_clickhouse را از یک پل ساده داده به یک بهینهساز پرسوجوی توزیعشده پیشرفته تبدیل میکند.
گام بعدی شما
- اگر از PostgreSQL برای تحلیل دادههای حجیم استفاده میکنید، نسخه ۰.۱۰.۰ را نصب کنید تا هزینه محاسباتی را به سرور ClickHouse منتقل کنید.
- در دستورات
EXPLAINخود بررسی کنید که آیا SubPlanها به Remote SQL تبدیل شدهاند یا خیر. - برای استفاده از حداکثر سرعت، مطمئن شوید نسخه ClickHouse شما ۲۵.۸ یا بالاتر است.
اما داستان سختافزاری این تحول حتی شگفتانگیزتر است — به تحلیل ما درباره تراشههای Blackwell مراجعه کنید.




گفتگو