در قسمت قبلی با insert های ترتیبی (ordered inserts) و غیر ترتیبی آشنا شدید اما گزینه دیگری نیز به نام writeConcern وجود دارد که باید آن را بررسی کنیم. در جلسات اولیه این دوره آموزشی، روند ثبت داده ها در MongoDB را بررسی کردیم. این روند بدین شکل بود که شما یک کلاینت (client) دارید که یا shell است یا برنامه شما روی سرور که با PHP یا زبان های دیگر نوشته شده است. زمانی که شما کوئری write (دستوراتی مثل insertOne و update) خود را به سرور MongoDB ارسال می کنید، این سرورِ MongoDB نیست که داده های شما را در دیسک ثبت می کند بلکه دستور و داده های شما را به موتور ذخیره سازی (storage enging) می دهد. سپس موتور ذخیره سازی ممکن است داده ها را در ابتدا روی مموری ببرد تا به داده های مورد نیاز دسترسی سریع تری داشته باشد و همه چیز را بررسی کند چرا که دسترسی به مموری (مثلا RAM شما) بسیار سریع تر از دسترسی به دیسک (حافظه HDD یا SSD) است. در نهایت بعدا داده ها حتما روی دیسک ذخیره می شوند. این روند معمول ثبت داده ها در MongoDB است.
ما می توانیم خصوصیتی به نام writeConcern را برای دستورات Write خود (مثلا insertOne و updateOne و غیره) تعریف کنیم که چنین شکلی دارد:
{w: 1, j: undefined}
حرف w یعنی write و مشخص می کند که چند instance (نمونه) از سرورِ MongoDB باید این دستور را تایید کنند (ما می توانیم چندین نمونه از سرور های MongoDB را روی یک سرور میزبانی داشته باشیم که بحث آن مربوط به فصل های آینده می شود و فعلا کاری با آن نداریم). به طور خلاصه w:1 (حالت پیش فرض) یعنی سرورِ MongoDB من باید این دستور Write را قبول کند تا storage Engine از آن مطلع باشد و نهایتا داده هایتان را روی دیسک ذخیره کند.
حرف j مخفف journal است. فایل journal یک فایل جداگانه است که تحت مدیریت storage engine می باشد که شبیه یک فایل todo است (لیستی از کار هایی که باید انجام شوند). یعنی لیستی از کار هایی است که storage engine باید انجام بدهد (مثلا دستور insertOne من را بعدا روی دیسک نیز ذخیره کند تا فقط در مموری نماند). البته من توضیح دادم که دستور write و قبول شدن آن برای ذخیره سازی داده ها در دیسک کافی است بنابراین دیگر چه نیازی به این فایل است؟ فرض کنید در حین انجام عملیات (مثلا insertMany) سرور ما از کار بیفتد (در چنین حالتی ممکن است مموری کاملا پاک شود). دستوراتی که در مموری بودند و هنوز به دیسک منتقل نشده اند چه خواهند شد؟ فایل journal این کار ها را ذخیره می کند تا پس از راه اندازی و بالا آمدنِ دوباره سرور، کوئری ادامه پیدا کند.
سوال: چرا داده ها را مستقیما وارد پایگاه داده نمی کنیم؟
پاسخ: ثبت داده ها در پایگاه داده کمی پیچیده و زمان بر است (شما باید محل دقیق ثبت هر سند را پیدا کنید، یا مثلا اگر index داشته باشیم - بعدا در مورد آن ها صحبت می کنیم - باید آن ها را نیز به روز رسانی کنیم و الی آخر) بنابراین اضافه کردن آن ها به journal که یک خط ساده از داده است، سریع تر می باشد. البته در حالت پیش فرض از journal استفاده نمی شود (j: undefined) که سریع ترین حالت است.
اگر می خواهید journal را فعال کنید باید گزینه آن را به شکل J: true قرار بدهیم. یعنی فقط زمانی به من گزارش موفقیت آمیز بودن کوئری را بده که هم سرورِ MongoDB آن را تایید کرده باشد (w: 1) و هم در journal ذخیره شده باشد (j: true). البته می توانیم آن را به شکل زیر بنویسیم:
{w:1, wtimeout: 200, j: true}
خصوصیت wtimeout یعنی چقدر به سرور زمان می دهیم که گزارش موفقیت آمیز بودن را ارسال کند. اگر از این زمان بیشتر شود، کل دستور لغو خواهد شد. در مورد استفاده از این دستور باید مراقب باشید چرا که اگر زمان بسیار کمی را تعیین کنید دستورات سالم خود را کنسل خواهید کرد. پیشنهاد من این است که اگر هدف خاصی ندارید، از این گزینه استفاده نکنید. همچنین یادتان باشد که اگر journal را فعال کنید، دستورات write شما کمی بیشتر زمان می برند و فعال کردن یا نکردن آن بستگی به سلیقه شما و نظرتان دارد.
db.persons.insertOne({name: "Nastaran", age: 30}, {writeConcern: {w: 0}})
در مثال بالا مقدار w را روی صفر گذاشته ام که یعنی نیازی به تایید سرور ندارم بنابراین نتیجه زیر را می گیرید:
{ "acknowledged" : false }
توجه کنید که سند جدید ما به درستی اضافه شده است (می توانید با find.pretty تست کنید) اما id آن را دریافت نمی کنیم و سرور نتیجه کوئری را تایید یا acknowledge نمی کند (مشکلی نیست). چرا آیدی نمی گیریم؟ به دلیل اینکه w:0 یعنی نیازی به تایید سرور نیست و دستور خود را مستقیما ارسال می کنیم و دیگر منتظر پاسخ و تایید سرور برای نتیجه نمی مانیم. در حالت عادی w:1 است، یعنی سرور باید نتیجه کوئری را تایید کند تا نتیجه به ما برگردانده شود بنابراین id را خواهیم داشت اما حالا منتظر بررسی سرور نیستیم تا id را به ما بدهد. به عبارت دیگر storage engine فرصت نداشت که آن را در مموری ثبت کرده و id را پس بدهد (id در نهایت ثبت شده است و می توانید آن را با find.pretty مشاهده کنید، فقط ما آن را در لحظه دریافت نمی کنیم). طبیعتا در چنین حالتی نمی فهمیم که عملیات موفقیت آمیز بوده است یا خیر، مگر اینکه دستی با find آن را چک کنیم. این عملیات بسیار سریع است چرا که نیازی به صبر کردن برای سرور نیست. در برنامه هایی که هر ثانیه باید چیزی را log کنید و مهم نیست اگر یک یا دو ثانیه ثبت نشوند، می توان از این روش استفاده کرد.
برای فعال کردن journal هم می توانستیم به شکل زیر عمل کنیم:
db.persons.insertOne({name: "Nastaran", age: 30}, {writeConcern: {w: 0, j: true}})
همچنین برای wtimeout:
db.persons.insertOne({name: "Nastaran", age: 30}, {writeConcern: {w: 0, j: true, wtimeout: 200}})
یادتان باشد که writeConcern برای تمام دستورات write مانند update ها و insert ها است.
در این قسمت، به پرسشهای تخصصی شما دربارهی محتوای مقاله پاسخ داده نمیشود. سوالات خود را اینجا بپرسید.