🚖 چگونه Uber با ترکیب Apache Spark و Ray، عملکرد سیستم خود را بهبود داد؟
چگونه Uber با Ray سرعت پردازش دادهها را ۴۰ برابر کرد؟

در دنیای مدیریت داده بخصوص در بخش زیرساختهای پردازشی، بررسی راهکارهای شرکتهای بزرگ میتواند دید مناسبی در انتخاب ابزارهای مناسب به ما بدهد. هرچند ممکن است این راهکارها دقیقاً برای همه کسبوکارها قابل استفاده نباشند—زیرا شرکتهای بزرگ معمولاً وابستگیهای عمیقی به فناوریهای قدیمی دارند که ممکن است برای ما یک محدودیت نباشد—اما بررسی و آشنایی با آنها به شناخت بهتر ابزارهابخصوص یافتن نقاط قوت و ضعف آنها کمک شایانی میکند.
در این نوشتار، به بررسی رویکرد Uber در بهینهسازی بخشی از خطوط پردازش داده خود میپردازیم؛ جایی که ترکیب Apache Spark و Ray، سرعت اجرای یکی از فرآیندهای کلیدی را ۴۰ برابر افزایش داد!
مقاله اصلی را که در ژانویه ۲۰۲۵ منتشر شده است، از لینک زیر میتوانید مطالعه کنید :
https://www.uber.com/blog/how-uber-uses-ray-to-optimize-the-rides-business
🚖 مشکل چه بود؟
Uber برای تنظیم بودجه تخفیف مسافران و مشوقهای رانندگان، نیاز به انجام محاسبات پیچیدهای داشت. این محاسبات باید هر هفته انجام میشد و پارامترهای آنها به ازای هر شهر متفاوت بود. هر محاسبه سبک و سریع بود (حدود ۱-۲ ثانیه برای هر شهر)، اما وقتی این کار باید برای هزاران شهر انجام میشد، سیستم موجود که بر اساس آپاچی اسپارک ایجاد شده بود، به شدت کند عمل میکرد.
دلایل اصلی کندی سیستم:
- Spark برای اجرای توابع سبک و پرتکرار بهینه نبود؛ این ابزار برای پردازشهای حجیم طراحی شده است و سربار بالایی برای وظایف کوچک ایجاد میکرد.
- اجرای کدهای Python/Pandas در Spark دشوار بود؛ از طرفی اجرای محاسبات ریاضی بر روی دادههای موجود در دیتافریمها (ساختمان داده اصلی اسپارک)، نیاز به Pandas UDF داشت که کارایی مورد انتظار را ارائه نمیداد و همین بخش از کار هم کندی کل فرآیند محاسبه تخفیفها و مشوقها را به همراه داشت.
گزینههای اولیه
اوبر در ابتدا برای رفع این مشکل، سه گزینه را بررسی کرد:
۱️⃣ ادامه استفاده از Apache Spark: از آنجا که Spark اجرای توابع Python را بهصورت موازی (بدون استفاده از APIهای مخصوص Spark) پشتیبانی نمیکند، تمام این توابع بهینهسازی برای شهرهای مختلف فقط روی نود اصلی Spark (Driver Node) و بهصورت سریالی اجرا شوند، که باعث کاهش کارایی میشد.
۲️⃣ استفاده از Pandas UDF در Spark: این روش تا حدی سرعت اجرای عملیات روی DataFrameهای Pandas را افزایش میداد، اما بهبود عملکرد چشمگیر نبود. علاوه بر این، Pandas UDF نمیتواند کدهای عمومی Python را بهصورت موازی اجرا کند، که محدودیت بزرگی محسوب میشد.
۳️⃣ اجرای یک Job مستقل برای هر شهر: این روش مستلزم اجرای یک کانتینر Docker برای هر شهر بود. اما این راهکار به دلیل سربار راهاندازی بالا و مصرف نامتناسب منابع محاسباتی، کارآمد نبود.
🚀 در نهایت، اوبر با ترکیب Spark و Ray توانست این چالشها را برطرف کند و به عملکردی بهینهتر دست یابد.
⚡ چرا Uber از Ray در کنار Spark استفاده کرد؟
برای حل این مشکل، Uber تصمیم گرفت از یک راهحل ترکیبی استفاده کند. برای اتخاذ این تصمیم، اوبر این دو نکته را در نظر گرفت :
✔ Apache Spark برای پردازشهای حجیم (ETL، تبدیل دادهها، ذخیرهسازی در HDFS) عالی است، اما برای اجرای وظایف کوچک با فرکانس بالا، عملکرد مناسبی ندارد.
✔ Ray در اجرای موازی وظایف سبک و کوتاهمدت فوقالعاده است، زیرا:
- امکان موازیسازی کدهای Python را بدون نیاز به تغییرات پیچیده فراهم میکند. (یک مثال ساده از Ray را در انتهای این نوشته میبینید)
- مدیریت منابع را برای وظایف سبک و پرتکرار بهینه میکند.
- اجرای سریعتر را بدون نیاز به Docker و تغییر ساختار کد تسهیل میکند.
معرفی RAY : کتابخانه ای برای پردازش توزیع شده
چندی پیش وقتی گزارش Oreilly راجع به دستمزدها و پرداختی های حوزه پردازش داده/هوش مصنوعی را بررسی می کردم در بخش کتابخانه های پایتون که آشنایی با آنها بیشترین درآمد را برای متخصصین این حوزه به همراه دارد، به کتابخانه Ray برخوردم که حقوق متوسط ۱۹۰ هزار دلاری برای آن، وسوسه انگیز به نظر میرسید …
🔀 راهحل Uber: ترکیب Spark و Ray
🚀 Spark همچنان برای پردازشهای حجیم دادهها و خواندن/نوشتن در HDFS استفاده شد.
⚡ Ray برای اجرای سریع توابع Python، مدلهای یادگیری ماشین و محاسبات بهینهسازی به کار گرفته شد.
نتیجه:
✅ ۴۰ برابر افزایش سرعت در اجرای فرآیند بهینهسازی
✅ سادگی توسعه؛ دانشمندان داده میتوانند مستقیماً در Notebookها با Pandas کار کنند
✅ کاهش سربار پردازشی بدون نیاز به تغییرات گسترده در کدهای موجود
💡آموختهها
✅ ابزارها را متناسب با نیاز انتخاب کنید! Apache Spark و Ray هرکدام در زمینهی خود قوی هستند، اما ترکیب آنها میتواند بسیاری از محدودیتها را برطرف کند.
✅ نوسازی کامل همیشه راهحل نیست. Uber بهجای بازنویسی کل سیستم، فقط بخشی از پردازشها را که نیاز به بهینهسازی داشتند با Ray اجرا کرد. این یعنی بهینهسازی هوشمندانه، بدون تحمیل هزینههای سنگین به سازمان.
✅ ریشهی مشکل را درست شناسایی کنید. در این مثال، مشکل اصلی کندی Spark نبود، بلکه ماهیت وظایف کوچک و پرتکرار بود که در Ray بهتر مدیریت شدند.
📌 نتیجه: بهجای تغییر کل فناوری، روی بهینهسازی بخشهایی که نیاز دارند تمرکز کنید. 🚀
🛠 مثال سریع از اجرای یک کد با Ray
در این مثال، تابع square
که محاسبه مربّع اعداد را انجام میدهد، به کمک Ray بهصورت موازی اجرا میشود. این روش باعث افزایش کارایی در وظایف تکراری و کوچک میشود.
برای درک بهتر عملکرد Ray، بیایید یک مثال ساده را اجرا کنیم:
import ray
ray.init() # راهاندازی Ray
@ray.remote
def square(x):
return x * x
futures = [square.remote(i) for i in range(10)]
results = ray.get(futures)
print(results) # خروجی: [۰, ۱, ۴, ۹, ۱۶, ۲۵, ۳۶, ۴۹, ۶۴, ۸۱]
#DataEngineering #MachineLearning #ApacheSpark #Ray #UberTech