تیم داده دیوار، از اوایل تابستان ۹۵ با افرادی با تخصصها و مهارتهای مختلف تشکیل شد و فعالیت خودش را آغاز کرد. در این یادداشت، نگاهی میاندازیم به تجربیاتی که در این مدت کسب کردیم- چه از نظر تکنولوژی و چه ساختاری.
در محصول دیوار، به صورت جسته و گریخته، کارهای مختلفی روی دادههای موجود انجام شده بود. آمارهای مختلفی اندازه گیری میشد و چند داشبورد اولیه برای نمایش وضعیت فعلی دیوار وجود داشت. نمودارهایی مثل تعداد آگهی و تعداد بازدید از آگهیها معیارهای اصلی ما برای نظارت بر روند پیشرفت محصول بود. با این حال میدانستیم که نیاز به یکپارچهسازی این آمارها و اندازهگیری دقیقتر آنها وجود دارد. همچنین دادههای دیوار، پتانسیل بسیار زیادی برای بهبود کیفیت محصول داشتند. مثلاً بهبود کیفیت جستجوی آگهی یا سرعت و دقت بررسی آگهیهای ثبت شده. از این رو، کارهایمان به دو قسمت اصلی تصمیمگیری داده محور (data-driven decision making) و محصولات دادهای (data products) تقسیم شد. در تصمیمگیری داده محور هدف اصلی کسب دانش بیشتر در مورد محصول و کاربران آن برای تعیین ادامه مسیر محصول بود. در محصولات دادهای نیز هدفمان ارائهی ابزارها و مدلهایی بود که بتوان به کمک آنها کیفیت محصول را افزایش داد که در ادامهی یادداشت بیشتر توضیح داده میشود.
با این که از قبل تجربیاتی در محصول کافهبازار در این زمینه داشتیم، این کار همچنان برای ما جدید بود. بنابراین تا مدتی به بررسی تجارب شرکتهای مشابه پرداختیم. علیرغم اینکه محتوای زیادی دربارهی علوم داده و مهارتهای حول آن وجود دارد، اما پیدا کردن محتوایی با کیفیت مطلوب که به ساختن و پیش بردن یک تیم داده پرداخته باشد نیاز به جستجو و بررسی بیشتری داشت. مقالات و ویدیوهای سایت O'Reilly مانند این ویدئو، در این زمینه بسیار مفید بودند.
در ابتدای کار نمیخواستیم زمان زیادی را صرف زیرساخت داده کنیم، زیرا نمیدانستیم دادهها چه شمایی (schema) دارند، پردازشهایی که میخواستیم روی آن انجام دهیم چقدر منابع نیاز دارند و به چه ابزارهای دیگری نیازمندیم. معمولاً تصمیم گیری بدون اطلاعات کافی نتیجهی خوبی در بر ندارد و علاوه بر هزینه، در ادامهی راه، محدودیتهای پیشبینی نشدهای به وجود میآورد. بنابراین سعی کردیم برای کسب این دانش اولیه در کوتاهترین زمان، ابزارهای پایهای مورد نیازمان را راه اندازی کنیم. هدف از این تصمیم رسیدن به سرعت لازم برای ارزشرسانی به محصول بود و میدانستیم از طرفی دیگر بدهیهای فنی رشد خواهند کرد که باید آنها را کنترل میکردیم. منظور از بدهی فنی، مشکلاتی است که میدانیم وجود دارند، برطرف کردن آنها مستقیماً ارزشی به محصول نمیرسانند و تجمع آنها به مرور زمان، باعث اختلال و کند شدن توسعه میشود. به عنوان مثال، مزیت اصلی اسپارک (Spark) به عنوان یک ابزار پردازش داده، ایجاد یک کلاستر و استفاده از چندین سرور معمولی برای تقسیم کردن پردازشهای سنگین است. اما به جای استفاده از چندین سرور و راه اندازی یک کلاستر، مدتی نسبتاً طولانی از یک سرور قدرتمند که توان پردازشی آن معادل چند سرور عادی بود استفاده کردیم تا نیازی به تنظیمات کلاسترینگ و راهاندازی یک فایل سیستم توزیع شده نداشته باشیم. در واقع به جای تغییر مقیاس افقی (scale out)، از تغییر مقیاس عمودی (scale up) استفاده کردیم. این کار باعث شد تا بتوانیم در چند ماه اول، تمرکز اصلیمان را به جای مقیاسپذیری، روی تمیزسازی و تحلیل داده بگذاریم.
ساختار تیمهای فنی در شرکت الهام گرفته از ساختار Spotify در آن زمان بود که طبق آن، تیمها بر اساس محصول شکسته میشوند؛ مثلاً تیم جستجوی آگهی، تیم ثبت آگهی و … . در هر تیم افرادی با مهارتهای مختلف وجود دارند تا بتوانند نیازمندیهای محصول را برآورده کنند. در اکثر تیمها برنامهنویس موبایل، توسعه دهندهی سمت کاربر و توسعه دهندهی سمت سرور وجود دارند. پس از گذشت زمان و بزرگتر شدن تیم داده، تصمیم گرفتیم تا مطابق این ساختار رفتار کنیم و در تیمهای مختلف دیوار قرار گرفتیم. بنابراین دیگر، تیم داده وجود نداشت ولی با جلسات منظم هفتگی توانستیم اشتراک دانش و تجربیاتمان را در «چپتر داده» حفظ کنیم. در این جلسات، افراد مسائل داخل تیمشان را با یکدیگر به اشتراک میگذارند تا بقیه نیز در مورد آن همفکری کنند و همچنین مسائل مشترک بین تیمی، مانند زیرساخت و ابزارهای مشترک داده در آن مطرح میشود. با این کار توانستیم نیازهای هر تیم را بهتر درک کنیم و ارتباطات مفیدتری با هر تیم داشته باشیم. یکی از مهمترین نکات در این فرایند شروع به عنوان یک «تیم» و سپس تبدیل شدن به یک «چپتر» بود. در چند ماه اول، اولویتهای اصلی راهاندازی ابزارها، فهم کامل دادههای موجود در دیوار و یکسانسازی فرایندهای جمعآوری و تحلیل داده بود که نیاز به ارتباطات بیشتری بین دانشمندان و مهندسان داده داشت. اما پس از انجام مراحل ابتدایی، تبدیل شدن به یک چپتر در ادامهی مسیر باعث شد تا بتوانیم در دیوار موثرتر باشیم. زیرا افراد روی مسائل یک تیم به مدت چندین ماه تمرکز داشتند و در عین حال بستری برای هماهنگی کارهای مشترک بین تیمی وجود داشت.تفاوت بین تیم و چپتر: اعضای چپتر داده در تیمهای مختلف مشغول به کار هستند.
تا نزدیکی اسفند ۹۵ اکثر تحلیلها بر اساس اطلاعات آگهیهای ثبت شده یا به عبارتی کاربران فروشنده در دیوار انجام میشد؛ مثلاً چه آگهیهایی بیشتر نمایش داده میشوند و چه کاربرانی بیشتر آگهی ثبت میکنند. این اطلاعات از پایگاههای داده اصلی دیوار استخراج میشد و در سرورهای داده ذخیره میشد. با این حال کاربران خریدار نیز از اهمیت بسزایی برخوردار بودند و نیاز داشتیم تا اطلاعات بیشتری از نحوهی کار آنها با دیوار داشته باشیم. برای مثال این که کاربران بیشتر چه چیزهایی را جستجو میکنند، روی چه آگهیهایی بیشتر کلیک میکنند و با چه آگهیهایی بیشتر تماس میگیرند. این اطلاعات به ما کمک میکند تا آگهیهای مرتبطتر و با کیفیت بیشتری را به کاربران نشان دهیم و تجربهی کاربری بهتری برایشان ایجاد کنیم.
برای همین، زیرساختی فراهم کردیم تا چنین اطلاعاتی از سمت کاربر برای سرورهای ما ارسال شود. حال مسئلهی اصلی نحوهی ذخیرهسازی و پردازش این اطلاعات بود. در ابتدا کسندرا (Cassandra) را به عنوان راه حل ذخیرهسازی در نظر گرفته بودیم؛ اما با کمی تحقیق و بررسی فهمیدیم که نحوهی دسترسی به اطلاعات ذخیره شده در کسندرا با حالتی که ما برای استفاده در نظر داشتیم تفاوت دارد. به عبارت دیگر نمیتوانستیم Data Modeling ای ارائه دهیم که هم برای ذخیرهسازی در کسندرا بهینه باشد و هم برای خواندن از آن به صورت انبوه. جزییات این مسئله پیچیده است و در این یادداشت قابل بحث نیست اما شاید در آینده در مورد این موضوع به صورت تخصصیتر بنویسیم. این مقاله به خوبی بعضی از مشکلات تکنولوژیهایی مانند کسندرا را که ما نیز به آن برخورد کردیم مطرح میکند.
از این رو به یک راهحل سطح پایینتر اما بالغتر روی آوردیم: با این که HDFS تنها یک فایل سیستم توزیع شده است، اما میتواند به خوبی نیازهای ذخیرهسازی و پردازشی ما را پوشش بدهد. داده در ابتدا به صورت متنی و کاملاً خام در HDFS ذخیره میشود و پس از کامل شدن دادههای یک روز، به صورت ساختاریافته و به فرمت پارکت (Parquet) تبدیل میشود. بر خلاف اکثر پایگاههای داده سنتی که دادههای هر ردیف از جدول را کنار هم ذخیره میکنند، پارکت یک فرمت ستونی است و برای دسترسی سریع به صورت ستونی بهینه شده است. بنابراین برای پردازشهای مختلف میتوانیم تنها ستونهای مورد نیاز را از دیسک بخوانیم و نیاز به خواندن تمام دادههای ذخیره شده نیست.
در حال حاضر، هر روز حدود ۴۰۰ گیگابایت در زیرساخت HDFS ما ذخیره میشود و در عین حال یک پرسمان روی دادههای یک روز تنها در عرض چند دقیقه انجام میشود. ساختارمند کردن تمام دادههای موجود که امکان اجرای اکثر پرسمانهای SQL را روی آن ممکن میکند باعث شد تا فرایندهای تحلیل داده سرعت بیشتری داشته باشند و دغدغههای کمتری حول بهینهسازی پرسمان داشته باشیم.
با بالغتر شدن چپتر داده در دیوار، نیازهای بنیادین تا حدی برطرف شد و توانستیم بیشتر به سمت محصولات دادهای حرکت کنیم. منظور از محصولات دادهای ارائه کردن یک سرویس یا کتابخانه است که بتوان از آن در محیط Production استفاده کرد؛ مثلاً سرویسی که با استفاده از شبکه عصبی، دستهبندی یک آگهی را به صورت خودکار تشخیص بدهد تا فرایند ثبت آگهی تسریع شود. منظور از محیط Production تمامی کدها و پایگاههای دادهای است که مستقیم برای سرویسدهی به کاربران استفاده میشوند. از همان ابتدا میدانستیم که راهاندازی چنین سرویسهایی را نمیتوانیم بر عهده دانشمندان داده بگذاریم، زیرا تخصص این افراد متفاوت است و لزوماً دانش زیادی در مورد چالشهای فنی چنین سرویسهایی مثل مانیتورینگ، لود بالانسینگ و … ندارند. این مقاله از گوگل به خوبی چالشهایی را که در راهاندازی چنین سرویسهایی وجود دارد، شرح میدهد. بنابراین نیاز به یک نقش جدید برای انجام چنین کارهایی را حس کردیم و نقش مهندس یادگیری ماشین (ML Engineer) را در دیوار تعریف کردیم. بنابراین سه نقش متفاوت در چپتر داده به وجود آمد که هر کدام وظایف خود را داشتند:
این مقاله تا حدی به توصیف ما از این نقشها نزدیک است با این تفاوت که در تعریف ما یک مهندس یادگیری ماشین، فردی است که بین دانشمندان داده، مهندسین داده و توسعه دهندههای سمت سرور قرار میگیرد. در حال حاضر مرزهای شفافی بین این نقشها نیست و هر فرد بخشی از مهارتهای هر کدام از این نقشها را دارد. این دیدگاه که هر کدام از این نقشها به نقش دیگر سرویس میدهد یا از آن سرویس میگیرد، همواره خطرناک بوده و هیچ گاه دوست نداریم که چنین مرزبندیهای قاطعی بین نقش افراد وجود داشته باشد. وجود چنین مرزهایی مشابه مرزهای تیم Operations و Development در ساختارهای سنتی بود که باعث غیربهینه بودن فرایند توسعه میشد و برای حل آن DevOps به وجود آمد. ما هم در چپتر داده تلاش کردیم تا فرهنگ مشابهی را به وجود بیاوریم.
در ابتدای کار نیاز داشتیم تا محیطی برای پردازش دادههای دیوار فراهم کنیم بدون این که بار اضافهای روی پایگاههای داده و سرورهای اصلی بیاوریم و تغییری در کدهای موجود ایجاد کنیم. پایگاههای داده اصلی برای بازیابی پستها و نمایش آنها به صورت تک تک (OLTP) بهینه بودند اما در اکثر پردازشهای ما نیاز بود تا تمام پایگاه داده را از دیسک بخوانیم (OLAP) و این کار باعث کند شدن درخواستهای اصلی به پایگاه داده دیوار میشد.
بنابراین نیاز به ایجاد یک زیرساخت کاملاً مستقل، برای وابسته نبودن به زیرساخت سمت سرور و جلوگیری از ایجاد اختلال در کیفیت سرویسهایمان احساس میشد. در ادامه ابزارها و تکنولوژیهایی که هم اکنون در زیرساخت داده از آنها استفاده میشوند به صورت مختصر معرفی شدهاند.
در دیوار دو پایگاه داده اصلی Live و Archive وجود دارد که در آنها به ترتیب پستهای فعال و پستهای قدیمی و آرشیو شده وجود دارند. همانطور که در شکل مشخص شده است، دادهها به کمک Airflow و replication از این پایگاههای داده جمع آوری شده، در HDFS ذخیره میشوند. لاگهای مختلف نیز از وب سرورها به سرویس Fluentd ارسال شده تا در HDFS ذخیره شوند. سپس پردازشهای گوناگونی روی این دادهها به کمک Airflow و اسپارک انجام میشود که نتایج آنها داخل پایگاه دادهی Metrics ذخیره میشود. از طریق Metabase میتوان به این پایگاه داده و نسخهی replicate شده از پایگاه دادهی Live دسترسی داشت تا این اطلاعات تصویرسازی شوند. از طریق Zeppelin نیز میتوان مستقیماً به دادههای HDFS و پایگاههای داده دسترسی پیدا کرد و تحلیلهای پیچیدهتری روی آنها انجام داد. در نهایت از طریق Zeppelin و Metabase گزارشها و تصویرسازیهای مختلفی انجام میشود تا در دسترس افراد مربوط قرار میگیرند.
خوشبختانه پس از دو سال از انتخاب این ابزارها، هنوز از تمامی آنها استفاده میشود و این ابزارها نیز بهبودهای قابل توجهی داشتهاند.
در طی این چند سال تجربیات بسیار زیادی به ما اضافه شد و تصمیم های درست و غلط فراوانی گرفتیم. شاید مهمترین نکاتی که در طی این فرایند به ما کمک کرد اینها بود: