به آخرین قسمت از فصل ایندکس ها در MongoDB خوش آمدید! ما در قسمت قبل در رابطه با دو روش اصلی برای ساخت ایندکس ها (فارغ از نوع ایندکس) صحبت کردیم:
سپس برای آماده سازی پایگاه داده اسکریپت زیر را در MongoDB اجرا کردیم:
conn = new Mongo(); db = conn.getDB("credit"); for (let i = 0; i < 1000000; i++) { db.ratings.insertOne({ "person_id": i + 1, "score": Math.random() * 100, "age": Math.floor(Math.random() * 70) + 18 }) }
با این کار یک میلیون سند در کالکشن خود خواهیم داشت که ساختار زیر را دارند:
"_id" : ObjectId("5ebfe1ee15d4667bc590befc"), "person_id" : 1, "score" : 9.568823573984254, "age" : 72
حالا می توانیم شروع به اضافه کردن ایندکس های متخلف کنیم. من در قدم اول به صورت عادی و همیشگی یک single field index را تعریف می کنم:
db.ratings.createIndex({age: 1})
اجرای این کوئری حدود 5 ثانیه طول می کشد (بسته به سیستم شما ممکن است بیشتر یا کمتر شود) و در حین اجرا نیز نمی توانید هیچ دستور دیگری را اجرا کنید. البته 5 ثانیه کم است اما به صورت لحظه ای نیست و زمان نیاز دارد. برای استفاده از این ایندکس نیز می توانیم یک کوئری ساده find را داشته باشیم:
db.ratings.explain("executionStats").find({age: {$gt: 80}})
یعنی تمام افرادی را پیدا کن که بالای 80 سال دارند. البته از آنجایی که از explain استفاده کرده ایم، گزارشی نیز برایمان برگردانده می شود. این گزارش می گوید که حدود 100 هزار سند بررسی شده اند تا این فرد پیدا شود:
"keysExamined" : 100034
و طبیعتا از IXSCAN یا index scan استفاده شده است بنابراین تعداد اسناد برگردانده شده نیز دقیقا به تعداد افراد بررسی شده است:
"nReturned" : 100034, "executionTimeMillis" : 366
این نتیجه را به خاطر داشته باشید تا بتوانیم بعدا آن را مقایسه کنیم. حالا ایندکس خودمان را حذف می کنیم:
db.ratings.dropIndex({age: 1})
سپس کوئری قبلی را دوباره اجرا می کنیم تا ببینیم بدون ایندکس چه اتفاقی می افتد:
db.ratings.explain("executionStats").find({age: {$gt: 80}})
گزارش کوئری بالا نشان می دهد که برای اجرای آن به جای 100 هزار سند، تمام 1 میلیون سند بررسی شده اند (که طبیعی است چرا که ما ایندکس نداریم) و زمان اجرای آن نیز تقریبا 2 برابر شده است:
"executionSuccess" : true, "nReturned" : 100034, "executionTimeMillis" : 605, "totalKeysExamined" : 0, "totalDocsExamined" : 1000000,
بنابراین تا اینجا همه چیز عادی است و طبق انتظار ما پیش رفته است. در قدم اول باید ثابت کنیم که در هنگام ساخت ایندکس ها به صورت مستقیم، کل کالکشن قفل می شود. برای این کار می توانید دو پنجره ترمینال را باز کنید و در اولین پنجره کوئری زیر را آماده داشته باشید اما آن را اجرا نکنید:
db.ratings.findOne()
سپس در پنجره دوم کوئری زیر را اجرا کنید تا ایندکس ساخته شود اما سریع و قبل از آنکه عملیات تمام شود به پنجره اول برگشته و کوئری آماده شده در آن را اجرا کنید.
db.ratings.createIndex({age: 1})
با این کار می بینید که دستور findOne هیچ نتیجه ای را برنمی گرداند تا 5 ثانیه تمام شود (ایندکس ها ساخته شوند) سپس مقدار مورد نظرتان را به شما می دهد. چرا؟ به دلیل اینکه کالکشن قفل شده است. بنابراین خطایی دریافت نمی کنید اما تمامی عملیات های مختلف آن کالکشن (چه از نوع read مانند find و چه از نوع Write مانند insert) به تعویق انداخته می شوند.
یادتان باشد که این یک ایندکس ساده از single field index بود و اگر بخواهیم از multi-key index ها روی داده های یک سایت استفاده کنیم باید زمان بسیار بیشتر را منتظر تکمیل کوئری باشیم. برای جلوگیری از چنین مشکلی می توانیم فرآیند ساخت ایندکس را در پس زمینه یا background اجرا کنیم تا کالکشن قفل نشود (هنوز این کوئری را اجرا نکنید):
db.ratings.createIndex({age: 1}, {background: true})
خصوصیت background به طور پیش فرض روی false قرار دارد اما اگر آن را به شکل بالا روی true بگذاریم، فرآیند ساخت ایندکس ها به پس زمینه منتقل می شود و جلوی اجرای دیگر کوئری های ما را نمی گیرد. برای تست کردن این موضوع می توانیم همان تست قبلی را دوباره تکرار کنیم. دو ترمینال مختلف را باز کنید و در یکی از آن ها کوئری زیر را بنویسید اما اجرا نکنید:
db.ratings.findOne()
سپس در دومین ترمینال کوئری زیر را قرار بدهید اما اجرا نکنید:
db.ratings.createIndex({age: 1}, {background: true})
حالا به محض اجرای کوئری در ترمینال دوم، سریعا به ترمینال اول رفته و کوئری Find را اجرا کنید. به محض اجرای find، نتیجه به شما برگردانده می شود و اصلا نیازی نیست که برای تکمیل آن صبر کنیم.
نکات جمع بندی این فصل:
در این قسمت، به پرسشهای تخصصی شما دربارهی محتوای مقاله پاسخ داده نمیشود. سوالات خود را اینجا بپرسید.