SQL شما سریع است اما API کند است

پرس‌وجوهای پایگاه داده شما سریع هستند. سیستم کشینگ شما کار می‌کند. کارهای پس‌زمینه شما هم مشکلی ندارند.

با این حال، API شما همچنان کند است. میزان استفاده از CPU بالا است.

مشکل از پایگاه داده نیست. مشکل از لایه Ruby است.

گلوگاه (bottleneck) اغلب زمانی رخ می‌دهد که داده‌ها از پایگاه داده خارج شده و وارد اپلیکیشن شما می‌شوند. این اتفاق از طریق سه مشکل اصلی رخ می‌دهد:

  • سریال‌سازی سنگین (Bloated serialization)
  • تخصیص بیش از حد اشیاء (Excessive object allocation)
  • محاسبات تکراری

در اینجا روش رفع آن‌ها آمده است.

  1. از سریال‌سازی سنگین جلوگیری کنید

بسیاری از توسعه‌دهندگان کل مدل‌ها را به JSON تبدیل می‌کنند.

render json: @shipments

اگر یک محموله (shipment) دارای ۴۰ ستون باشد اما فرانت‌اند شما فقط به ۵ مورد نیاز داشته باشد، چرخه‌های CPU را هدر می‌دهید. همچنین خطر نشت داده‌های خصوصی مانند کلیدهای API یا هزینه‌ها وجود دارد.

راه حل: فقط فیلدهایی را که نیاز دارید برگردانید.

render json: @shipments.as_json(only: [:id, :tracking_no, :status])

برای سرعت حتی بیشتر، از pluck برای دریافت داده‌ها به صورت آرایه استفاده کنید. این کار باعث می‌شود از ساخت اشیاء سنگین ActiveRecord کاملاً جلوگیری شود.

  1. تخصیص اشیاء را کاهش دهید

هر شیئی که Ruby ایجاد می‌کند، حافظه مصرف می‌کند. ایجاد هزاران شیء در طول یک درخواست واحد، Garbage Collector (GC) را مجبور می‌کند تا سخت‌تر کار کند. این امر کل سیستم شما را کند می‌کند.

از ساخت هش‌ها (hashes) یا رشته‌های (strings) جدید در داخل حلقه‌ها خودداری کنید.

بد:

@shipments.map do |s|
  { label: "#{s.tracking_no} - #{s.status.upcase}" }
end

خوب: داده‌های ایستا (static) را به بیرون از حلقه منتقل کنید. به جای انجام کار در Ruby، بیشتر کارها را در پایگاه داده انجام دهید.

  1. از محاسبات تکراری خودداری کنید

اگر یک متد را چندین بار در یک درخواست فراخوانی کنید، زمان را هدر می‌دهید.

مثال:

def total_weight
  shipments.sum(&:weight)
end

اگر view، helper و serializer شما همگی این متد را فراخوانی کنند، مجموع را سه بار محاسبه می‌کنید.

راه حل: از memoization استفاده کنید.

def total_weight
  @total_weight ||= shipments.sum(&:weight)
end

این کار تضمین می‌کند که محاسبات ریاضی فقط یک بار در هر درخواست انجام شود.

جدول خلاصه:

  • سریال‌سازی سنگین: فقط فیلدهای مورد نیاز را برگردانید یا از pluck استفاده کنید.
  • تخصیص بالا: اشیاء کمتری در حلقه‌ها بسازید.
  • محاسبات تکراری: از memoization برای استفاده مجدد از نتایج استفاده کنید.

بهینه‌سازی پایگاه داده یعنی درخواست داده‌های کمتر. بهینه‌سازی اپلیکیشن یعنی پس از دریافت داده‌ها، کارهای اضافی کمتری انجام دهید.

منبع: https://dev.to/danewu/your-sql-is-fast-but-the-api-is-slow-its-the-ruby-layer-2fno