ساخت یک دیتابیس ترکیبی از SQL و فایل های XML
نویسنده: مهدی محمد
تاریخ: ۱۴۰۳/۰۵/۱۹ ۰۸:۲۳
آدرس: www.dntips.ir
سلام. یک نرم افزار رو در نظر بگیرید که هر روز، به صورت خودکار، قیمت 1000 کالا را از 100 وبسایت مختلف دریافت و در دیتابیس ذخیره می کند. پرسش من اینه که کدوم روش زیر برای طراحی دیتابیس این نرم افزار و همچنین منطق کاری اون اصولی تر و بهینه تره:
روش اول: در این روش، دیتابیس نرم افزار، شامل چهار جدول (جدول سایت ها – جدول کالاها – جدول تاریخ ها و جدول ثبت اطلاعات روزانه قیمت کالا) است که جدول ثبت تاریخ و جدول اطلاعات روزانه، ارتباط یک به چند دارند (جدول اطلاعات روزانه، شامل یک کلید خارجی از جدول کالاها، یک کلید خارجی از جدول سایت ها و یک کلید خارجی از جدول تاریخ ها است) و نرمالسازی جداول هم انجام شده.
در این روش، هر روز، به صورت خودکار، نرم افزار، ابتدا تاریخ جاری را در جدول تاریخ ها ثبت می کند و سپس، به ازای هر سایت و به ازای هر کالا، یک رکورد (شامل شناسه تاریخ جاری، شناسه سایت و شناسه کالا) را به همراه قیمت روز آن کالا که از وبسایت مربوطه دریافت کرده است، در جدول اطلاعات روزانه ثبت می کند.
بنابراین، حدود 100 هزار رکورد در هر روز باید در این جدول ثبت گردد که هم زمانبر است و هم ممکن است هنگام ثبت این داده ها در دیتابیس، خطایی رخ دهد (فرض کنیم که صحت داده ها اهمیت زیادی داشته باشد). پس لازم است پس از ثبت این رکوردها، همه آن ها مجددا چک شود که عملیات ثبت ناقص انجام نشده باشد و این هم مدتی زمان لازم دارد. ضمن آنکه حجم دیتابیس نیز روز به روز افزایش خواهد یافت.
مشکل دیگر این روش آن است که اگر در آینده، تعداد سایت ها یا تعداد کالاها افزایش یابد، اجرای این روش دشوارتر و احتمال خطای آن بیشتر می شود. مثلا اگر تعداد سایت ها 1000 و تعداد کالاها 1000 شود، هر روز باید یک میلیون رکورد در جدول اطلاعات روزانه ثبت و کنترل گردد.
روش دوم: در این روش، دیتابیس شامل همان چهار جدول قبلی است. با این تفاوت که در جدول اطلاعات روزانه، یک فیلد از نوع XML و یا یک فیلد از نوع رشته ای برای ثبت تمامی اطلاعات لازم کالا در یک روز (شامل شناسه تاریخ، شناسه کالا، شناسه سایتی که اطلاعات از آن دریافت می شود و سایر مشخصات لازم) در نظر گرفته شده است. بنابراین، در این روش، در ابتدای شروع کار نرم افزار، ابتدا به ازای هر کالا، یک رکورد در جدول اطلاعات روزانه ثبت می شود. بنابراین، کلا 1000 رکورد در این جدول وجود خواهد داشت. سپس، هر روز، عملیات دریافت اطلاعات هر کالا از سایت های مربوطه انجام شده و فایل XML یا رشته مربوط به رکورد متناظر با آن کالا ویرایش می شود. بنابراین، در این روش، همیشه کلا 1000 رکورد وجود دارد که باید به صورت روزانه، بروزرسانی شوند و این موضوع، سرعت کار را افزایش و میزان خطا و همچنین حجم دیتابیس را نیز کاهش می دهد. البته به نظرم این روش غیر اصولی است و اصول نرمال سازی و ... در مورد اون رعایت نشده و خیلی ایراد داره.
حالا پرسش من اینه که اگر محدودیت زمان برای دریافت و ثبت داده های روزانه وجود داشته باشد (مثلا کلیه اطلاعات باید در 5 دقیقه دریافت و ثبت شود) و در آینده نیز تعداد رکوردهایی که باید به صورت روزانه ثبت شود افزایش پیدا کند، کدامیک از این دو روش، از نظر طراحی و هم از نظر کاربری نرم افزار، بهینه تر و اصولی تر است؟ آیا برای رسیدن به سرعت و کارایی بالاتر، مجاز هستیم از روش دوم که غیر اصولی به نظر میرسه استفاده کنیم؟ آیا ساخت یک دیتابیس ترکیبی از SQL و فایل های XML به این شکل کار درستی است؟ در غیر اینصورت، روش اصولی برای نرم افزارهایی که باید در هر عملیات، تعداد زیادی داده را ثبت و مدیریت کنند چیست؟ با تشکر.
یقینا سرعت کار با بانکهای اطلاعاتی رابطهای و امکانات توکار آنها همیشه بیشتر از کار با XML و هر حالت مشابه دیگری در آنهاست و بنابراین ... بله. حالت دوم بهینهتر نیست، سریعتر نیست و همچنین کمحجمتر هم نیست. اساسا بانکهای اطلاعاتی رابطهای زمانی طراحی شدند که یک هارد دیسک 4 مگابایتی، چندهزار دلار قیمت داشت. به همین جهت در زمینه ذخیره سازی اطلاعات، بسیار بهینه و کمحجم عمل میکنند؛ با حداقل تکرار و سربار و با سرعت بالا. استفاده از XML و JSON امثال اینها زمانی باب شدند که قیمت هارد دیسکهای حجیم کاهش یافته بود و همچنین بیشتر سرعت read مطرح بود و نه سرعت write. اطلاعات بیشتر
یعنی می فرمائید اینکه در روش اول، هر روز تعداد زیادی رکورد به دیتابیس افزوده میشه ولی در روش دوم، فقط تعداد ثابتی رکورد (به ازای هر کالا یک رکورد) وجود داره که هر روز، یک فیلد آن (مثلا در قالب یک رشته) بروزرسانی میشه، بازهم روش دوم کم حجم تر از روش اول می شه؟ مثلا برای 1000 کالا، در روش اول، پس از یکسال، 10 میلیون رکورد باید ثبت بشه ولی در روش دوم، فقط 1000 رکورد در سال وجود داره که البته هر روز، فایل XML (یا در قالب رشته) مربوط به این رکوردها با زمان حجیم تر میشه.
در واقع مشکل اصلی من اینه که نرم افزار، هر روز، باید تعداد زیادی داده رو در مدت زمان کوتاهی دریافت و ثبت کنه. برای کاهش مدت زمان لازم برای این کار، من از BullkInsert و حتی افزونه هایی مانند Z.EntityFramework.Extensions استفاده می کنم. ولی بازهم زمان زیادی برای دریافت و ثبت داده ها لازم هستش. به همین دلیل می خوام بدونم آیا در این سناریوی خاص، میشه طراحی رو به شکلی تغییر داد که بر خلاف روش اول، نیازی به ثبت این تعداد رکورد در هر روز نباشه و یا اینکه ثبت این داده ها اجتناب ناپذیره؟
همانگونه که شما فرمودید روش دوم به وضوح غیر اصولی است ولی آیا جنابعالی پیشنهادی برای اجرای روش اول به صورت اصولی و بهینه دارید؟
۱: راهکار بهینه، نیاز به دیتای بیشتری داره، چون مثلا سایز تیم تولید، دانش و امکانات نگهداری سیستم بعد از راهاندازی، زیرساخت و امکانات سختافزاری و... میتونه پاسخ نهایی رو کلی تغییر بده.
۲: ۱۰۰۰ کالا از ۱۰۰ سایت، آیا دامنه تغییر این اعداد مثلا ۱۲۰۰ کالا از ۱۳۰ سایت خواهد بود؟ یا امکانش هست که در کوتاه/میانمدت تبدیل به ۱۰٬۰۰۰ کالا از ۵۰۰۰ سایت بشه؟ (پیشبینی رشد)
۳: ایندکسگذاری روی XML در SQL Server به خوبی پشتیبانی میشه، سایز XML در سریالایز/دیسریالایز شدن و طبیعتن در کارایی سیستم اثرگذار است
۴: برای محصولی مثل SQL Server نگهداری ۴۰ میلیون رکورد (۱ سال داده با اعدادی که فرمودید) عدد کوچکی است، «ولی» بسیار وابسته به اینه که زیرساخت خوب و مدیردیتابیس با دانشی داشته باشید وگرنه حتی ۴۰۰٬۰۰۰ رکورد هم میتونه مشکل ایجاد کنه
۵: این نوع سیستمها عموما در مرحله جمعآوری به صورت OLTP طراحی میشن و برای سابقه به DW با ساختار Dimensional تبدیل و منتقل میشن. باز هم بستگی به تیم و شرایط تولید داره
۶: نکته بعدی اینکه مثلا به راحتی میتونید دیتا رو روی جدول پارتیشنبندی کنید و پارتیشنهای قدیمی رو فشرده کنید
بله درسته. بنابراین، طبق گفته های شما و آقای نصیری، روش دوم باید کنار گذاشته بشه.
در روش اول، مشکل من بیشتر با ثبت و کنترل این تعداد رکورد (در مدت زمان کوتاه) هستش و در مورد حجم دیتابیس همانگونه که شما اشاره کردید این تعداد رکورد برای SQL (در صورتیکه صحیح مدیریت شود) عدد بسیار کوچکی است و مشکلی ایجاد نخواهد کرد.
در مورد تعداد پیشبینی رشد، نهایتا 1000 کالا و 1000 سایت خواهد بود و بیش از این نخواهد شد.
ولی ثبت همین تعداد رکورد (1 میلیون رکورد روزانه) هم باید در مدت زمان کوتاهی (مثلا 10 دقیقه) انجام بشه. در واقع، مشکل اصلی، مدت زمانی است که باید این داده ها دریافت و ثبت بشه. من دنبال روشی هستم (مثلا تغییر طراحی دیتابیس یا افزایش سرعت ثبت اطلاعات) که بشه این کار رو در زمان کوتاهی انجام داد. بررسی هایی که من انجام دادم دریافت اطلاعات از وب، زمان زیادی لازم ندارد و بیشتر بحث مدت زمانی است که داده ها در دیتابیس ثبت می شوند.
.بنابراین، اگر طراحی دیتابیس مشکلی نداشته باشه، باید روشی پیدا کنم که بتونم این داده ها رو در مدت زمان مورد نظر ثبت کنم
طبق مقالاتی که آقای نصیری به اون ها ارجاع دادند، با استفاده از روش هایی مثل MultiThreading میشه این کار رو انجام داد.
اگر مشکل در ذخیره و نمایش داده ها به صورت آنی ندارید، پشنهاد میکنم یک message broker در سر راه این معماری قرار بدید تا مشکل که بیان کردید در مورد (تایم 10 دقیقه ) درج داده و مشکل دیتابیس رو به حداقل برسونید.
کجا می تونم اطلاعاتی در مورد این روش پیدا کنم؟
پیشنهاد میکنم یک کافکا یا rabbit بالا بیارید و کالاهایی که crawl کردید رو ابتدا روی صف قرار بدید(fire and forget) و یک background service داشته باشید که به صف گوش بده و دیتا رو به دیتابیس انتقال بدید.
مثالی با rabbit
پیشنهاد میکنم، ابتدا با ابزاری مثل benchmarkdotnet نرخ دریافت و ذخیره رو اندازهگیری کنید. بعد ببینیم آیا گلوگاهی وجود داره یا نه، و اگر داره کجاست. چون الان هر بحث و راهحل پیشنهادی مثل پیشنهاد دکوراسیون یک اتاق کاملا تاریک است!
شاید لازم باشه بعدش پرفرمنس دیسک دیتابیس سرورتون رو با ابزاری مثل diskspd بسنجید. یا به فکر in-memory oltp روی slq server باشید که سرعت درج داده رو کاهش بده.
استفاده از Multithreading هم حتمن یکی از پیشنهادهای ضروری و خوب است.