تصور کنید یک سرور ارزانقیمت با هزینه ماهانه ۱۶.۴۹ دلار را دارید که بهجای متوقف شدن در برابر حجم زیاد داده، قادر است استک مشاهدهپذیری (Observability Stack) را مدیریت کند که داشبوردهایی با ۱۰۰ میلیون ردیف را سرو میکند؛ مقیاسی که پیش از این برای پایگاههای داده جاسازیشده (Embedded Databases) غیرممکن بود. در تاریخ ۲۹ جولای ۲۰۲۶، پلتفرم Traceway دادههای بنچمارکی را منتشر کرد که نشان میدهد تغییر موتور ذخیرهسازی از SQLite به DuckDB بهطور موثری هر «دیوارِ عملکردی» (Performance Cliff) را ۱۰۰ برابر دورتر برده است.
این تغییر در زمانی رخ میدهد که توسعهدهندگان برای یافتن تعادل بین سادگی پایگاههای داده جاسازیشده — که مانند یک دفترچه یادداشت سریع کنار برنامه هستند — و قدرت سیستمهای OLAP کلاینت-سرور مانند ClickHouse در تقلا بودهاند. بهطور سنتی، استقرار تک-باینری (Single-Binary Deployment) به معنای فدا کردن توانایی پرسوجو روی مجموعههای بزرگ داده بدون برخورد با دیوار تأخیر (Latency Wall) بود. با تکیه بر تستهای قبلی که در آن SQLite با لاگها و تعداد ردیفهای بالا دستوپنجه نرم میکرد، این نتایج جدید بررسی میکنند که آیا یک موتور ستونی جاسازیشده میتواند این شکاف را بدون افزودن سربار حافظه یک کانتینر کامل دیتابیس پر کند یا خیر. در همین راستا، تلاش برای بهینهسازی SQLite برای کاربردهای خاص ادامه دارد، بهطوری که اخیراً رویکردهایی برای دستیابی به تأخیر زیر ۱۰ میلیثانیه در جستوجوهای معنایی محلی با استفاده از sqlite-vec معرفی شده است.
متدولوژی اندازهگیری
برای تضمین عدالت در مقایسه، نویسنده دقیقاً همان محیطی را شبیهسازی کرد که در بنچمارکهای SQLite استفاده شده بود. سیستم تحت تست (SUT) با اجرای باینری Traceway (کامیت 14b4aa6e) روی اوبونتو ۲۴.۰۴ و میزبانی روی یک نمونه CCX13 در دیتاسنتر نورمبرگ شرکت هتزنر (Hetzner) اجرا شد. نکته جالب این است که برای این اجرا، مولد بار (Load Generator) به یک نمونه CCX23 ارتقا یافت؛ زیرا مدل قبلی CCX13 در حالی که موتور DuckDB هنوز با نرخ خطای ۰٪ عمل میکرد، کرش کرد. در واقع، تولیدکننده ترافیک پیش از آنکه دیتابیس از پا دربیاید، شکست خورد.
تستها از پروتکل OTLP (پروتکل شبکه OpenTelemetry) روی protobufهای gzipped استفاده کردند تا دادههای تصادفی یکنواخت در محدودههای مشخص را شبیهسازی کنند. برای هر سیگنال، دو سناریوی خاص اندازهگیری شد:
- شتاب توان عملیاتی (The Throughput Ramp): این مرحله شامل یک افزایش تدریجی اندازه دستهها (Batch-size ramp) در یک نرخ ثابت بود و پس از آن، یک افزایش نرخ درخواست (از ۱ تا ۲۵ درخواست در ثانیه) با استفاده از برنده ترین اندازه دسته اجرا شد. این نردبان باریکتر به این دلیل انتخاب شد که نقاط شکست جالب بین گامهای درشتتر در پستهای قبلی رخ داده بودند. در نهایت، یک شبیهسازی با دستههای کوچک و نرخ بالا برای تقلید از یک ناوگان واقعی SDK انجام شد. یک گام زمانی «پاس شده» تلقی میشد که نرخ خطای زیر ۵٪ را حفظ کرده و دستکم به ۷۰٪ از نرخ هدف دست یافته باشد.
- پروب خوانش (The Read-Probe): جداول تا سطوح ۱ میلیون، ۱۰ میلیون، ۱۰۰ میلیون، ۱ میلیارد و ۵ میلیارد ردیف پر شدند. در هر سطح، سه نقطه پایانی (Endpoint) واقعی داشبورد مورد بررسی قرار گرفتند. یک سطح زمانی پاس میشد که میانگین پاسخ $\le$ ۵ ثانیه بود و هیچ درخواستی به تایم-اوت ۶ ثانیهای برخورد نمیکرد. سقف ۵ ثانیه بهطور عمدی سخاوتمندانه انتخاب شده بود.
جزئیات فنی پیادهسازی
برای تطبیق با معماری DuckDB، دو تغییر فنی حیاتی در زیرساخت (Harness) اعمال شد:
- دروازه هضم (The Digestion Gate): موتور DuckDB پس از توقف ورود دادهها، لاگهای پیشنویس (WAL) را چکپوینت میکند. اگر در این پنجره زمانی پروب خوانش انجام شود، در واقع «مشغول بودن موتور» اندازهگیری میشود نه «سرعت پرسوجو». اکنون پروب خوانش، نقطه پایانی سلامت عمیق (Deep-health endpoint) بکاند را تا زمانی که حجم فایلهای دیتابیس و WAL تثبیت شوند، مانیتور میکند. در اجراهای نهایی، این زمان انتظار در هر سطح ۵ ثانیه بود.
- شمارش ردیفهای پذیرفتهشده: نویسنده تنها ردیفهای «پذیرفتهشده» (تایید شده با پاسخهای 2xx) را شمرد، نه ردیفهای تلاششده. این موضوع زمانی حیاتی میشود که بکاند شروع به رد درخواستها از طریق دروازه پذیرش (Admission Gate) میکند.
به دلیل اینکه پر کردن جداول اغلب از اهداف فراتر میرفت (تا ۲.۶ برابر در کوچکترین سطح)، در گزارش تعداد ردیفهای واقعی ذکر شده است. علاوه بر این، اینها اجراهای تکشوت (Single-shot) بودند. اگرچه پروتکل اصلی تقاضای میانگین سه تکرار داشت، اما چرخه «اصلاح و اجرای مجدد» بودجه زمانی را مصرف کرد. با این حال، سلولهای کلیدی بازتولید شدند؛ برای مثال، خوانشهای متریک در ۱۰۰ میلیون ردیف طی چهار روز مختلف، بین ۲.۷ تا ۳.۲ ثانیه ثبت شدند.
جهش در توان عملیاتی
طبق گزارش Traceway، gains در بخش نوشتن فوری و چشمگیر است. DuckDB در تمام سه سیگنال اصلی تلهمتری بر SQLite پیروز شد:
- متریکها: ۲۵۴,۲۴۲ نقطه در ثانیه برای DuckDB در مقابل ۶۱,۷۱۲ برای SQLite (افزایش ۴ برابری). شکل برنده برای DuckDB، دستهای ۱۶,۳۸۴ تایی در ۲۰ درخواست بر ثانیه با p50 معادل ۳.۲ ثانیه بود. پایینترین گام شکستخورده ۲۲.۵ درخواست بر ثانیه با ۱۲.۲٪ خطا بود.
- اسپنها (Spans): ۹۵,۷۳۷ اسپن در ثانیه برای DuckDB در مقابل ۳۰,۵۰۸ برای SQLite (افزایش ۳ برابری). شکل برنده، دستهای ۱۶,۳۸۴ تایی در ۷.۵ درخواست بر ثانیه با p50 معادل ۱.۲ ثانیه بود. پایینترین گام شکستخورده ۸.۷۵ درخواست بر ثانیه با ۹.۷٪ خطا بود.
- لاگها: ۷۵,۲۲۵ رکورد در ثانیه برای DuckDB در مقابل ۴,۸۷۷ برای SQLite (افزایش ۱۵ برابری). شکل برنده، دستهای ۱۶,۳۸۴ تایی در ۵ درخواست بر ثانیه با p50 معادل ۳.۰ ثانیه بود. پایینترین گام شکستخورده ۵.۶۲۵ درخواست بر ثانیه با ۱۱.۴٪ خطا بود.
لاگها بیشترین بهبود را نشان دادند. در حالی که SQLite در تنها ۵ درخواست بر ثانیه به یک «دیوار» سخت برخورد میکرد — جایی که افزایش ۱.۵ برابری درخواستها، نرخ خطا را از ۰٪ به ۹۸٪ میرساند — DuckDB این محدودیت سخت را به یک شیب مدیریتشده از «رد درخواستهای مودبانه» تبدیل کرد. این نتیجه بهویژه مهم است زیرا لاگها سیگنالی با بیشترین حجم در دنیای واقعی و پایینترین سقف در SQLite هستند.
علاوه بر این، موتور توانست «شکل ناوگان SDK» (صدها دسته کوچک در ثانیه) را بهراحتی مدیریت کند. در این تستها، DuckDB توانست ۵۳,۰۹۱ نقطه متریک، ۳۶,۵۷۷ اسپن و ۳۰,۴۴۲ رکورد لاگ را در ثانیه با اندازه دسته ۱۰۰ حفظ کند. این یک تاییدیه کلیدی بود؛ زیرا بکاند پیش از اصلاح (Pre-fix) پیش از رسیدن به این مرحله میمیرفت و این اعداد پایداری دروازه ورود جدید را ثابت کردند.
جابهجایی نقاط شکست خوانش
«دیوار خوانش» حداکثر اندازه جدولی است که در آن صفحات داشبورد هنوز با میانگین ۵ ثانیه لود میشوند. در تمام سیگنالها، این دیوار با DuckDB روی سختافزاری یکسان دقیقاً ۱۰۰ برابر دورتر شد.

در متریکها، این مرز از ۱ میلیون ردیف در SQLite به ۱۰۰ میلیون ردیف در DuckDB با میانگین تأخیر ۳.۰ ثانیه رسید. خوانشهای متریک بهطور بهینه مقیاس میشوند؛ افزایش ۱۰۰ برابری تعداد ردیفها تنها تأخیر را ۲۰ برابر زیاد کرد (از ۱۴۶ میلیثانیه در ۱ میلیون به ۳۸۲ میلیثانیه در ۱۰ میلیون و سپس ۳ ثانیه در ۱۰۰ میلیون). این بدان معناست که اسکنها در واقع با رشد جدول، به ازای هر ردیف ارزانتر میشوند.

اسپنها مقیاسپذیری مشابهی داشتند. پرسوجوهای تجمیعی (Aggregation Queries) — بهویژه تجمیعهای P50/P95/P99 و نمودار تأخیر پشتهای — که در SQLite بین ۱۰۰ هزار تا ۱ میلیون ردیف میمیرند، اکنون تا ۱۰ میلیون ردیف با میانگین ۹۰۲ میلیثانیه کاربردی باقی میمانند. با این حال، در ۱۰۰ میلیون ردیف، این تجمیعها سرانجام به محدودیت خود رسیده و از ۶۰ ثانیه فراتر میروند.

لاگها که تحلیل قبلی Traceway توصیه میکرد بهطور کلی از SQLite دور نگه داشته شوند، اکنون قویترین دلیل برای استفاده از DuckDB هستند. موارد استفادهای که یک مهندس آنکال (On-call) در حین یک حادثه واقعاً به آنها نیاز دارد، مانند جستجوی Trace-ID (۲ میلیثانیه) و جستجوی بدنه (۳.۵ ثانیه)، در ۱۰۰ میلیون ردیف همچنان viable هستند. تنها فیلترهای شدت (Severity) کل جدول که بخش بزرگی از ۱۰۰ میلیون ردیف را تطبیق داده و رتبهبندی میکنند، در این مقیاس شکست میخورند. شکاف بین پرسوجوهای «کاربردی» و «غیرکاربردی» صرفاً ۱۰۰ برابر جابهجا شده است.

استرستست میلیارد ردیفی
برای تست کف نهایی، بنچمارک یک میلیارد نقطه متریک را وارد کرد. سیستم در کمتر از یک ساعت موفق شد. تنها مرحله نهایی پر کردن ۵۲ دقیقه زمان برد و ۹۰۰ میلیون نقطه آخر را با سرعت پایدار ۲۸۷ هزار نقطه در ثانیه اضافه کرد، در حالی که دروازه ورود، اضافات را میزدود.
- تعداد نهایی: ۱,۰۰۱,۴۷۲,۰۰۰ ردیف
- تأثیر روی دیسک: ۱۰.۸ گیگابایت روی دیسک
- بهرهوری: تقریباً ۱۰.۸ بایت برای هر نقطه پس از فشردهسازی ستونی
- سلامت سیستم: سرور بالا ماند، چکهای سلامت در تمام مدت پاسخ دادند و هضم پس از پر کردن دقیقاً ۵ ثانیه زمان برد.
اگرچه این حجم برای پرسوجوهای داشبورد در زمان واقعی (Real-time) بسیار زیاد است (همه پرسوجوها از ۶۰ ثانیه فراتر رفتند)، اما باکس پایدار ماند. این مقیاسی است که بنچمارک SQLite حتی در حاشیه ۱۰۰ برابری نمیتوانست به آن نزدیک شود.
برای تعیین اینکه سیستم پس از عبور از دیوار دقیقاً چقدر «کند» میشود، نویسنده نردبان تست را با آستانه پروب ۶۰ ثانیهای مجدداً اجرا کرد. نتایج نشان داد که اینها «شیب» نیستند، بلکه «دیوارهای واقعی» هستند: پرسوجوها در یک گام ۱۰ برابری، از چند ثانیه به بیش از یک دقیقه میپرند. برای مثال، متریکها در ۱ میلیارد ردیف همچنان ۶۱ ثانیه اجرا میشدند و تجمیعهای اسپن در ۱۰۰ میلیون ردیف بهطور مشابه متوقف شدند. این امضای یک تجمیع است که از بودجه حافظه ۴ گیگابایتی فراتر رفته و به دیسک Spill میکند.
این اجرای ۶۰ ثانیهای یک باگ جزئی را نیز آشکار کرد: پرسوجوهای لغو شده داشبورد بهسرعت اتصالات Read-pool خود را آزاد نمیکنند. پس از اینکه دو تجمیع اسپن تایم-اوت شدند، یک پرسوجوی exceptions با تأخیر ۳ میلیثانیهای به مدت ۵۲ ثانیه در صف انتظار ماند. این مورد به لیست اصلاحات طراحی اضافه شده است.
مهندسی پیروزی
این اعداد بدون شکست به دست نیامدند. نویسنده افشا کرد که اجراهای اولیه DuckDB به دلیل نبود کنترل پذیرش (Admission Control) در مسیر ورود دادهها باعث کرش سرور میشدند. این باگ اجازه میداد انفجارهای مداوم از دستههای حجیم، حافظه پروسه را تمام کنند (Out of Memory). نتایج نهایی به یک اصلاح خاص وابسته است — یک دروازه ورود (کامیت 14b4aa6e) که پردازشهای همزمان را محدود کرده و هنگام فشار زیاد، خطای ۵۰۳ را با هدر Retry-After برمیگرداند.
بدون این دروازه، سرعت بالای موتور ستونی در واقع علیه سیستم عمل میکند. SQLite هرگز این باگ را نشان نمیداد زیرا مسیر نوشتن کندتر آن زودتر شکست میخورد و به عنوان یک ترمز طبیعی، هرچند ناکارآمد، عمل میکرد. نتایج پیش از اصلاح، عملکرد DuckDB را کمتر از حد واقعی نشان میداد و بهویژه اسپنها را ۲۳٪ و لاگها را تقریباً نصف کاهش میداد، زیرا کرشها باعث پایان زودرس ریمپها میشدند.
برای کاربرانی که این مدل را استقرار میدهند، Traceway بیلد DuckDB را از طریق docker-compose.duckdb.yml توصیه میکند. یک نکته پیکربندی حیاتی: همان فایل compose با سقف محافظهکارانه ۲ گیگابایت حافظه و آستانه چکپوینت ۱۶ مگابایتی عرضه میشود. برای تطبیق با اعداد بنچمارک، کاربران باید بهطور دستی DUCKDB_MEMORY_LIMIT را روی ۴ گیگابایت و DUCKDB_CHECKPOINT_THRESHOLD را روی ۲۵۶ مگابایت تنظیم کنند.
تحلیل: پایان ضرورت «کلاینت-سرور»
این بنچمارک پیشفرضها را برای مشاهدهپذیری خود-میزبان (Self-hosted) بازنشانی میکند. برای سالها، انتخاب دوتایی بود: استفاده از یک DB جاسازیشده ساده و از دست دادن دادهها پس از چند ساعت، یا استقرار یک کلاستر پیچیده ClickHouse و مدیریت اشتهای شدید آن برای حافظه. DuckDB بهطور موثری یک مسیر میانی ایجاد میکند. با ارائه ۱۰۰ برابر ظرفیت خوانش SQLite در همان ردپای تک-باینری، اجازه میدهد یک «ناوگان کوچک» (ده بکاند که هر کدام ۵۰ اسپن/ثانیه، ۲۰۰ رکورد لاگ/ثانیه و ۱۰ نقطه متریک/ثانیه ارسال میکنند) برای مدتهای قابل توجه حفظ شوند:
- لاگها: در حدود ۸۵ دقیقه به سطح پذیرفتهشده ۱۰ میلیون ردیف و در حدود ۱۴ ساعت به سطح شکست میرسند (در مقایسه با پنجره ۵۰ ثانیهای در SQLite).
- اسپنها: در ۵.۵ ساعت به سطح پذیرفتهشده ۱۰ میلیون ردیف میرسند.
- متریکها: در ۱۱ روز به سطح پذیرفتهشده ۱۰۰ میلیون ردیف میرسند.
در این نرخ ناوگان، استفاده از ظرفیت برای DuckDB حداقلی است: ۰.۵٪ برای اسپنها، ۲.۷٪ برای لاگها و ۰.۰۴٪ برای متریکها. این موضوع در درجه اول به نفع Indie Hackerها و مهندسان DevOps تیمهای کوچک است و «مالیات زیرساختی» مشاهدهپذیری را حذف میکند.
حکم نهایی و ملاحظات
در حالی که بیلد DuckDB برای اکثر کاربران برنده است، بیلد SQLite در دو مورد خاص ترجیح دارد: توسعهدهندگانی که به باینری نیاز دارند که در هر جایی که Go کامپایل شود اجرا شود (چون DuckDB به CGO و glibc نیاز دارد و به جای Alpine به یک ایمیج Debian نیاز دارد)، یا کسانی که حجم دادههایشان بهطور راحت زیر دیوارهای خوانش SQLite میماند. اگر هیچ چکپوینت معوق یا پنجره هضمی برای انتظار وجود نداشته باشد، SQLite همچنان سادهترین ابزار برای کوچکترین کارهاست.
هنوز شکافهایی در دادهها وجود دارد. تستها تک-شوت بودند نه میانگین سه تکرار، و عملکرد خواندن تحت بار نوشتن همزمان یا بار سیگنالهای مختلط را اندازهگیری نکردند. صحت نتایج (مقایسه محتوا) و دوام بلندمدت در چندین هفته نیز در لیست کارهای آینده باقی مانده است. علاوه بر این، شکست در ۱ میلیارد ردیف نشان میدهد که آزمایشهای بیشتر روی بودجه حافظه ممکن است نتایج جدیدی بدهد.
در مرحله بعد، نویسنده مقایسه وعده داده شده با ClickHouse روی همان سختافزار و مولدهای بار را منتشر خواهد کرد تا تعیین کند انتقال به یک استک کامل کلاینت-سرور، در مقابل هزینه پیچیدگی، دقیقاً چه سود حاشیهای میبخشد.




گفتگو