دو پروتکل اصلی برای ساخت ایندکس‌ها - پیاده‌سازی

Two Main Protocols for Building Indexes

25 اردیبهشت 1401
درسنامه درس 62 از سری دوره جامع آموزش MongoDB
MongoDB: دو پروتکل اصلی برای ساخت ایندکس ها - پیاده سازی (قسمت 64)

به آخرین قسمت از فصل ایندکس ها در MongoDB خوش آمدید! ما در قسمت قبل در رابطه با دو روش اصلی برای ساخت ایندکس ها (فارغ از نوع ایندکس) صحبت کردیم:

  • تعریف ایندکس در foreground (پیش زمینه) - به صورت مستقیم. در این حالت کالکشن قفل شده و قابل دسترسی نیست اما سریع تر است.
  • تعریف ایندکس در background (پس زمینه) - به صورت غیر مستقیم. در این حالت کالکشن قابل دسترسی است اما زمان طولانی تری برای اجرا می خواهد. معمولا از این روش در وب سایت های live (در حال استفاده توسط عموم) استفاده می شود.

سپس برای آماده سازی پایگاه داده اسکریپت زیر را در 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، نتیجه به شما برگردانده می شود و اصلا نیازی نیست که برای تکمیل آن صبر کنیم.

نکات جمع بندی این فصل:

  • ایندکس ها (در صورتی که به درستی استفاده شوند) باعث بالا رفتن سرعت کوئری هایتان می شوند چرا که MongoDB فقط به بخشی از document ها نگاه می کند و لازم نیست تک تک آن ها را بررسی کند.
  • ایندکس ها انواع مختلفی دارند که مهم ترین آن ها را در همین فصل بررسی کردیم: single-field و compound و multi-key و text. البته انواع دیگری مانند geospatial نیز داریم که بعدا در مورد آن صحبت خواهیم کرد.
  • ایندکس ها باعث پایین تر رفتن سرعت کوئری های Write مانند insert می شوند بنابراین باید حد تناسبی بین استفاده از آن ها پیدا کنید و گرنه بیشتر از سود به پایگاه داده ضرر می زنید.
  • برای بررسی نحوه اجرای یک کوئری از متد explain استفاده کنید.
  • ایندکس ها می توانند به صورت خودکار منقضی شوند (Time-To-Live) و همینطور می توانند یکتا (unique) یا partial (جزئی) باشند.
  • text index ها می توانند weight و default_language خودشان را بگیرند.
تمام فصل‌های سری ترتیبی که روکسو برای مطالعه‌ی دروس سری دوره جامع آموزش MongoDB توصیه می‌کند:
نویسنده شوید
دیدگاه‌های شما

در این قسمت، به پرسش‌های تخصصی شما درباره‌ی محتوای مقاله پاسخ داده نمی‌شود. سوالات خود را اینجا بپرسید.

مقالات مرتبط
آخرین سوالات کاربران
5451218 در 2 سال قبل پرسیده:
ما را دنبال کنید
اینستاگرام روکسو تلگرام روکسو ایمیل و خبرنامه روکسو