تصور کنید یک جستوجوی ساده در پایگاهداده که باید در صدم ثانیه انجام شود، ناگهان ۱۲ ثانیه طول بکشد و در نهایت با خطا متوقف شود. این جهش عملکردی ۱۳۰ برابری زمانی رخ داد که یک تیم مهندسی از Claude برای تشخیص یک شکست خاموش در ایندکسگذاری هنگام مهاجرت از MySQL به PostgreSQL استفاده کرد. در واقع، جستوجویی که پیش از این پس از ۱۲ ثانیه با Timeout مواجه میشد، به زمانی کمتر از ۹۰ میلیثانیه کاهش یافت.
بسیاری از توسعهدهندگان تصور میکنند اگر یک ایندکس وجود داشته باشد، پایگاهداده حتماً از آن استفاده میکند. اما همانطور که در تحلیل قبلی ما دربارهی ابزارهای عیبیابی در عاملهای هوش مصنوعی اشاره کردیم و دیدیم که ابزارهایی مانند runtape چگونه نقاط شکست خاص را در عاملهای AI ایزوله میکنند، شکاف بین کد سطح بالا و اجرای سطح پایین جایی است که اکثر گلوگاههای محیط تولید (Production) پنهان میشوند. در این مورد، انتزاع ارائهشده توسط Django ORM — که شبیه به یک مترجم است که دستورات ساده ما را به زبان پیچیده پایگاهداده تبدیل میکند — یک عدم تطابق بنیادی در نحوه کوئری زدن به دادهها ایجاد کرده بود و این موضوع باعث شد مشکل برای مدتها پنهان بماند.
شکاف عملکردی
تیم مهندسی ابتدا با استفاده از New Relic عملیات آسیبدیده را شناسایی کرد تا دقیقاً بفهمند کدام بخش از برنامه کند شده است، در حالی که CloudWatch و معیارهای RDS بستر منابع و وضعیت سختافزاری را تحلیل میکردند.
طبق گزارش تیم، در حالی که سیستم ۴۹۰۸ درخواست در دقیقه را روی یک نمونه db.r6i.large پردازش میکرد، معیارهای استخراج شده تفاوت فاحشی را بین محیط قدیمی MySQL و محیط جدید PostgreSQL نشان دادند:
- مصرف CPU پایگاهداده: از ۴۲.۷٪ در MySQL به ۹۴.۸٪ در PostgreSQL جهش کرد.
- میانگین زمان درخواست: از ۷۲ میلیثانیه به ۴۴۴ میلیثانیه افزایش یافت.
- زمان کوئری لیست محصولات: در ساعات پیک از ۱۱۲ میلیثانیه به ۴.۶۷ ثانیه رسید.
این اعداد ابعاد مشکل را به طور دقیق مشخص کردند، اما دلیل وقوع آن را فاش نکردند. حتی با وجود اندازه مشابه نمونهها و میزان ترافیک یکسان، متغیرهایی مثل ترکیب کوئریها (Query Mix)، تنظیمات پیکربندی و برنامههای اجرا (Execution Plans) همچنان ناشناخته بودند و نیاز به بررسی عمیقتر داشتند. این چالشها یادآور تجربههای مشابهی است که در بهینهسازی تأخیر پرسوجوهای Postgres با مدلهای کوچکتر مشاهده کردیم، جایی که حتی تغییرات کوچک در مدل تحلیل میتواند تأثیرات قابلتوجهی بر عملکرد دیتابیس داشته باشد.
عدم تطابق پنهان
بررسیها روی یک جستوجوی استاندارد بدون حساسیت به حروف بزرگ و کوچک متمرکز شد که در کد به صورت query = query.filter(product_name__icontains=value) نوشته شده بود. با وجود اینکه تیم ایندکسهای pg_trgm (trigram) — که شبیه به خرد کردن کلمات به تکههای سه حرفی برای جستوجوی سریعتر است — را روی ستونهای خام ایجاد کرده بود، اما SQL تولیدشده توسط Django تغییری ایجاد میکرد که این ایندکسها را کاملاً بیفایده میکرد.
جزئیات فنی شکست
برای درک دقیقتر این شکست، باید به جزئیات پیادهسازی نگاه کنیم:
- ایندکس: تیم ایندکسهای trigram را روی ستونهای خام با استفاده از عباراتی مانند
product_name gin_trgm_opsوitemid gin_trgm_opsایجاد کرده بود. - SQL تولیدشده: جنگو برای اجرای
icontainsعبارتUPPER(product_name::text) LIKE UPPER('%term%')را تولید میکرد. - تضاد: تبدیل
UPPER()جزئیات حیاتی و نقطه شکست بود. ایندکس روی ستون خام (Raw Column) ایجاد شده بود، اما کوئری روی نسخه حروف بزرگ (Uppercase) ستون جستوجو میکرد.
به نقل از خروجی EXPLAIN، این عدم تطابق باعث شد کوئری از ایندکس trigram عبور کند و نادیده بگیرد آن را. در نتیجه PostgreSQL مجبور شد تمام محصولات هر مشتری (Tenant) را بخواند و فیلتر متنی را ردیف به ردیف (Row by Row) اعمال کند. این موضوع ثابت میکند که صرفاً چک کردن وجود یک ایندکس در دیتابیس کافی نیست؛ بلکه باید درک کرد که آیا کوئری در زمان اجرا واقعاً میتواند از آن استفاده کند یا خیر.
تحلیل ریشه با کمک هوش مصنوعی
تیم به جای درخواست یک راهکار کلی یا پرسیدن «چرا کوئری من کند است؟»، خطای دقیق، عملیات آسیبدیده، برچسب زمانی (Timestamp) و کد مربوط به برنامه را به Claude داد. آنها همچنین تفاوت بین «آنچه انتظار داشتند اتفاق بیفتد» و «آنچه در واقعیت رخ داد» را شرح دادند.
آنها از استراتژی پرامپتنویسی خاصی استفاده کردند: «این شواهد چه چیزی را ثابت میکنند، چه توضیحاتی هنوز ممکن است و برای تمایز آنها باید چه چیزی را بررسی کنیم؟» این رویکرد باعث شد بررسیها به صورت گامبهگام و با رسیدن شواهد جدید پیش برود.
وقتی کد ORM به تنهایی شواهد محدودی ارائه داد، آنها SQL تولیدشده، تعریف ایندکس و برنامه کوئری (Query Plan) را با هم ارائه کردند و با این پرامپت ادامه دادند: «عبارت مورد فیلتر را با عبارت ایندکسشده مقایسه کن. توضیح بده کدام گرههای برنامه (Plan Nodes) از عدم تطابق ایندکس پشتیبانی میکنند و هر فرضیهای که هنوز نیاز به تایید دارد را شناسایی کن.»
این رویکرد مدل را مجبور کرد تا توضیحات خود را به شواهد مشاهدهپذیر — یعنی Query Plan — گره بزند، نه اینکه بر اساس کد ORM حدس بزند. این متد دقیقاً مشابه روش تست فرضیه در راهنمای Google SRE برای عیبیابی موثر است که بر پایه حذف احتمالات نادرست پیش میرود.
راهکار فنی
برای رفع این گلوگاه، دو تغییر حیاتی برای همراستاسازی کوئری با ایندکس موجود اعمال شد:
۱. تغییر کوئری: جایگزینی icontains با contains. این کار باعث شد تبدیل UPPER() از SQL حذف شود و کوئری مستقیماً روی ستون خام اجرا شود.
۲. تغییر پایگاهداده: پیکربندی Collation (قواعد مرتبسازی و مقایسه) بدون حساسیت به حروف بزرگ و کوچک روی ستون product_name. این اقدام تضمین کرد که رفتار جستوجو برای کاربر نهایی تغییر نکند و همچنان نتایج مورد نظر را دریافت کند.
هر دو بخش ضروری بودند؛ زیرا یک کوئری سریع اگر نتایج مورد انتظار کاربر را برنگرداند، بیفایده است. تیم توصیه هوش مصنوعی را با یک شرط سختگیرانه پذیرفت: مدل باید هم مکانیسم بهبود عملکرد و هم رفتاری که نباید تغییر کند (مانند حساسیت به حروف و عبارات جستوجوی نماینده) را به طور کامل توضیح دهد.
اعتبارسنجی و نتایج
تست در محیط Staging با دادههای یک شرکت که دارای تقریباً ۱.۱۵ میلیون محصول بود، نتایج را تایید کرد:
- عملکرد جدید: کوئریهای
LIKEروی ستون خام از ایندکسهای GIN trigram استفاده کردند و در بازه ۷۷ تا ۹۰ میلیثانیه تکمیل شدند. - عملکرد قدیمی: کوئریهای قبلی که از
UPPER(...) LIKE UPPER(...)استفاده میکردند، از سقف زمانی ۱۲ ثانیهای پایگاهداده فراتر میرفتند و با Timeout متوقف میشدند.
این یعنی بهبود عملکرد در کمترین حالت بیش از ۱۳۰ برابر است. مقدار دقیق افزایش سرعت قابل محاسبه نیست، زیرا کوئریهای قدیمی پیش از آنکه به پایان برسند، توسط سیستم متوقف میشدند.
ایجاد یک حلقه بازخورد
در اینجا است که یک بررسی به کمک هوش مصنوعی به یک حلقه بازخورد (Feedback Loop) نیاز دارد. تیم برنامه جدید (Plan)، زمانبندیها و نتایج صحت دادهها را به دستیار AI بازگرداند و پرسید که آیا این نتایج از توضیح پیشنهادی پشتیبانی میکنند و چه مواردی هنوز حل نشده باقی مانده است. این کار کمک کرد تا کالیبره شود که آیا دادههای جدید با اصلاحات اعمال شده مطابقت دارند یا خیر.
انتقال متد به خطاهای بعدی
اگرچه یافته نهایی — یعنی عدم تطابق بین عبارات کوئری و ایندکس — مختص این سناریو بود، اما متد عیبیابی به طور گسترده قابل اجراست. هنگام استفاده از AI برای خطاهای ناشناخته، فرآیند باید به این صورت باشد:
۱. با عملیات شکستخورده شروع کنید.
۲. کد مربوطه و شواهد زمان اجرا (Runtime Evidence) را جمعآوری کنید.
۳. برای توضیحات قابل تست (Testable Explanations) درخواست کنید.
۴. نتایج را به بررسی بازگردانید تا فرضیات تایید یا رد شوند.
برای یک تیم مهندسی کوچک، ارزش ماندگار در تکرارپذیر کردن این بررسیهاست. با حفظ علائم، شواهد، تغییرات و چکهای اعتبارسنجی، هر شکست ناشناخته بعدی با مجموعه بهتری از سوالات و روشی تستشده برای پاسخ به آنها آغاز میشود.
گام بعدی شما
- هنگام مشاهده کندی کوئریها، به جای اعتماد به ORM، خروجی
EXPLAIN ANALYZEرا مستقیماً بررسی کنید. - در پرامپتهای عیبیابی، از مدل بخواهید «تضاد بین عبارت ایندکسشده و عبارت فیلترشده» را تحلیل کند.
- برای جستوجوهای متنی در PostgreSQL، ترکیب
containsوCase-insensitive Collationرا جایگزینicontainsکنید.
اما داستان سختافزاری این تحول حتی شگفتانگیزتر است — به تحلیل ما دربارهی تراشههای Blackwell مراجعه کنید.




گفتگو