چالشهای شمارش بازدید در مقیاس بزرگ و بررسی روشهای کلاسیک و مدرن

نمایش تعداد بازدید یک ویدیو یا محصول در ظاهر کار سادهایست؛ کافی است یک فیلد view_count در دیتابیس داشته باشیم و با هر بازدید، آن را افزایش دهیم. اما هنگامی که:
📌میلیونها کاربر همزمان بازدید ارسال میکنند
📌ویدیویی مانند «Baby Shark» با ۱۶ میلیارد بازدید دارید
📌و نیاز دارید آمار را زیر ۲۰۰ms نمایش دهید
چالشهای اساسی زیر پیش میآید:
⚠️مقیاسپذیری (Scalability): ثبت میلیونها رویداد در ثانیه بدون نقطهی گلوگاه
⚠️عملکرد (Latency): پاسخ به کاربر در کمتر از ۲۰۰ میلیثانیه
⚠️تکرارناپذیری (Idempotency): جلوگیری از شمارش یک بازدید بیش از یکبار
⚠️ماندگاری داده (Durability): حفظ رویدادها حتی در صورت خرابی سرویس
⚠️دقت نهایی (Accuracy): تضمین دقت آمار با حداکثر تأخیر ۱۰ دقیقه
بیایید روشهای سنتی پیاده سازی این موضوع را با هم بررسی کنیم :
۱️⃣ مدل ساده با یک دیتابیس و فیلد view_count
در سادهترین حالت، یک جدول دیتابیس رابطهای (مثلاً MySQL یا پستگرس) داریم که برای هر آیتم (مثلاً ویدیو) یک ردیف با فیلدی به نام view_count در آن ذخیره شده. با هر بازدید، این فیلد را با یک UPDATE افزایش میدهیم.
✅ چرا مناسب است؟
- پیادهسازی بسیار ساده
- برای MVP یا پروژههای آزمایشی سریعترین راه
❌ محدودیتها - نقطهی شکست واحد: اگر دیتابیس از کار بیفتد همهچیز متوقف میشود
- ظرفیت پایین: عدم توان پاسخگویی به صدها هزار درخواست در ثانیه
- بدون کنترل تکراری: هر رفرش صفحه دوبارهکاری میکند
۲️⃣ شاردینگ (Sharding) دیتابیس
در این روش، دادهها را بین چند دیتابیس (شارد) تقسیم میکنیم.
- با کلید video_id % N، هر ویدیو به یکی از N شاردها میرسد
- هر آپدیت یا خواندن، تنها به شارد مربوطه هدایت میشود
✅ مزایا
- توزیع بار روی چند سرور
- مقیاسپذیری خطی با افزایش شاردها
❌ معایب
- هاتپارتیشن: ویدیوهای وایرال ترافیک بیش از حد به یک شارد میفرستند
- خواندن توزیعشده: جمعآوری آمار از چند شارد پیچیده و کند میشود
- همچنان بدون کنترل کامل تکراری
۳️⃣ تجمیع در حافظه با Cache
رویدادهای بازدید ابتدا به کش (مثلاً Redis) میروند:
- روی هر بازدید، یک کلید یکتا (user_id:video_id) با TTL کوتاه تنظیم میکنیم تا تکراری نشمارد
- در حافظه، بازدیدها را جمع میکنیم
- هر ۱۰ دقیقه یک بار، مجموع را در دیتابیس اصلی آپدیت میکنیم
✅ مزایا
- سرعت بالا: خواندن/نوشتن در حافظه زیر ۱ میلیثانیه
- کاهش بار دیتابیس به دلیل آپدیت گروهی
کنترل تکراری: با TTL در Redis
❌ معایب
- ریسک از دست رفتن داده در صورت کرش سرویس کش
- مدیریت TTL و همگامسازی کش با دیتابیس پیچیده
۴️⃣ معماری رویدادمحور با Kafka + Flink + Redis
در این معماری، هر بازدید یک رویداد است:
- -کافکا: دریافت رویداد و نگهداری آن با پارتیشنبندی روی video_id
- فلینک: پردازش جریانی، تجمیع هر ۱۰ دقیقه و ارسال خروجی
- ردیس: نگهداری کلیدهای یکتا برای حذف رویدادهای تکراری
- دیتابیس/Cache: ذخیرهشماری نهایی برای پاسخ لحظهای
✅ مزایا
- مقیاسپذیری افقی بینهایت با Kafka/Flink
- امکان بازیابی کامل رویدادها (Durability)
- کنترل تکراری با Redis
- نمایش زیر ۲۰۰ms با Cache
❌ چالشها
- پیچیدگی عملیاتی: مدیریت خوشههای Kafka و Flink
- تأخیر جزئی در لحظههای انفجاری (Viral Burst)
🎯 این معماری فقط برای شمارش بازدید ویدیو نیست!
برای لایک، کلیک تبلیغ، بازدید صفحه و حتی شمارنده تعاملات در اپلیکیشنهای اجتماعی یا فروشگاهها هم کاربرد دارد.
تا اینجا بهطور خلاصه گفتیم که در بار ترافیکی بالا، بهتر است بازدیدها را در حافظه نگهداری و جمعبندی کرده، سپس در بازههای زمانی مشخص وارد دیتابیس کنیم. همچنین به رویکرد پیشرفتهتری با Kafka + Flink برای ایجاد بافر و بروزرسانی دورهای دیتابیس اشاره شد.
اما بیایید به سراغ راهکارهای مدرنتر برویم. پیشرفتهای اخیر در استکهای داده، امکانات جدیدی برای ما فراهم کرده که فقط محدود به شمارش ساده نیستند.
🎯 هدف ما فقط شمارش نیست!
آنچه امروز اهمیت دارد، ذخیرهسازی دقیق تمام اکشنهای کاربر است.
چرا؟
✅برای شخصیسازی تجربه کاربری بر اساس رفتار هر فرد
✅برای تحلیل عمیق روی محصولات یا ویدئوها و بهبود تجربه کاربران
پس راهکار ایدهآل باید هم شمارش و هم ذخیرهسازی کامل دادهها را پوشش دهد.
🛠 سه راهکار مدرن برای شمارش و ذخیره اکشنها
۱️⃣ استفاده از Cassandra / ScyllaDB و قابلیت Distributed Counter
🎯برای هر کاربر و هر محصول، یک جدول بازدید ایجاد میکنیم
🎯هر اکشن را در هر دو جدول ذخیره میکنیم (مدل داده این دیتابیسها بر اساس Query طراحی میشود)
🎯شمارش اکشنها با Distributed Counter انجام میشود
🎯امکان تعریف شمارنده برای بازههای زمانی مختلف (ساعتی، روزانه و…) وجود دارد
✅مزیت اصلی: مقیاسپذیری بالا و سرعت فوقالعاده
۲️⃣ ذخیره خام دادهها در قالب Apache Iceberg با AutoMQ
🎯جایگزین Kafka سنتی با AutoMQ
🎯 پیام رسان AutoMQ که دقیقا منطبق بر استاندارد کافکا است، پیامها را مستقیماً در Iceberg ذخیره میکند
🎯شمارش با Flink + Redis انجام میشود
🎯امکان تحلیل بعدی رفتار کاربران با ابزارهایی مثل ClickHouse یا Spark
✅مزیت اصلی: فشار کمتر روی دیتابیس اصلی و نگهداری دادههای خام برای تحلیلهای آینده
۳️⃣ استفاده از دیتابیس جریانی RisingWave – سریع، مدرن و چندکاره 🚀
دیتابیس RisingWave یک دیتابیس جریانی (Streaming Database) است که با استاندارد PostgreSQL توسعه یافته و از SQL بهعنوان زبان اصلی پردازش دادههای جریانی استفاده میکند.
📌 ویژگیها و مزایا:
🎯شمارش و پردازش جریانی با SQL ساده → ایجاد Materialized Viewها برای شمارش بازدیدها و اکشنها در لحظه
🎯ذخیره اکشنها در S3 و Iceberg → امکان نگهداری دادههای خام برای تحلیلهای آینده
🎯سرعت بالا به لطف Rust → هسته سیستم با زبان Rust نوشته شده و از مزایای کارایی و مصرف کم منابع بهره میبرد
🎯پشتیبانی از Sinkهای متنوع → خروجی مستقیم به دیتابیسها، سیستمهای پیامرسان، S3، Kafka و…
🎯پردازش رویدادهای پیچیده → اجرای Queryهای تحلیلی پیشرفته بر روی جریان داده بدون نیاز به ابزار جداگانه
✅ نتیجه؟
با RisingWave میتوان علاوه بر شمارش بازدید و اکشنها، بسیاری از پردازشهای همزمان و تحلیلهای اولیه را نیز انجام داد، بدون نیاز به زیرساخت پیچیده و چندلایه.
📌 جمعبندی
این سه راهکار نسبت به روشهای سنتی و حتی رویکرد Kafka + Flink، مدرنتر هستند و از فناوریهای جدید حوزه داده بهره میبرند.
اگر در حال طراحی یا ارتقای بخش شمارش بازدید و اکشنها هستید، پیشنهاد میکنم این گزینهها را نیز بررسی کنید.