مدتی پیش به پیشنهاد یکی از شرکت ها، قرار شد راه حلی برای افزایش سرعت گزارشات داشبورد ارائه کنم. در این نوشته سعی می کنم موارد مهمی که یادگرفتم رو با شما به اشتراک بذارم.
خب راستش قبل از این تجربه، بیشترین حجم داده ای تا حالا دیده بودم زیر یک میلیون بود. در برخی موارد دیده بودم کندی احساس می شه اما معمولا از اونجایی که ریسپاسنس گزارش ها اگر زیر 30 ثانیه باشند، مشکلی ندارند و جواب گو بسیاری از مصارف کسب و کار ها هستند. بنابراین منی که تا به حال با دیتابیس هایی با حجم های زیر یک میلیون کار کرده بودم حالا باید با حجم های زیر یک میلیارد کار می کردم!
از اونجایی که کار های این تیپی خروجیشون صفر و یکی هست (یعنی نمی تونی کار رو مثلا با 80 درصد نتیجه نهایی تحویل بدی! یا میشه یا نمی شه!) و همچنین اولین تجربه من بود، برای همین فاز تحقیقاتی رو شروع کردم.
از اونجایی که دیتابیس های OLTP برای مصارف تحلیلی در حجم و ابعاد بالا توانایی ندارند، پس باید با نوع دیگه ای از دیتابیس ها کار می کردم. در واقع نیاز من این بود که بتونم مصارف تحلیلی مثل گزارشات لحظه ای، روزانه فصلی، سالیانه و ... سمت داشبورد رو با سرعت بالا به نتیجه برسونم. یکی از راه حل ها، استفاده از دیتابیس های OLAP هست. این دیتابیس ها دیگه مثل OLTP ها داده ها رو Row Oriented ذخیره نمی کنن بلکه مکانیزم ذخیره سازی اون ها به صورت Column Oriented هست. به همین دلیل معمولا توابع تجمعی بسیار سریع پردازش می شن و مناسب مصارف تحلیلی هستند.
سوال؟ نمی شد این کار رو با استفاده از دیتابیس های OLTP هندل کرد؟
پاسخ: بله. اما نیاز های تحلیلی بعضا شامل نیاز های ریل تایم می شن که دیگه دیتابیس های OLTP جواب گو نیستند.
بنابراین به سراغ یکی از معروف ترین این دیتابیس ها، یعنی Clickhouse رفتم که یکی از گزینه های موجود در اکوسیستم کلان داده محسوب میشه.
همونطور که اشاره کردم جداول اصلی که برای گزارش گیری استفاده می شدند، جداولی با حجم بالا بودند. از طرفی چون خوندن اطلاعات از طریق دیتابیس Mysql انجام می شد، برای جداولی با حجم بالا با کوئری های نسبتا پیچیده، بعضا ساعت ها زمان صرف میشد.
گاهی مواقع ما به دنبال یک داده مشخص هستیم که در این حالت با ایندکس گذاری مناسب و بهینه سازی دیتابیس های OLTP مثل MySQL، می تونیم به این هدف برسیم. اما برای گزارشات تحلیلی، دیگه این روش ها پاسخگو نیستند. بسیاری از موارد کسب و کار نیازمند گزارشهای بروز همون لحظه هستند و عدم توجه به این موضوع ممکنه ارزش اون گزارش رو کاهش بده یا بعضا دیگه ارزشی نداشته باشه.
بنابراین در دو فاز مختلف به بررسی این دیتابیس پرداختم.
برای دستیابی به پاسخ این سوال، توی یک سرور تستی (که هشت گیگ رم داشت!) اول جداول MySQL رو در کلیک هاوس ایجاد و بعد داده ها رو منتقل کردم. برای این منظور از ابزار clickhouse-mysql-data-reader استفاده کردم.
لینکش رو قسمت در زیر گذاشتم :
https://github.com/Altinity/clickhouse-mysql-data-reader
در نهایت بعد از ایجاد جداول و انتقال داده ها به مقایسه کوئری ها قبل و بعد از کلیک هاوس پرداختم.
می تونم بگم نتیجه شگفت انگیز بود!
ریسپانس گزارشها، زیر 5 ثانیه بود در حالی که همین گزارشها در MySQL ساعت ها به طول می انجامید.
اما این شگفت زدگی ها تا حدی ادامه داشت!
تا اون لحظه تمامی گزارشهایی که تنها روی یک جدول پردازش میشدند، مورد بررسی قرار میگرفتند. این دفعه اندکی گزارشات پیچیده تر رو بررسی کردم. این گزارشها نیاز به join دو جدول داشتند.(یک جدول سنگین + یک جدول سبک)
سرعت نسبت به حالت قبلی که یک جدول پردازش میشد کاهش پیدا کرد اما با نرخ خوبی پذیرفته شد.
یکی از مهم ترین درس هایی که یادگرفتم این بود که تا جایی که امکان داره از جداول دینرمالایز شده باید استفاده کنیم. یعنی از افزونگی داده ها نترسیم. چراکه دیتابیس های تحلیلی با عملکرد فشرده سازی خوبی که دارند، نمی ذارن مشکل حادی پیش بیاد!
به عنوان نمونه گزارشهایی که در MySQL حدودا 30 دقیقه به طول می انجامیدند، در این حالت 15 الی 20 ثانیه پردازش می شدند.
در انتها گزارشهایی را تست کردم که نیاز به join دو یا سه جدول سنگین داشتند. متاسفانه نتونستم نتیجه این مدل گزارشها رو ببینم. چرا که حافظه سرور کاملا پر میشد و گاها می دیدم سرویس کلیک هاوس restart میشه.
بنابراین نتیجه ای که در فاز اول مطالعات کسب کردم، این بود که در این Case Study با توجه به سخت افزاری که در اختیارم بود، تنها می تونستم کوئری های ساده رو با Clickhouse جواب بگیریم.
من با سه جدول مهم رو به رو بودم که شرایط زیر رو داشتند:
کلیک هاوس Update یا Delete رو مثل دیتابیس های OLTP پشتیبانی نمی کنه اما اخیرا اون ها رو پشتیبانی کرده و شرط استفاده از اون هم پایین بودن فرکانس آپدیتهاست. متاسفانه از اونجایی که در Case Study ما نرخ آپدیت ها بسیار بالا بود، امکان استفاده از دستور آپدیت به طور صریح امکان پذیر نبود.
یکی دیگه از روش های هندل کردن آپدیت، استفاده از موتور های پردازشی هست که از MergeTree ارث بری میکنند.
بنابراین به آزمایش با موتور های مختلف MergeTree پرداختم.
استفاده از Insert به جای Update لازمه استفاده از این موتور ها هست. در نهایت براساس آزمایش های مختلف تنها موتوری که میتونست پاسخگو نیاز Case Study ما باشه، موتور collapsing merge tree بود.
تجربه: با وجود آپدیت های زیاد، موتور collapsing برای گزارشهای یک جدوله بسیار مناسب هست. در صورتی که دو یا سه یا ... جدول رو بخوایم بدون دستور final جوین کنیم گزارشات خطا دارند.
پس با ایجاد جداول براساس موتور collapsing merge tree و همچنین انتقال دادهها، مجددا کوئری ها را بررسی کردم.
متاسفانه به دلیل اینکه برای حذف داده های تکراری در Join، مجبور به استفاده از کلمه final بعد از نام جداول میشدم، سرعت گزارشات افت شدیدی پیدا می کردند و قابل قبول نبودند.
اگه با دستور final بیگانه هستید می تونم بگم دستوری هست که داده های DEDUPLICATE شده رو در لحظه لحظه پردازش کوئری حذف می کنه. (به عنوان توضیحات اضافه در خصوص چرایی این DEDUPLICATE، باید بگم که من داده هایی که آپدیت می شن رو به جای عمل آپدیت، آخرین نسخه اونو insert می کردم. بنابراین دو نسخه از یک ردیف بوجود میومد.)
پس با final می تونیم مطمئن باشیم که همیشه یک نسخه از هر داده وجود داره.
با توجه به محدودیت هایی که در پاراگراف های بالا بهش اشاره کردم، تصمیم گرفتم از کلیک هاوس روی پردازش های یک جدوله استفاده کنم.
چون این دیتابیس برای پردازش ها از همه منابع موجود روی سرور من استفاده می کرد، بنابراین اگه به طور میانگین 7 نفر در حال استفاده از داشبورد باشند، سرعت گزارشات به شدت افت می کرد.
پس قانون جدیدی وضع کردم که هر گزارش باید زیر 1 ثانیه ریسپاس بده!
برای حل این مشکل از materialized view ها استفاده کردم که سمت داشبورد با جداول تجمع شده رو به رو باشم. بنابراین گزارش ها نیازی به پردازش روی 500 میلیون دیتا نداشتند.
برای پشتیبانی از دستور آپدیت و پیچیدگی هایی که برخی گزارشات دارند، نیازمند ابزاری بودم تا بتونه از پس این مسئله بر بیاد. تنها ابزاری که برای این کار مناسب به نظر می رسید، دیتابیس Singlestore بود. این دیتابیس نتنها آپدیت رو ساپورت می کنه بلکه سرعت عالی (در برخی موارد بهتر از Clickhouse) در پردازش کوئری ها داره. داده ها در این دیتابیس به طور کلی در دو نوع جدول میتونن پردازش بشن. اولی rowstore و دومی columnstore که به صورت پیش فرض همین columnstore تنظیم شده. (من از نوع columnstore استفاده کردم.) در کنار سرعت بالا در پردازش ها، این دیتابیس SQL رو به خوبی پشتیبانی می کنه.
برای استقرار و تست این دیتابیس باید سروری با مشخصات بهتر خریداری و استفاده می شد.
این دیتابیس برخی از داده ها رو در یک مدت زمان کوتاهی داخل رم نگه می داره.
بعد از تست های مختلفی که روی این دیتابیس گرفتم می تونم عملکرد این دیتابیس رو عالی ارزیابی کنم.
نرخ فشرده سازی عالی، پشتیبانی از دستورات UPDATE و DELETE، محیط گرافیکی عالی و همچنین سرعت بسیار بالا در data ingestion و ... مهم ترین دلایل انتخاب این دیتابیس بود.
مهم ترین سوالی که به تجربه بهش رسیدم این بود که
آیا همه گزارشات نیاز به پردازش روی جداول سنگین دارند؟!
در این Case Study من در واقع نمی تونستم پیش بینی کنم که دقیقا چه گزارشاتی نیاز ریل تایم بودن دارن یا کدوم ها اینطور نیستند!!!
بروز بودن گزارشات بستگی به نقش های مختلف و افراد مختلف داره و می تونه متفاوت باشه.
در این مقاله سعی کردم به طور خلاصه از تجربیات کار با دو مورد از دیتابیس های تحلیلی که در یک محیط واقعی بدست آوردم، با شما به اشتراک بگذارم.
قطعا در میان ابزار های اصلی تحلیلی نام Clickhouse به عنوان یکی از برترین ها یاد میشه. این دیتابیس روسی، شاید برای Case Study های دیگه پاسخ مناسبی باشه اما برای این Case Study مناسب نبود.
اگه با وجود آپدیت ها، نیازمند به اعمال join های بیش از 2 مورد با جداول سنگین دارید، این دیتابیس نمی تونه انتظارات شما را برآورده کنه. پس توی این دیتابیس اگه محدودیت هایی که گفتم رو نداشته باشید، ممکنه بهترین ابزار براتون محسوب بشه و در نهایت دیگه لذت نوشیدن چای حین اجرای کوئری ها رو از دست بدید!
در دیتابیس Clickhouse میشه با تعریف materialized view ها کندی و فشار پردازش ها رو به حداقل رسوند. توی دیتابیس Singlestore هم باید تا جایی که میشه هر حجمی از داده رو داخلش نریزیم. چون الکی کندش می کنیم! از طرفی هم توی حجم زیاد این دیتابیس خدمات رایگان نمی ده و باید نسخه پولی رو خریداری کنید.
امیدوارم نوشته امروز مورد توجهتون قرار گرفته باشه.
از همراهیتون صمیمانه ممنونم.
ابوالفضل وکیلی