Skip to content

فصل ۶ — تجمیع رویداد کلیک تبلیغ (Ad Click Event Aggregation)

خلاصهٔ تحلیلی — این متن چکیدهٔ مفاهیم فصل به زبان فارسی است، نه ترجمهٔ واژه‌به‌واژه.

در تبلیغات دیجیتال، فرایند RTB (حراج بلادرنگ) در کمتر از یک ثانیه انجام می‌شود و متریک‌هایی مثل CTR و CVR بر دادهٔ تجمیعی کلیک‌ها تکیه دارند — یعنی نتیجهٔ تجمیع مستقیماً روی پولی که تبلیغ‌دهنده می‌پردازد اثر می‌گذارد. این فصل یک سیستم تجمیع کلیک در مقیاس فیس‌بوک/گوگل طراحی می‌کند.

گام ۱ — نیازمندی‌ها

  • ورودی: فایل‌های لاگ روی سرورهای مختلف؛ هر رویداد شامل ad_id، click_timestamp، user_id، ip، country.
  • سه کوئری اصلی: تعداد کلیک‌های یک آگهی در M دقیقهٔ اخیر؛ ۱۰۰ آگهی پرکلیک در M دقیقهٔ اخیر (تجمیع هر دقیقه)؛ فیلتر هر دو بر اساس ip/user_id/country.
  • غیرکارکردی: صحت (مبنای صورتحساب است)، مدیریت رویدادهای دیررس و تکراری، تاب‌آوری در برابر خرابی جزئی، و تأخیر انتها-به-انتها حداکثر چند دقیقه (برخلاف RTB که زیر ثانیه است).
  • برآورد: ۱ میلیارد کلیک در روز ≈ ۱۰ هزار QPS (پیک ۵۰ هزار)؛ روزی ~۱۰۰ گیگابایت دادهٔ خام؛ رشد ۳۰٪ سالانه.

گام ۲ — طراحی سطح بالا

API و مدل داده

دو endpoint کفایت می‌کند (فیلتر با query parameter):

  • GET /v1/ads/{ad_id}/aggregated_count با from/to/filter
  • GET /v1/ads/popular_ads با count/window/filter

خام یا تجمیعی؟ هر دو. دادهٔ خام پشتوانهٔ دیباگ و بازمحاسبه است (و بعداً به cold storage می‌رود)؛ دادهٔ تجمیعی دادهٔ فعال کوئری‌هاست. فیلترها با star schema پیاده می‌شوند: به ازای هر ترکیب بُعد (مثلاً کشور) ردیف تجمیعی جدا — ساده و سریع، به بهای انفجار تعداد رکوردها با افزایش ابعاد.

انتخاب دیتابیس: سیستم write-heavy است (خواندن دادهٔ خام کم است)؛ Cassandra یا InfluxDB مناسب‌اند (رابطه‌ای در نوشتن سنگین مشکل دارد). گزینهٔ دیگر: S3 با فرمت ستونی (Parquet/ORC). دادهٔ تجمیعی هم read-heavy و هم write-heavy است و همان دیتابیس جواب می‌دهد.

معماری

log watcher ← صف پیام ۱ (رویدادهای خام) ← سرویس تجمیع ← صف پیام ۲ (نتایج دقیقه‌ای: شمارش و top-N) ← database writer ← دیتابیس تجمیع ← سرویس کوئری/داشبورد.

  • پردازش ناهمگام با دو صف، producer و consumer را جدا می‌کند تا موج ترافیک، مصرف‌کننده‌ها را از پا نیندازد.
  • صف دوم برای رسیدن به exactly-once انتها-به-انتها (atomic commit) لازم است، نه نوشتن مستقیم در دیتابیس.

سرویس تجمیع — الگوی MapReduce (DAG)

  • Map: خواندن از منبع، پاک‌سازی/نرمال‌سازی و پارتیشن‌بندی بر اساس ad_id (لازم است چون شاید کنترلی روی نحوهٔ پارتیشن‌بندی تولید رویدادها نداشته باشیم).
  • Aggregate: شمارش در حافظه به ازای هر دقیقه؛ برای top-N هر گره یک heap نگه می‌دارد.
  • Reduce: ادغام نتایج گره‌های Aggregate به نتیجهٔ نهایی (مثلاً top-3 از سه گرهٔ top-3).

گام ۳ — بررسی عمیق

Streaming در برابر batching؛ لامبدا و کاپا

سرویس‌های آنلاین به کاربر پاسخ می‌دهند؛ batch (مثل MapReduce) ورودی محدود و throughput-محور دارد؛ streaming (مثل Flink) ورودی بی‌کران و near-real-time. این طراحی هر دو مسیر را دارد: streaming برای تجمیع زنده و batch برای پشتیبان تاریخی. دو مسیرِ همزمان = معماری لامبدا (دو codebase)؛ کاپا هر دو را در یک موتور استریم یکی می‌کند — طراحی ما کاپاست: بازمحاسبه (replay) هم از راه همان سرویس تجمیع اما با instance اختصاصی و منبع دادهٔ خام انجام می‌شود تا مسیر زنده مختل نشود.

زمان رویداد در برابر زمان پردازش

به دلیل تأخیر شبکه و صف، فاصلهٔ این دو می‌تواند زیاد باشد. زمان رویداد دقیق‌تر است اما به ساعت کلاینت (و کاربران بدخواه) وابسته است؛ زمان پردازش قابل اعتمادتر اما برای رویداد دیررس غلط است. چون صحت حیاتی است، زمان رویداد + watermark انتخاب می‌شود: پنجرهٔ تجمیع چند ثانیه (مثلاً ۱۵ ثانیه، قابل تنظیم) تمدید می‌شود تا رویدادهای کمی دیررس جا بمانند. watermark طولانی‌تر = دقت بیشتر/تأخیر بیشتر. رویدادهای خیلی دیررس ارزش پیچیده‌کردن طراحی را ندارند و با reconciliation پایان روز جبران می‌شوند.

پنجرهٔ تجمیع

از چهار نوع پنجره، دو نوع اینجا به کار می‌آید: tumbling (قطعات هم‌طول بی‌همپوشان — برای شمارش دقیقه‌ای) و sliding (پنجرهٔ لغزان با همپوشانی — برای top-N در M دقیقهٔ اخیر).

تضمین تحویل و تکرارزدایی

اختلاف چند درصدی می‌تواند میلیون‌ها دلار خطا در صورتحساب بسازد ← exactly-once لازم است. منابع تکرار: retry کلاینت (بحث ضدتقلب جداست) و سقوط aggregator. تحلیل ترتیب ذخیرهٔ offset:

  • ذخیرهٔ offset در Kafka بالادستی پس از ارسال نتیجه: اگر ack نهایی شکست بخورد، مصرف دوباره ← تکرار.
  • ذخیرهٔ offset در storage خارجی (HDFS/S3) قبل از ارسال نتیجه: اگر ارسال شکست بخورد ← از دست رفتن داده.
  • راه درست: ذخیرهٔ offset پس از دریافت ack پایین‌دستی، و برای exactly-once واقعی، قرار دادن «ارسال نتیجه + ذخیرهٔ offset + ack» در یک تراکنش توزیع‌شده.

مقیاس‌دهی

  • صف: رشد ۳۰٪ سالانه یعنی هر ~۳ سال دو برابر. Consumer ها با rebalancing گروه اضافه می‌شوند (با صدها consumer، rebalance کند است — در ساعت کم‌بار انجام شود). ad_id کلید هش پارتیشن است؛ پارتیشن‌ها از قبل به اندازهٔ کافی تخصیص یابند؛ در صورت نیاز topic بر اساس جغرافیا یا نوع کسب‌وکار شکسته شود.
  • سرویس تجمیع: افقی مقیاس‌پذیر؛ افزایش throughput یا با multi-threading (هر thread یک بازهٔ ad_id) یا استقرار روی resource provider مثل YARN (رایج‌تر).
  • دیتابیس: Cassandra با virtual node ها به‌طور بومی افقی مقیاس می‌گیرد؛ افزودن نود rebalance خودکار دارد.
  • Hotspot: آگهی‌های شرکت‌های بزرگ کلیک بسیار بیشتری دارند؛ گرهٔ پرفشار از resource manager منابع اضافه می‌گیرد و رویدادها را بین گره‌های جدید تقسیم می‌کند (روش‌های پیشرفته‌تر: Global-Local Aggregation، Split Distinct Aggregation).

تحمل خطا و صحت

  • تجمیع در حافظه است؛ پس از سقوط، replay کامل از Kafka کند است ← snapshot دوره‌ای از «وضعیت سیستم» (offset بالادستی + دادهٔ top-N) گرفته می‌شود و گرهٔ جدید از آخرین snapshot + replay رویدادهای بعدش بازیابی می‌کند.
  • پایش: تأخیر مرحله‌به‌مرحله (با timestamp گذاری در طول مسیر)، عمق صف (records-lag در Kafka) و منابع گره‌ها.
  • Reconciliation: چون طرف سومی برای مقایسه نیست، یک batch job پایان روز رویدادها را بر اساس زمان رویداد مرتب و دوباره تجمیع می‌کند و با نتیجهٔ real-time مقایسه می‌کند (تطابق صددرصدی به دلیل رویدادهای دیررس ممکن نیست).

طرح جایگزین

ذخیرهٔ کلیک‌ها در Hive + لایهٔ Elasticsearch برای کوئری سریع + تجمیع در OLAP هایی مثل ClickHouse یا Druid — در مصاحبهٔ عمومی، توضیح trade-off ها مهم‌تر از دانستن جزئیات ابزارهای تخصصی است.

نکات کلیدی فصل

  • وقتی خروجی مبنای پول است، exactly-once و reconciliation دیگر «امکانات» نیستند؛ بخشی از تعریف مسئله‌اند.
  • زمان رویداد + watermark مصالحهٔ استاندارد دقت-تأخیر برای رویدادهای دیررس است.
  • ترتیب «ارسال نتیجه، ack، ذخیرهٔ offset» تعیین می‌کند سیستم داده گم می‌کند یا تکرار می‌کند — این را باید بتوانید روی وایت‌برد اثبات کنید.
  • معماری کاپا با «همان کد، منبع متفاوت» بازمحاسبهٔ تاریخی را بدون codebase دوم حل می‌کند.
  • snapshot از وضعیت درون-حافظه‌ای، بازیابی سریع را ممکن می‌کند؛ replay از صفر آخرین راه‌حل است.

یادداشت‌های مترجم

  • اصطلاحات RTB، watermark، tumbling/sliding window، exactly-once و star schema لاتین حفظ شده‌اند.