خلاصه
ایوان بورمیستروف توضیح میدهد که چگونه ShareChat پلتفرم ویژگی بیدرنگ خود را ساخت که بیش از 1 میلیارد ویژگی را در ثانیه ارائه میدهد و چگونه توانست آن را مقرون به صرفه کند.
متن
بورمیستروف: من با یک داستان کوتاه که برای من اتفاق افتاد شروع می کنم. یک روز کاری عادی بود. من از سر کار آمدم تا برای شام به همسر و دخترم ملحق شوم. من واقعاً هیجان زده هستم زیرا مدلی که تیم ما روی آن کار می کرد بالاخره شروع به نشان دادن نتایج امیدوارکننده کرده است. من واقعاً مشتاق هستم که این را با خانواده ام به اشتراک بگذارم. با این حال، همسرم مشغول است، زیرا سعی می کند به دخترمان که یک غذاخور سریع است، غذا بدهد. او نمی خواهد در آن زمان در مورد مدل ها یا چیز دیگری صحبت کند. سپس، آنقدر هیجان زده که نمی توانم صبر کنم، بنابراین شروع کردم به گفتن به او: "ما این مدل را ساختیم و کار نمی کرد. بیش از یک ماه است که آن را اشکال زدایی می کنیم."
در نهایت، معلوم شد که ما فکر میکردیم فقط اشکالات احمقانه مانند غلط املایی، مانند نام ویژگی سریع، این نوع چیزها است. ما بالاخره آنقدر آنها را شکار کردیم که مدل اکنون همانطور که انتظار داشتیم عمل می کند. من با هیجان به اشتراک گذاشتم و انتظار واکنش یا چیزی را داشتم، اما او مشغول بود. او به سختی گوش داد، اما نمی خواهد بی ادب باشد، بنابراین می فهمد که باید کاری انجام دهد، بنابراین نظرات خود را ارائه می دهد، مانند، "خوب است، این مدل درست مانند شما در مواقعی رفتار می کند". من می گویم: "منظورت چیست، چگونه حتی مرتبط است؟" او گفت: "وقتی از دست من عصبانی هستی، به سادگی به من نمی گویی که مشکل چیست. من باید زمان زیادی را صرف کنم تا بفهمم چه خبر است، درست مانند شما بچه ها با این مدل". او در واقع درست می گوید.
اشکال زدایی مدل ها واقعاً سخت است. اگر مدلی بسازید و عملکرد پایینی داشته باشد، اشکال زدایی می تواند یک کابوس باشد. این می تواند روزها، هفته ها یا ماه ها طول بکشد، مانند مورد ما. حداقل کاری که می توانیم برای کاهش درد انجام دهیم این است که اطمینان حاصل کنیم داده هایی که به مدل می دهیم خوب هستند، بنابراین داده ها حاوی این اشکالات نیستند. این همان چیزی است که پلتفرم های ویژگی قصد انجام آن را دارند. هدف آنها ارائه داده های خوب برای مدل های یادگیری ماشین است. پلتفرم های ویژگی چیزی است که مورد بحث قرار خواهیم داد.
پیشینه و طرح کلی
اسم من ایوان است. من یک مهندس دانشجوی ShareChat هستم. ShareChat یک شرکت هندی است که تعدادی شبکه اجتماعی در هند می سازد، مانند بزرگترین شبکه های اجتماعی داخلی. من در درجه اول بر این داده ها برای ML و به ویژه این پلتفرم ویژگی تمرکز می کنم. قبل از ShareChat، تجربه کار در متا و ScyllaDB را داشتم. یکی از شبکه های اجتماعی که ShareChat می سازد Moj نام دارد. این یک برنامه ویدیویی کوتاه است، تقریباً شبیه TikTok. دارای فید کاملاً شخصی شده، حدود 20 میلیون کاربر فعال روزانه، 100 میلیون کاربر فعال ماهانه است. مدل موجود در داستان در واقع مدل اصلی این برنامه است، بنابراین مدل اصلی که رتبه بندی را مشخص می کند، که مشخص می کند کدام ویدیوها را به کاربران نشان می دهیم. ما در مورد نحوه ساخت پلتفرم ویژگی برای این برنامه یا هر برنامه ای مانند این، به طور خاص صحبت خواهیم کرد. ما پیشینه ای در مورد اینکه یک پلتفرم ویژگی چیست یا ویژگی ها چیست، معماری سطح بالا از هر پلتفرم ویژگی خواهیم داشت. بیشتر زمان صرف چالش ها و نحوه غلبه بر آنها بر اساس مثال های ما خواهد شد. در نهایت، برخی از نکات مهم.
مقدمه ای بر پلتفرم ویژگی
بیایید حافظه خود را در مورد ویژگی برای ML تازه کنیم. یک ویژگی تقریباً هر چیزی است که می توانیم از داده ها استخراج کنیم. به طور معمول، به یک نهاد در سیستم متصل است. به عنوان مثال، می تواند کاربر به عنوان یک نهاد باشد، یا پست، یا خالق، که همچنین یک کاربر است، اما کسی که محتوا را ارسال می کند. انواع مختلفی از ویژگی ها وجود دارد که ممکن است کسی به آنها علاقه مند باشد. به عنوان مثال، شمارنده های پنجره مانند یک ویژگی هستند، تعداد لایک های این پست را در 30 دقیقه گذشته به من بدهید، یا تعداد تعاملات از این کاربر را در یک روز گذشته به من بدهید. این نوع شمارنده های پنجره، زیرا دارای پنجره زمانی هستند. می توانند شمارنده های طول عمر وجود داشته باشند. طول عمر پنجره ای ندارند، مانند تعداد کل پست ها یا لایک های پست داده شده، یا تعداد کل تعاملات از کاربر داده شده. می توانند برخی از ویژگی ها، مانند تاریخ تولد برای یک کاربر، یا زبان پست، یا این نوع چیزها وجود داشته باشد. یا چیزی شبیه N آخر، 100 تعامل آخر با این پست داده شده را به من بدهید، یا 1000 تعامل آخر را برای کاربر داده شده به من بدهید.
در این گفتگو، ما در درجه اول بر روی ویژگی های پنجره تمرکز خواهیم کرد، تا حدودی جالب تر. برای خلاصه کردن اینکه پلتفرم ویژگی چیست، پلتفرم ویژگی مجموعه ای از خدمات یا ابزارها است که امکان تعریف ویژگی ها را فراهم می کند، مانند ارائه برخی از API ویژگی. آنها ویژگی ها را فهرست می کنند و می خوانند که به چه معنا هستند. این امکان را می دهد تا برخی از خطوط لوله ویژگی را برای محاسبه راه اندازی کنند. در نهایت، فروشگاه ویژگی برای خواندن داده ها در صورت نیاز است. یک جنبه مهم که می خواستم بر آن تاکید کنم این است که واقعاً مهم است که یک پلتفرم ویژگی به سرعت مهندسی کمک کند، به این معنی که مانع ایجاد نکند. در عوض، امکان تکرار سریع را فراهم می کند. افرادی که مدل را می سازند، می توانند به راحتی ویژگی ها را تعریف کنند، در صورت نیاز آنها را به روز کنند یا بخوانند که به چه معنا هستند. زیرا وقتی صحبت از نوعی تحقیق می شود، به خصوص مدل کم کارایی، بیایید بگوییم، واقعاً مهم است که مردم بتوانند بروند و به راحتی بخوانند، اینها ویژگی هایی هستند که ما تغذیه می کنیم و این همان چیزی است که آنها معنی می دهند. کاملا مشخص است. در ده ها فایل، به زبان مرموز یا هر چیز دیگری پنهان نشده است.
معماری - نمای کلی سطح بالا از اجزای پلتفرم ویژگی
در معماری معمولی برای هر پلتفرم ویژگی، با برخی از جریان های داده شروع می شود. در یک شبکه اجتماعی، ممکن است جریان لایک ها، جریان بازدیدها، پخش ویدیو، این نوع چیزها باشد. برخی از موتورهای پردازش که این جریان های داده را دریافت می کنند، چیزی را محاسبه می کنند و در نوعی پایگاه داده می نویسند. در نهایت، در مقابل این پایگاه داده، سرویسی وجود دارد که درخواست ویژگی ها را دریافت می کند، آن را به هر پرس و جویی که برای پایگاه داده نیاز دارد تبدیل می کند و احتمالاً برخی از تجمیع های نهایی را انجام می دهد و برمی گرداند. با صحبت از تجمیع های نهایی، به ویژه برای شمارنده های پنجره، نحوه ارائه پرس و جو، مانند تعداد لایک ها را در 30 دقیقه یا یک روز گذشته به من بدهید. به طور معمول، جدول زمانی به سطلهای از پیش جمعآوریشده تقسیم میشود، ما آنها را کاشیهایی با اندازه متفاوت مینامیم.
به عنوان مثال، می توانیم جدول زمانی را به سطل های 1 دقیقه، 30 دقیقه، 1 روز تقسیم کنیم. وقتی درخواست می آید، مثلاً در ساعت 3:37 بعد از ظهر، ما تعداد لایک ها را در دو ساعت گذشته می خواهیم. میتوانیم این پنجره دو ساعته را تقسیم کنیم، آن را با کاشیهای مربوطه بپوشانیم، و سپس از پایگاه داده برای این کاشیها درخواست میکنیم، پاسخ را برگردانیم و در این کاشی جمعآوری کنیم و نتیجه را برگردانیم. این یک تکنیک معمولی برای تامین انرژی این شمارنده های پنجره است.
چالش ها و راه حل ها: داستان 1 (خسته کننده)
این در مورد معماری بود. اکنون بیایید به چالش ها، راه حل هایی برویم که در قالب داستان در مورد آنها صحبت خواهم کرد. اولین داستان یک داستان خسته کننده است. این در مورد انتخاب پلتفرم جریان و پایگاه داده است. برای پخش جریانی، Redpanda را انتخاب کردیم. برای پایگاه داده، ScyllaDB را انتخاب کردیم. ScyllaDB و Redpanda مانند خواهر و برادر هستند. آنها شباهت های زیادی دارند.
اول، آنها با سازگاری API با سیستم های موجود متولد شدند، مانند Scylla با Cassandra API متولد شد و بعداً DynamoDB API را انجام داد. Redpanda با Kafka سازگار است. هر دو وسواس زیادی به عملکرد دارند. آنها این کار را از طریق معماری به اصطلاح shard-per-core انجام می دهند. Shard-per-core زمانی است که داده ها را در برخی از shardها تقسیم می کنیم. هر shard توسط یک هسته معین در سیستم پردازش می شود. این معماری امکان حذف همگام سازی، سربار یا گزارش ها، این نوع چیزها را فراهم می کند. این به آنها کمک می کند تا به اعداد عملکردی که می خواهند برسند. ساخت برنامه ها با استفاده از این تکنیک shard-per-core آسان نیست. هر دو از چارچوب Seastar استفاده می کنند. این یک چارچوب منبع باز است. توسط تیم Scylla ساخته شده است. این امکان را به شما می دهد تا این نوع برنامه ها را بسازید. هر دو دارای مقیاس خودکار نیستند. در واقع دیگر برای Redpanda کاملا درست نیست. آنها به تازگی اعلام کردند که Redpanda serverless را در یکی از ابرهای خود راه اندازی کرده اند. اگر آنها را در پشته خود نصب کنید، از جعبه دارای مقیاس خودکار نیستند.
با وجود این، آنها بسیار مقرون به صرفه هستند. در شرکت ما، ما از آنها نه تنها در این پلتفرم ویژگی استفاده می کنیم، بلکه در واقع بسیاری از حجم های کاری را به ScyllaDB منتقل کردیم: به Scylla از Bigtable منتقل شدیم و به Redpanda از GCP Pub/Sub و Kafka داخلی منتقل شدیم و به صرفه جویی بسیار خوبی در هزینه دست یافتیم. هر دو دارای طلسم های بسیار زیبا هستند. اگر با سیستم ها کار می کنید، باید با برخی از swagها مانند این یکی کار کنید. این در مورد داستان است، زیرا این سیستم ها به طرز غم انگیزی خسته کننده هستند. آنها فقط کار می کنند. به نظر می رسد که وعده سهولت عملیاتی را برآورده می کنند. زیرا وقتی آنها را اجرا می کنید، نیازی به تنظیم ندارید، بنابراین تمایل دارند بهترین استفاده را از سخت افزار داده شده ببرند. دیدگاه من در مورد این موضوع کمی مغرضانه است، زیرا ما از نسخه های مدیریت شده استفاده می کنیم. ما نسخه های غیر مدیریت شده را نیز آزمایش کردیم و این یکی است. ما آن را آزمایش کردیم و مدیریت شده را انتخاب کردیم، فقط به این دلیل که نمی خواستیم تیمی پشت این موضوع داشته باشیم.
داستان 2 (ساده لوح بودن)
بیایید به قسمت کمتر خسته کننده برویم. وقتی نوبت به پخش جریانی و پایگاه داده رسید، گزینه های زیادی وجود داشت. وقتی نوبت به موتور پردازش می رسد، به ویژه در لحظه ای که تصمیم گرفتیم، گزینه های زیادی وجود نداشت. در واقع، تنها یک گزینه وجود داشت، زمانی که پردازش جریان بی درنگ می خواهید. نکته مهم این است که ما پردازش جریان بی درنگ می خواهیم و یک زبان رسا برای پردازش جریان می خواهیم.
Apache Flink چارچوبی است که می تواند یک جریان داده را در زمان واقعی پردازش کند، قابلیت های قدرتمندی دارد. نکته مهم این است که Flink SQL دارد. می توان کار را در زبان SQL تعریف کرد و به دلیل آن ویژگی که می خواهیم ارائه دهیم، بسیار مهم است تا نوشتن ویژگی ها آسان باشد و مهمتر از همه خواندن ویژگی ها آسان باشد. ما Flink را انتخاب کردیم. ما می توانیم با استفاده از این SQL پخش جریانی ویژگی بسازیم. کاری که این پرس و جو انجام می دهد، اساساً بر روی داده های پخش جریانی کار می کند، آنها را انتخاب می کند، GROUP BY توسط برخی از شناسه های نهاد و برخی از شناسه های کاشی، زیرا باید این کاشی ها را جمع آوری کنیم. این پرس و جو این به اصطلاح جدول مجازی را تشکیل می دهد. مجازی، زیرا واقعاً مادی نشده است، با ورود داده ها به روز می شود. به عنوان مثال، برخی از رویدادها می آیند و اکنون ما سهام و لایک ها را به روز کردیم. برخی از رویدادهای جدید می آیند، دوباره آن را به روز می کنیم. این روند ادامه دارد. بسیار خوب، ما این اثبات مفهوم را نسبتاً سریع می سازیم و خوشحالیم. از تجربه توسعه دهنده، Flink واقعاً خوب است. نوشتن با استفاده از گردش کار توسعه دهنده آسان است.
سپس این سوال پیش می آید، بنابراین ما این پرس و جو را می سازیم، کار را راه اندازی می کنیم، در حال اجرا است، این جدول مجازی را به روز می کند، همه چیز خوب است. اکنون می خواهیم پرس و جو را به روز کنیم. Flink این مفهوم را دارد که savepoint، checkpoints نامیده می شود. اساساً، به دلیل پردازش stateful، دارای مقداری حالت است. کاری که savepoint انجام می دهد این است که می توانیم کار را متوقف کنیم، یک عکس فوری از حالت بگیریم، سپس می توانیم کار را به روز کنیم و دوباره شروع کنیم و از این عکس فوری شروع کنیم. بدون عقب ماندگی زیاد یا چیزی شبیه به این، به کار خود ادامه می دهد. این انتظار بود. خوب، ما Flink داریم، بنابراین Flink دارای savepoint است. اکنون ما Flink SQL داریم، بنابراین می توانیم این SQL را به روز کنیم و از savepoint بازیابی کنیم. متاسفانه، اینطور کار نمی کند.
در واقع، ارتقاء کار Flink SQL غیرممکن است. برای ما، در آن لحظه، مهندسان Flink تا حدودی بی تجربه، این مانند یک ضربه بزرگ بود. زیرا مانند، بیایید بچه ها، شوخی می کنید؟ قرار است چه کار کنیم؟ ما کار را راه اندازی کردیم و باید انتظار داشته باشیم که هرگز شکست نخورد؟ مثل این است که تا ابد کار می کند یا چه؟ وقتی اولین شوک از بین رفت و کمی بیشتر به این موضوع فکر کردیم، تعجب آور نیست. زیرا این پرس و جو در واقع به مجموعه ای از اپراتورهای stateful ترجمه می شود. وقتی حتی یک تغییر جزئی در پرس و جو انجام می دهیم، این اپراتورهای stateful ممکن است کاملاً متفاوت باشند. البته، نگاشت یک مجموعه از اپراتورهای stateful به دیگری یک کار فوق العاده دشوار است. هنوز پیاده سازی نشده است. این تقصیر Flink نیست که پیاده سازی نشده است. دانستن این موضوع زندگی ما را آسان تر نمی کند، زیرا برای ما، می خواهیم پلتفرمی را ارائه دهیم که کاربران بخواهند بروند، SQL را به روز کنند و فقط کار را دوباره راه اندازی کنند. باید فقط انتخاب شود و به کار خود ادامه دهد. توصیه معمولی این است که همیشه backfill کنید.
اگر می خواهید Flink SQL را به روز کنید، ما کار را به گونه ای ترکیب می کنیم که ابتدا در حالت دسته ای اجرا شود، از قبل داده ها را پردازش کند و سپس به داده های جدید ادامه دهد. این واقعاً ناخوشایند است و همچنین پرهزینه است. اگر بخواهیم این کار را هر بار که این ویژگی ها را به روز می کنیم انجام دهیم، هزینه زیادی برای ما خواهد داشت. این فقط فوق العاده ناخوشایند است، زیرا backfill نیز زمان می برد. ما می خواهیم تجربه ای داشته باشیم تا کار را به روز کنیم و فقط آنها را دوباره راه اندازی کنیم و به کار خود ادامه دهد.
یکی از مواردی که Flink در آن می درخشد، به اصطلاح DataStream API است. DataStream، در مقایسه با SQL API، DataStream جایی است که ما کار را در قالب برخی از زبان های JVM مانند Java، Kotlin یا Scala بیان می کنیم. یک تعامل واقعا خوب بین SQL و DataStream وجود دارد. به طور خاص، Changelog نامیده می شود. وقتی می خواهیم از SQL به DataStream برویم، چیزی به نام Changelog وجود دارد. اساساً، SQL ردیف های به اصطلاح Changelog را ارسال می کند. چیست؟ این یک ردیف از داده ها با این نشانگر، مانند این لیست، +I است. +I به این معنی است که این یک ردیف جدید در جدول مجازی ما است. ممکن است -U، +U وجود داشته باشد. آنها به صورت جفت می آیند. -U به این معنی است که این ردیف قبل از به روز رسانی بود و +U به این معنی است که این ردیف بعد از به روز رسانی است. هنگامی که SQL در حال اجرا است، به صدور این ورودی های Changelog ادامه می دهد. نکته جالب در مورد این Changelog این است که اگر به این نگاه کنید، می توانید متوجه شوید که مقادیر نهایی ستون های ما، ویژگی های ما، تجمع بر روی این Changelog است.
اگر در نظر بگیرید + عملیات مثبت است و - عملیات منفی است. اساساً، در اینجا سه ردیف می بینیم. اگر بخواهیم سهام ها را بشماریم، این کار را می کنیم، 3 - 3 + 5، نتیجه نهایی 5 است. همین روند ادامه دارد. ما می توانیم به رفتار با این ورودی های Changelog مانند دستورات، یا + دستور یا - دستور ادامه دهیم. اگر بخواهیم این را در قالب این DataStream بیان کنیم، تابعی مانند این وجود خواهد داشت. فوق العاده ساده. ما به روز رسانی ردیف داریم. ما حالت فعلی داریم. ما حالت را دریافت می کنیم. می بینیم که آیا به روز رسانی مثبت است یا به روز رسانی منفی و این به روز رسانی را انجام می دهیم. فوق العاده ساده. تاکنون، فوق العاده واضح است. نکته جالب در مورد این مدل این است که از ارتقاء کار جان سالم به در می برد. زیرا وقتی کار را ارتقا می دهیم، حالت SQL را از دست می دهیم، خوب. این SQL به صدور این ورودی های Changelog ادامه خواهد داد. ما این مجموعه +I، -U، +U را داریم.
سپس ارتقاء کار اتفاق می افتد و اکنون این ردیف از دیدگاه موتور SQL، یک ردیف جدید است. شروع به ارسال این ورودی های +I می کند. خوب است. این +I دقیقاً نشان دهنده تغییر از داده های قبلی است. از منطقی که تجمع را بر روی Changelog انجام می دهد، هیچ اتفاقی نمی افتد. فقط به رفتار با این ورودی ها به عنوان دستورات، مانند + دستور، - دستور ادامه می دهد. می بینیم، ما این تجمع را داشتیم. اکنون ارتقاء کار اتفاق افتاده است. ما اهمیتی نمی دهیم. ما به جمع آوری این ورودی های Changelog ادامه می دهیم. ما می توانیم کار را به این روش ترکیب کنیم. یک قسمت SQL وجود دارد و یک قسمت تجمع Changelog وجود دارد. قسمت SQL، ما حالت را در آنجا کنترل نمی کنیم، به دلیل این جادو و پیچیدگی های این SQL و غیره. در این قسمت تجمع Changelog، در DataStream بیان شده است. اینجاست که ما حالت را کنترل می کنیم. ما می توانیم حالت را به گونه ای بسازیم که از ارتقاء کار جان سالم به در ببرد. این قسمت از کار می تواند از savepoint بازیابی شود و به کار خود ادامه دهد. جریان به این صورت خواهد بود که پرس و جو را به روز کردیم و فقط کار را از savepoint دوباره راه اندازی کردیم. محاسبات ادامه خواهد داشت.
داستان 3 (خسته بودن)
اکنون ما سیستمی داریم که می توانیم بنویسیم و همچنین به روز کنیم. بسیار هیجان انگیز است. مشکل بعدی که ممکن است با آن مواجه شویم این است که وقتی کار را راه اندازی می کنیم، عملکرد با گذشت زمان ممکن است کاهش یابد. ما این را خسته شدن کار می نامیم. واضح است که چرا. زیرا این یک کار stateful است و ما حالت داریم. هر چه حالت بزرگتر باشد، عملکرد کار کمتر است. این یک وابستگی کاملاً واضح است. توصیه معمولی این است که حالتی داشته باشید که فقط TTL اعمال شده باشد. حالت منقضی خواهد شد. اندازه ثابتی خواهد داشت. کار خسته نخواهد شد. این یک توصیه پرونده است، اما واقعاً مشخص نیست که چه نوع TTL را انتخاب کنیم. به عنوان مثال، در مورد ما، بزرگترین کاشی که در این سیستم استفاده می کنیم یک کاشی پنج روزه است. اگر میخواهید شمارندهها دقیق باشند، TTL باید حداقل پنج روز باشد. این حتی به طور کامل مشکل شمارنده های طول عمر را حل نمی کند، جایی که پنجره نداریم. واقعاً مشخص نیست که چه نوع TTL را انتخاب کنیم. با فرض اینکه آنها با نادرست بودن شمارنده های طول عمر مشکلی ندارند و با فرض اینکه ما TTL پنج روزه را در زمینه شمارنده های طول عمر پیدا می کنیم، مشکل این است که پنج روز خیلی زیاد است.
بر اساس تجربه ما، کاری که صدها هزار رویداد را پردازش می کند و میلیون ها عملیات در ثانیه را با حالت انجام می دهد، نشانه هایی از خسته شدن را به سرعت نشان می دهد، فقط چند ساعت پس از راه اندازی. پنج روز خیلی زیاد است. البته، می توانیم کار را مقیاس بندی کنیم، اما هزینه دارد. در مورد آن چه کاری می توانیم انجام دهیم؟ به یاد داشته باشید که اکنون دو نوع حالت داریم. یکی حالت SQL است و دیگری حالت تجمع Changelog است. خبر خوب در مورد حالت SQL این است که نباید کاری انجام دهیم، زیرا ما به دلیل این حالت Changelog از قبل از ارتقاء کار جان سالم به در بردیم.
اکنون از دیدگاه SQL، مهم نیست که به دلیل ارتقاء کار حالت را از دست داده است یا به دلیل TTL حالت را از دست داده است. مهم نیست. ما فقط TTL را روی SQL تنظیم می کنیم و به رفتار با این دستورات Changelog به عنوان دستورات ادامه می دهیم. نباید کاری انجام دهیم. برای حالت تجمع Changelog، می توانیم تابع خود را کمی تغییر دهیم. وقتی به حالت دسترسی پیدا می کنیم و منقضی شده است، می توانیم فقط از Scylla خود پرس و جو کنیم، زیرا داده ها در Scylla وجود دارند. ما تغییر می دهیم، مانند این که به روز رسانی سبز تابع خود را داشته باشیم. اکنون می توانیم TTL را برای تجمع Changelog نیز تنظیم کنیم. به کار خود ادامه خواهد داد و خود را از پایگاه داده اصلی بازیابی می کند. اکنون کارها دیگر خسته نمی شوند، بنابراین عملکرد ثابتی دارد زیرا حالت دارای اندازه ثابتی است.
داستان 4 (همزیستی خوشبختانه)
خوب. اکنون میتوانیم کار را راه اندازی کنیم، کار را به روز کنیم و نمیمیرد. با این حال، مشکل اکنون با تغییر قبلی این است که اکنون کارها نه تنها در پایگاه داده می نویسند، بلکه از آنها نیز می خوانند. این در واقع یک معامله بزرگ است. یک معامله بزرگ برای کار برای خواندن از پایگاه داده، زیرا برای پایگاه داده ای مانند Scylla یا Cassandra یا نوع مشابهی، خواندن تا حدودی گران تر از نوشتن است، به ویژه خواندن سرد. اگر چیزی را بخوانیم که سرد است، که حاوی حافظه پنهان پایگاه داده نیست، اتفاقات زیادی می افتد. زیرا برای دریافت داده ها باید چندین فایل را روی دیسک اسکن کنیم، آنها را ادغام کنیم، حافظه پنهان را به روز کنیم، اتفاقات زیادی می افتد. نکته جالب در مورد کارها این است که آنها بیشتر از سرویسی که ویژگی ها را ارائه می دهد، به خواندن سرد ضربه می زنند. اتفاقی که می افتد این است که وقتی کاری را در سایت کار انجام می دهیم، به عنوان مثال، مجموعه ای از کارهای آزمایشی را راه اندازی می کنیم. ما می خواهیم کارهای آزمایشی را راه اندازی کنیم زیرا می خواهیم این سرعت مهندسی را باز کنیم و می خواهیم آزمایش کنیم و غیره. یا شاید به دلایلی برخی از کارها عقب ماندگی داشته باشند و نیاز به backfill عظیمی داشته باشند یا هر چیز دیگری.
ما کار را راه اندازی می کنیم و آنها شروع به ضربه زدن به این خواندن های سرد می کنند، به خصوص اگر در backfill باشد و سعی می کنند عقب ماندگی را پردازش کنند: آنها به بسیاری از این خواندن های سرد ضربه می زنند. این پایگاه داده اصلی را به هم می زند و بر تاخیر سرویس تأثیر می گذارد که به ویژگی ها دسترسی دارد. در مورد آن چه کاری انجام می دهیم؟ اولین فکری که ممکن است به ذهن برسد این است که ما به مقداری دریچه گاز نیاز داریم. با این حال، مشکل این است که واقعاً مشخص نیست سطحی که باید دریچه گاز را در آن اعمال کنیم. ما نمی توانیم دریچه گاز را در سطح هر کارگر در کار Flink اعمال کنیم، زیرا حداقل چندین کار داریم و یک کار جدید می تواند بیاید و برود.
در عوض، میتوانیم نوعی هماهنگکننده بین کارها و Scylla داشته باشیم که اساساً یک پروکسی است. با این حال، این یک چیز فریبنده است، زیرا این پروکسی، Scylla و کلاینت های Scylla واقعاً فوق العاده بهینه شده اند. اگر همان کارایی را برای خواندن می خواهید، این پروکسی باید حداقل بهینه شده باشد و همچنین Scylla خودش نیز دشوار است. به طور کلی، این راه حل پیچیده است. در واقع هزینه اضافی دارد، زیرا به این جزء نیاز داریم که باید به طور مناسب مقیاس شود. به احتمال زیاد، کارآمد نیست، زیرا واقعاً آسان نیست که این پروکسی را به همان روشی بنویسید که به همان اندازه Scylla کارآمد باشد.
گزینه دوم مراکز داده نامیده می شود. Scylla، مانند Cassandra، دارای انتزاع مرکز داده است. این یک انتزاع کاملاً منطقی است. نیازی نیست که یک مرکز داده واقعی، واقعی فیزیکی باشد. اساساً، می توانیم خوشه خود را به دو مرکز داده منطقی تقسیم کنیم. کارها به مرکز داده برای کار ضربه می زنند و فروشگاه ویژگی به مرکز داده برای فروشگاه ویژگی ضربه می زند. بسیار خوب است، زیرا عایق بسیار خوبی دارد. ما همچنین می توانیم این مراکز داده مختلف را به طور مستقل مقیاس بندی کنیم. نکته منفی این است که مدیریت خوشه برای Scylla بسیار پیچیده تر می شود. همچنین هزینه دارد، زیرا حتی اگر بتوانیم این مراکز داده را به طور مستقل مقیاس بندی کنیم، باز هم به معنای ظرفیت اضافی است. همچنین، پیچیدگی مدیریت خوشه نباید دست کم گرفته شود، به خصوص اگر مراکز داده واقعی می خواهید. اکنون، به عنوان مثال، ما می خواستیم پایگاه داده ما در دو مرکز داده باشد و اکنون در چهار مرکز داده است. پیچیدگی در واقع بسیار زیاد افزایش می یابد.
گزینه سوم، گزینه ای که در نهایت با آن به پایان رساندیم، به اصطلاح اولویت بندی حجم کار است. این ویژگی در Scylla وجود دارد که اولویت بندی حجم کار نامیده می شود. این است که ما می توانیم چندین سطح سرویس را در داخل Scylla تعریف کنیم و حجم های کاری مختلف را به سطوح سرویس مختلف پیوست کنیم. چگونه کار می کند؟ هر دسترسی به هر منبعی در Scylla دارای صف عملیات است. به عنوان مثال، ما صف کار و صف سرویس داریم: کار 200 سهم دارد و سرویس 1000 سهم دارد. یعنی چی؟ این بدان معناست که برای هر واحد کار برای پرس و جوهای کار، Scylla حداکثر پنج واحد کار برای پرس و جوهای سرویس انجام می دهد. برنامه ریزی وجود دارد که از این صف ها انتخاب می کند و صف نهایی را تشکیل می دهد. یعنی چی؟ این بدان معناست که در نهایت تاخیر ثابتی خواهیم داشت. البته، تاخیر کار بیشتر از تاخیر سرویس خواهد بود. این خوب است زیرا کار یک چیز پس زمینه است و زیاد به تاخیر اهمیتی نمی دهد. به توان عملیاتی اهمیت می دهد.
از طرف دیگر، سرویس به تاخیر اهمیت می دهد. خلاصه کردن این راه حل نهایی، چگونه به نظر می رسد، اساساً ما با خوشه، با خود Scylla کاری انجام نمی دهیم. ما فقط این سطوح سرویس مختلف را تنظیم می کنیم. از کار، ما با استفاده از کاربر برای حجم کاری کار به خوشه دسترسی پیدا می کنیم. از سرویس ویژگی، ما با استفاده از کاربر برای حجم کاری سرویس به خوشه دسترسی پیدا می کنیم. این یک راه حل نسبتاً ساده است. خوب است این را بپذیرید، و این راه حلی است که برای ما خوب کار می کند.
یک داستان کوتاه
با این حال، قبل از اینکه خلاصه ای داشته باشیم، یک داستان کوتاه به عنوان نوعی حواس پرتی می گویم. چند ماه پس از راه اندازی این پلتفرم. یک تیم کاملاً متفاوت به من مراجعه کرد و گفت که تیم آنها یک مدل جدید دارد. آنها می خواستند آن را در خط لوله تبلیغات قرار دهند. در آن لحظه در این خط لوله، آنها از یک سیستم کاملاً متفاوت از ویژگی های استاتیک از نوع hive استفاده می کردند و باید یک ویژگی زمان واقعی نیز وجود داشته باشد. ویژگی زمان واقعی آنها در واقع روی Memcached بود. هر دوی این سیستم ها فوق العاده بهینه شده اند، اما یک نقص دارند. آنها نمی توانند چیزی بیش از چند صد هزار سهم در ثانیه مدیریت کنند. این تیم از من می پرسید، آیا این ویژگی جدیدی است که می خواهیم به خط لوله تبلیغات اضافه کنیم، آیا چیزی بیش از یک میلیون در ثانیه می خواهیم. مطمئن نبودیم و گفتم: "من نمی دانم." گفتم: "چرا از پلتفرم ویژگی ما استفاده نمی کنید؟" ما این مسئله اولویت بندی حجم کار را داریم. اگر در مقیاس مناسب برای تبلیغات به آن دسترسی پیدا کنیم، این کار می کند. فقط آن را امتحان کنید." آنها گفتند: "خیلی خوب است، با این حال، ما اصلاً Flink نمی دانیم، حتی SQL نمی دانیم و این کار را تا فردا در آزمایش A/B می خواهیم." چه کاری باید انجام دهیم؟ خوب، با استفاده از پلتفرم ویژگی ما، در نهایت کار را با کمتر از 3 ساعت با ایجاد یک منبع سفارشی جدید انجام دادیم. 3 ساعت از هیچ چیز. ما در یک سیستم کاملاً متفاوت، نوع دیگری از کاربردی بودن را می بینیم. چیزی که متوجه شدیم این است که این سیستم و این پلتفرم نه تنها در رابطه با کار اصلی مفید است، بلکه در واقع می تواند برای استفاده های مختلف مورد استفاده قرار گیرد.
نکات کلیدی
مواردی که من در کل می خواستم ارائه دهم، سه نکته اصلی است. به دنبال سیستم هایی باشید که سهولت عملیاتی ارائه می دهند. عملکرد واقعا مهم است، به خصوص مقیاس ما، اما فقط وقتی این سهولت عملیاتی وجود دارد. در غیر این صورت، صرفاً نمی تواند به اندازه کافی سریع تکرار کند. نکته دوم این است که همیشه ساده لوح بودن نمی پردازد. وقتی در مورد فریم ورک های بزرگ و شناخته شده مانند Flink صحبت می کنید، انتظار این است که همه چیز کار می کند. از خود سؤال کنید که از چه چیزی می خواهید بیشتر استفاده کنید: API که به درستی کار نمی کند یا DataStream API که بسیار قدرتمند است، شما می توانید مهندسی خود را با چسباندن این موارد به هم کنترل کنید. نکته آخر این است که از خود سؤال کنید آیا با سیستم همزیستی خوشبختانه ای دارید یا خیر. ممکن است یک معامله بزرگ به نظر نرسد تا زمانی که متوجه شوید که می خواهید ویژگی ها را در خط لوله قرار دهید. آنجاست که در واقع متوجه می شوید که از استفاده های مختلف محافظت می کنید، برای مثال، پایگاه داده اصلی که از سرویس های مختلف یا کاربران مختلف در سیستم جدا شده است. اینها مواردی هستند که من سعی می کنم روی آنها تأکید کنم.
ممنون از گوش دادن به من.