ذخیره‌سازی فایل‌های کوچک – HBASE MOB

ذخیره سازی فایل های کم حجم یکی از مسائلی‌ هست که اکثر برنامه‌نویس‌های وب باهاش برخورد می‌کنن و ممکنه خیلی ساده به نظر بیاد. البته اکثر برنامه نویس‌ها راه حل‌های ساده‌ای برای این مسئله دارن.

ایده خیلی ساده‌ست. فایل‌ها رو روی دیسک ذخیره می‌کنیم و آدرس اون‌ها به عنوان Meta در دیتابیس ذخیره می‌کنیم. بنابراین خیلی راحت میشه با داشتن آدرس‌ها به فایل‌ها دسترسی داشت. اکثر اوقات این فایل‌ها به صورت Local ذخیره می‌شن و گاهی هم از طریق nfs روی وب‌سرور مانت می‌شن.

این روش تا حالا برای کاربردهای معمولی و ساده خیلی خوب جواب داده اما اگر با چند صد میلیون فایل روبرو باشیم این روش دو ایراد خیلی بزرگ داره.

۱) عدم مقیاس پذیری (فضای ذخیره سازی / توزیع بار) :‌ به عبارتی ساده خوندن و نوشتن داده به یک سرور محدود میشه که محدودیت دیسک و کارت شبکه مهمترین عامل افت کارایی خواهد بود.

۲) عدم وجود نسخه دوم (replication) : هیچ نسخه دومی از فایل‌ها وجود نداره و در صورتی که فایلی به هر دلیلی مثل خرابی دیسک از بین بره راهی برای بازیابی وجود نداره.

MOB

MOB یا Medium Object Storage قابلیتی هست که برای براورده کردن چنین نیازمندی در نسخه ۲ HBase ارائه شده. این قابلیت باعث میشه که HBase فایل‌هایی با حجم بین چند کیلو بایت تا ۱۰ مگابایت رو به خوبی و با تاخیر پایین ذخیره و بازیابی کنه.

در حقیقت در نسخه‌های قبلی HBase به دلیل وجود Compactionهای متعدد ، نوشتن و خوندن فایل‌ها با تاخیر زیاد مواجه می‌شد. بنابراین در نسخه جدید با در نظر گرفتن این مسئله ، تا حدی در ذخیره سازی فایل‌های کم حجم بهبود داشته.

فایل‌ها یا همون MOB objects ها در یک Region با اسم MOB در قالب یک سری فایل ذخیره می‌شن. پس به طور خلاصه فایل‌های کم حجم در در فایل‌های بزرگتری ذخیره می‌شن و HBase با داشتن Metaها قابلیت جواب دادن درخواست‌های READ رو داره.

فایل‌های MOB در ابتدای کار ممکنه حجم کمی داشته باشن (مثلا کمتر از بلاک‌های HDFS) بنابراین در طول زمان این فایل‌ها بر اساس یک سری سیاست‌ها با هم دیگه ادغام می‌شن (اصطلاحا MOB compaction) و فایل‌های بزرگتری رو به وجود میارن.

برای اینکه بیشتر با این روند آشنا بشیم فرض کنید یک جدول داریم به اسم t1 و Column Family به اسم cf1. این جدول از دو ریجن D279186428a75016b17e4df5ea43d080 و D41d8cd98f00b204e9800998ecf8427e تشکیل شده.

برای ریجن اول در هر کدام از تاریخ‌های 1/1/2016 و 1/2/2016 دو فایل MOB ایجاد شدن و برای ریجن دوم هم سه فایل MOB داریم که هر سه در تاریخ 1/1/2016 بوجود اومدن.

بنابراین انتظار داریم روی hdfs در مسیر زیر ۷ فایل MOB داشته باشیم

‫ hdfs dfs -ls /hbase/data/mobdir/data/default/t1/78e317a6e78a0fceb27b9fa0cb9dcf5b/cf1

    D279186428a75016b17e4df5ea43d08020160101f9d9713ab2fb4a8b825485f6a8acfcd5

D279186428a75016b17e4df5ea43d08020160101af7713ab2fbf4a8abc5135f6a8467ca8    

D279186428a75016b17e4df5ea43d080201601029013ab2fceda8b825485f6a8acfcd515    

D279186428a75016b17e4df5ea43d080201601029a7978013ab2fceda8b825485f6a8acf    

D41d8cd98f00b204e9800998ecf8427e20160101fc94af623c2345f1b241887721e32a48    

D41d8cd98f00b204e9800998ecf8427e20160101d0954af623c2345f1b241887721e3259    

D41d8cd98f00b204e9800998ecf8427e20160101439adf4af623c2345f1b241887721e32    

فرض کنید سیاست Compaction طوری باشه که همه‌ی فایل‌های مربوط به یک روز با همدیگه ادغام بشن. بنابراین انتظار داریم فایل‌های تاریخ 1/1/2016 با هم و  1/2/2016 با هم و …

در نتیجه فقط ۳ فایل باید داشته باشیم :

D279186428a75016b17e4df5ea43d08020160101f49a9d9713ab2fb4a8b825485f6a8acf

D279186428a75016b17e4df5ea43d08020160102bc9176d09424e49a9d9065caf9713ab2

D41d8cd98f00b204e9800998ecf8427e20160101d9cb0954af623c2345f1b241887721e3

با توجه به اینکه طی سال ۳۶۵ روز داریم ، حداقل (۳۶۵ x تعداد ریجن‌ها) فایل MOB برای یک سال خواهیم داشت. بنابراین برای ۱۰۰۰ ریجن ، طی ۱۰ سال حدود ۳.۶ میلیون فایل MOB داریم و تعدادشون رفته رفته بیشتر هم میشه. از طرفی hdfs برای داشتن تعداد فایل‌های بیشتر به ram بیشتری نیاز داره و افزایش تعداد این فایل‌ها باعث میشه NameNode به حافظه بیشتری نیاز داشته باشه. همچنین پارامتری در hdfs بیشترین تعداد فایل‌های موجود در یک دایرکتوری رو مشخص می‌کنه (dfs.namenode.fs-limits.max-directory-items) که به صورت پیش‌فرض مقدار اون یک میلیون هست. که برای ۱۰۰۰ ریجن فقط حدود ۳ سال دوام میاره.

در HBASE-16981 برای کاهش این مشکلات روش‌های ادغام هفتگی و ماهانه معرفی شد. در این حالت به ازای یک هفته/ماه فقط یک فایل به وجود میاد که به مراتب حجم بیشتری داره.

نحوه اجرای این ادغام اهمیت زیادی داره. مثلا فرض کنید از روش ادغام ماهانه استفاده می‌کنیم. در  آخر هر روز فایل‌های روز فعلی باید با فایل‌های روز قبل ادغام بشن. بنابراین تا پایان ماه فایل‌های مروبط به روز اول ۳۰ بار نوشته و خونده می‌شن! واضحه که چنین سیاستی کارایی سیستم رو به شکل قابل توجهی تحت تاثیر قرار می‌ده .

 

پیاده سازی نهایی

برای جلوگیری از ادغام‌های مکرر داده‌های یکسان، HBASE-16981 پیشنهاد شد. در شکل بالا فایل‌های مربوط به روز ۱۵ نوامبر با هم و ۱۴ نوامبر هم به طور مجزا با هم ادغام شدن. همچنین فایل‌های تقویم هفتگی (روزهای ۷ نوامبر تا ۱۳ نوامبر) هم به طور مجزا با هم و فایل‌های تقویم هفتگی اول ماه با هم ادغام شدن. به همین ترتیب بر اساس تقویم ماهانه فایل‌های مربوط به ماه گذشته با هم ادغام شدن.ممکنه فکر کنید که یک اشتباه رخ داده و هفته اول ماه بجای ۷ روز ، ۶ روز در نظر گرفته شده. اما باید دقت کنید که اولین تقویم هفتگی نوامبر ۲۰۱۶ از تاریخ ۱۰/۳۱/۲۰۱۶ تا ۱۱/۶/۲۰۱۶ هست و چون ادغام ماهانه اتفاق افتاده فایل‌های مربوط به روز اول این هفته با فایل‌های ماه قبل ادغام شدن (:

بنابراین تا این مرحله باید فقط ۵ فایل داشته باشیم. با این پیاده سازی فایل‌ها معمولا ۳ بار ادغام میشن ( یکبار ادغام روزانه و یکبار هفته‌ای و یکبار دیگر ماهانه)

اصل این مقاله رو می‌تونید از بلاگ کلودرا که هنوز تحریم نیست بخونید (:

کوین هایو

طی چند سال اخیر که یک بررسی در خصوص دامنه‌های سه‌حرفی .ir داشتم، آمارهای جالبی بدست آوردم که یکی از اون‌ها نظرم رو بیشتر از بقیه جلب کرد.

خب احتمالاً میدونید که من هم مثل همه گیگ‌ها در اوقات فراغتم، یک سری شیطونی‌های کوچیک هم می‌کنم. اینبار ایده این بود که بفهمم چه کسانی بیشتر از همه دامنه به اسم خودشون ثبت کردند. بنابراین مثل همیشه فقط یک اسکریپت ساده نوشتم که با استفاده از whois مشخصات مالکین دامنه‌ها رو در یک فایل لاگ ذخیره می‌کرد و در نهایت می‌شد اون‌ها رو بررسی کرد.

در بین این گراف بزرگی که من برای خودم از ارتباط تعداد دامنه‌های سه‌حرفی ساخته بودم، یک شخص بیشتر از بقیه اسمش در مالکیت دامنه‌ها تکرار شده بود. شخصی با نام Mohammad Khezri که طبق آخرین آماری که درآورده بودم، حدود ۱۶۰۰دامنه با طول سه‌حرف رو به اسم خودش خریداری کرده بود.

اولین چیزی که به ذهنمون میرسه اینه که احتمالاً ایشون دلال دامنه هستند و یا یکی از مافیای دامنه در ایران و یا حتی جهان هستند! این آمار هر چند موضوع جذابی نبود ولی ۲ سال بعد سر نخ یک موضوع جالب شد (:

از این‌ حرف‌ها که بگذریم، امروز در یک سناریو کاملا متفاوت و در پی‌ سوء استفاده‌هایی که اخیراً برای تولید پولی‌های مجازی از سیستم کاربران آنلاین می‌شد، به ذهنم رسید که چند مورد از دامنه‌های ir. که به چنین روشی اقدام به سو استفاده می‌کنند رو بررسی کنم که یکبار دیگه اسمِ این فرد رو مشاهده کردم! بله؛ درست حدس زدید، با یک آنالیز ساده می‌شد فهمید که این شخص، توی یک‌سری از دامنه‌هایی که خریده از یک اسکریپتِ CoinHive استفاده می‌کنه که فقط کافیه شما آدرس یکی از سایت‌های طراحی شده با این اسکریپت رو باز کنید تا حساب بانکی ایشون رو بزرگ‌تر کنید.

برای اطمینان بیشتر پیشنهاد می‌کنم TaskManager سیستم‌عاملِ خودتون رو باز کنید و سپس در مرورگرِ آدرس  http://fuv.ir رو  وارد کنید. تعجب نکنید؛ پردازنده‌ی دستگاه شما در اختیار حساب بانکی آقای Khezri قرار گرفته (: در حقیقت وقتی شما به این وب‌سایت‌ها سر می‌زنید دوستان با استفاده از یک کتابخونه جاوا اسکریپتی از منابع شما برای تولید پول دیجیتالی استفاده می‌کنند.

منطقا چنین کاری غیراخلاقیه اما چون ظاهرا کنترل درستی روی این‌ وب سایت‌ها وجود نداره، بهتره خودتون مراقب باشید. اگر در حین کار با لپ‌تاپ متوجهِ هرگونه اختلال شدید، و یا احساس کردید دستگاه به شدت داغ شده، احتمالاً یکی از سایت‌های با اسکریپت مخرب رو باز کرده‌اید.

آپدیت : طبق صحبتی که با آقای خضری عزیز شد ظاهرا ایشون از این اسکریپت بی اطلاع بودن و کسی دیگه این چند هزار دامنه رو برای ایشون مدیریت می‌کنه که اون شخص این کار رو کرده! 

هر چند این حرف از دید یک گیک مسخره بنظر می‌رسه ولی به هر حال اسکریپت کوین‌هایو از روی چندتا دامنه‌ای که امروز ۶ بهمن به صورت دستی چک کردم برداشته شده و  جا داره به خودمون تبریک بگیم (:

پیکربندی کافکا – پارامترهای مهم سیستم‌عامل

آپاچی کافکا یکی از پلتفرم‌هایی هست که برای موارد متنوعی از خزشگر موتور جستجو گرفته تا سیستم‌های تحلیل لاگ کاربرد خاص خودش رو داره. هدفم از این پست بیان چند مورد از پیکربندی‌های مربوط به سیستم عامل هست که می‌تونه اولین گام برای بهبود کارایی کافکا باشه. در اینده در مورد سایر پارامترها هم صحبت می‌کنیم.

صفحات کثیف!

سیستم‌عامل برای اینکه عملیات I/O رو بهتر انجام بده از حافظه اصلی با دسترسی تصادفی (RAM) استفاده می‌کنه و باعث می‌شه کارایی سیستم به شکل قابل توجهی برای بارکاری (workload) نوشتن بهبود پیدا کنه. وقتی شما در حال نوشتن بر روی دیسک هستید داده‌ها ابتدا در حافظه اصلی ذخیره و بعدا در زمانی مشخص روی دیسک ذخیره می‌شن. به این صفحات از حافظه که هنوز روی دیسک فلاش نشده‌‌ان صفحات کثیف گفته می‌شه.
دو مورد از پارامترهای مهمی که در کرنل لینوکس صفحات کثیف رو کنترل می‌کنن در زیر ذکر شده:

  • vm.dirty_ratio:

بیشترین مقداری از حافظه available که می‌تونه برای ذخیره‌سازی صفحات کثیف استفاده بشه. این مقدار به درصد بیان می‌شه. بعد از اینکه این حدآستانه فرا برسه پردازه‌ای که در حال نوشتن بوده بلاک می‌شه و منتظر خالی شدن می‌مونه.

  • vm.dirty_background_ratio

درصدی از حافظه رو تعیین می‌کنه که به محض رسیدن میزان صفحات کثیف به اون، ترد pdflush شروع به کار می‌کنه و صفحات کثیف رو بر روی دیسک می‌نویسه. این مقدار به درصد بیان می‌شه و پیش‌فرض این پارامتر ۱۰ ست. بنابراین کاهش این مقدار سبب می‌شه تا کرنل در بازه‌های زمانی کوتاه‌تری فلاش کنه و اگه اون رو به صفر برسونیم کارایی سیستم به شکل قابل توجهی کاهش پیدا می‌کنه.

خب حالا؟
با توجه به اینکه در کافکا دیسک نقش تعیین کننده‌ای در کارایی سیستم داره پیکربندی این پارامترها مهمه. تجربه ما ثابت کرده که dirty_ratio رو بهتره در حدود ۶۰ قرار بدیم. این مقدار رو می‌تونیم حتی در حد ۸۰ درصد هم ببریم ولی در کل هر چه این مقدار افزایش پیدا کنه داده‌های فلاش نشده افزایش پیدا می‌کنه و باعث میشه زمانی که سیستم عامل فلاش کردن رو force میکنه (یعنی synchronous) یک وقفه در سیستم اتفاق بیفته. از طرفی دیگه حتی زمانی که کافکا crash می‌کنه باعث از دست رفتن داده می‌شه که این رو می‌تونیم با استفاده از replication‌ حل کنیم. در مورد پارامتر dirty_background_ratio هم توصیه می‌کنیم اون رو به ۵ کاهش بدید.
برای اطلاعات بیشتر این راهنما رو بخونید.

بافر شبکه

کرنل لینوکس این قابلیت رو به ما میده که خیلی راحت اون رو برای موارد خاص پیکربندی کنیم. فراموش نکنید که کرنل لینوکس برای انتقال‌های سریع و حجیم از پیش پیکربندی نشده. به همین دلیل توصیه می‌کنم برای کافکا این تنظیمات رو متناسب با بار کاری خودتون تنظیم کنید. بنابراین با استفاده از پارامترهای net.core.wmem_default و net.core.rmem_default میزان بافر read/write سوکت‌های سیستم رو مشخص کنید. توصیه می‌کنم که برای این دو پارامتر مقدار ۱۳۱۰۷۲ (۱۲۸ کیلوبایت) رو در نظر بگیرید. علاوه بر این دو پارامتر .net.core.[r|w]mem_max هم داریم که حداکثر مقدار این بافرها رو مشخص می‌کنن که توصیه میشه اون رو به ۲۰۹۷۱۵۲ (۲ مگابایت) مقدار دهی کنید.
علاوه بر این پارامترها، دو پارامتر net.ipv4.tcp_[w/r]mem هم برای تنظیمات tcp وجود داره که باید متناسب با بارکاری تغییر پیدا کنن. این دو پارامتر هر کدام ۳ مقدار min|default|max دارن که اولی حداقل میزان بافر و دومی مقدار پیش‌فرض و سومی حداکثر مقدار رو تعیین می‌کنه. این پارامترها رو با مقادیر ۴کیلوبایت (حداقل) ، ۶۴کیلوبایت(پیش‌فرض) و ۲ مگابایت (حداکثر) مقدار دهی کنید.

حافظه مجازی

در مرحله اول روی هر سرویس‌دهنده‌ای توصیه می‌کنم swap رو غیر فعال کنید چون اگر به هر دلیلی برنامه شما نیاز به حافظه بیشتر داشته باشه و از swap استفاده کنه به شدت کارایی‌ش رو از دست می‌ده. اگر اصرار بر این دارید که از swap استفاده کنید توصیه می‌کنم پارامتر vm.swappiness رو به یک مقدار کم (مثلا ۱) مقدار دهی کنید. از مقدار صفر استفاده نکنید مگر اینکه دقیقا کرنلی که استفاده می‌کنید رو بشناسید. در گذشته مقدار صفر باعث می‌شد فقط در زمانی که outOfMemory اتفاق می‌افتاد از swap استفاده بشه ولی در حال حاضر معمولا مقدار صفر باعث میشه که هیچ وقت swap استفاده نشه.

دیسک

برای دیسک تنظیمات خاصی لازم نیست انجام بدید فقط حتما برای moun point دیسک از noatime استفاده کنید چون درغیر این‌صورت باعث می‌شه بعد از هر بار read هم زمان دستیابی فایل‌ها تغییر کنه که به این معنی یک write هست. این مشکل در دیسک‌های ssd هم خودش رو به شکلی دیگه نشون می‌ده که کم شدن عمر ssd هست.

صدک ۹۹ام تاخیر

یکی از مشکلاتی که توی بیان کارایی سیستم توزیع شده داریم بحث زمان پاسخ یا همون response time هستش و برای اینکه کمی با این بحث و اهمیتش آشنا بشیم من این متن رو اینجا می‌نویسم.
همه می‌دونیم تأخیر در سیستم‌های کامپیوتری یعنی مدت زمانی که یک پردازه دستورالعمل‌های خودش رو انجام میده و خاتمه پیدا میکنه. روش‌های متفاوتی مثل میانه، میانگین و صدک‌ها برای اندازه‌گیری تأخیر در این سیستم‌ها مطرح هستن. اما بایستی ببینیم واقعاً این معیارها برای اندازه‌گیری زمان پاسخ مناسب اند؟
دو معیار اول یا به عبارتی معیارهای میانگین و میانه روش‌های مناسبی برای توصیف طول تأخیر سیستم‌های کامپیوتری نیستن! چون این روش‌ها بدترین حالت‌ها (زمان پاسخ‌های طولانی) رو در خودشون محو می‌کنن و ابزارهای پایشی که بر این مبنا تأخیر رو محاسبه می‌کنن قادر به تشخیص موارد خاص نیستند. اگه واضح‌تر بخوام بگم فرض کنید ۲۰ تا درخواست به سیستم وارد شدن و زمان پاسخ اون‌ها بر حسب میلی‌ثانیه به شکل زیر هستش:

۱ ، ۱ ، ۱، ۱ ، ۲ ، ۲ ، ۲ ، ۳ ،۳ ، ۳ ، ۳ ، ۳ ، ۴ ، ۴ ، ۴ ، ۴ ، ۴ ، ۵ ، ۱۵ ، ۱۶

خب اگه بخواییم میانگین زمان پاسخ رو حساب کنیم میشه حدود ۴ میلی ثانیه و ما هم خیلی خوشحالیم! اما واقعیتش اینه که دو تا زمان پاسخ خیلی بد داشتیم که یطورایی در نتیجه کلی محو شدن و اثری ازشون دیده نمیشه!
اگه در مقیاس وسیع نگاه کنید یک درصد چیز کمی نیست. ممکنه ۱۰۰۰ تا درخواست (کاربر) با تأخیر زیاد مواجه بشن و در نتیجه کسی از سیستم ما راضی نباشه و سراغ یک جایگزین بهتر بره (:

شاید این بحث روی نمودار جالبتر باشه. توی نمودار زیر به طور میانگین زمان پاسخ در حدود ۵۰ میلی ثانیه است و در مواردی نادر به۱۰۰ میلی ثانیه می‌رسه.

نمودار میانه و میانگین

همونطور که می‌بینید این نمودار در بدترین حالت (تأخیر زیاد) توانایی نمایش تاخیر سیستم رو نداره.
حالا وضعیت سیستم رو با استفاده از یک معیار دیگه به نام صدک بررسی می‌کنیم. اینم بگم که ما انواع مختلفی از صدک داریم که توی نمودار زیر فقط صدک ۹۹ ام تأخیر نمایش داده شده و تمرکز رو روی «یک درصد زمان پاسخ خیلی بد» گذاشتیم.

صدک ۹۹ام تاخیر

نمودار بالا به طور باور نکردنی با نمودار اولیه (میانگین و میانه) تفاوت داره. این نمودار بیان می‌کنه که در ساعت ۹:۳۶ دقیقه ۹۹ درصد مقادیر کمتر از ۸۵۰ میلی ثانیه هستن یا به عبارتی دیگه یک درصد کاربرها تاخیری بیشتر از ۸۰۰ میلی ثانیه رو تجربه کردن!
نکته‌ای که وجود داره اینه که اگه از معماری توزیع شده استفاده کنیم این طول تأخیر تشدید می‌شه چون معمولاً توی این سیستم‌ها یک درخواست به چندین درخواست کوچکتر شکسته میشه و بصورت توزیع شده، همزمان روی صدها و یا هزاران ماشین‌ها اجرا و در نهایت نتیجه کل پاسخ‌ها تجمیع و به کاربر تحویل داده می‌شن.

برای اینکه کمی مستند صحبت کنیم باید از آمار و احتمالی که بلدیم استفاده کنیم (:

فرض کنید میانگین زمان پاسخ یک سرویس دهنده ۱۰ میلی‌ثانیه ست اما صدک ۹۹ ام تأخیر اون برابر با یک ثانیه باشه. بنابراین یک درصد از درخواست‌ها تاخیر یک ثانیه ای را تجربه می‌کنن. حالا تصور کنید درخواست کاربر برای انجام شدن نیاز به اجرا بر روی صد سرویس دهنده رو داشته باشه. یعنی ما داده‌ها رو بین ۱۰۰ سرویس‌دهنده توزیع کردیم (روش sharding) بنابراین :
درخواست توسط یک گره ریشه بین تعداد زیادی ماشین توزیع میشه و برای تولید پاسخ نهایی لازمه منتظر پاسخ تمام سرویس دهنده‌ها بمونیم.

خب اول فرض‌های مسأله رو می‌نویسیم:
احتمال اینکه «یک» سرویس دهنده به‌موقع پاسخ خود را تولید کند : ۹۹ درصد
تعداد کل سرویس دهنده ها : ۱۰۰
حالا مسئله مشخصه و می‌تونیم با داشتن این فرض‌ها احتمال تأخیر هر درخواست رو به صورت زیر محاسبه کنیم:

خب طبق این محاسبه ۶۳ درصد کاربرها تأخیر بیش از یک ثانیه رو تجربه می‌کنن و عملاً میشه گفت سیستم شما بدرد نمی‌خوره چون بیشتر مواقع کنده (:

 

سلام دنیا!

سلام بالاخره تصمیم گرفتم تا تنبلی رو کنار بذارم و شروع کنم به نوشتن. این وبلاگ رو با ایده اینکه قراره بیشتر در مورد سیستم‌های توزیع شده صحبت کنیم با نام اختاپوس انتخاب کردم و سعی دارم در این وبلاگ تجربه‌ها و دانش اندک خودم رو با بقیه به اشتراک بذارم. البته سعی می‌کنم وقت بیشتری روی این وبلاگ بذارم و بهترش کنم. (: