در این جلسه می خواهیم در رابطه با یک محدودیت در index ها صحبت کنیم تا با ابعاد جدید آن ها آشنا شوید. اگر یادتان باشد در جلسه قبل به دنبال افرادی گشتیم که سن آن ها بیشتر از 60 سال باشد:
db.contacts.explain("executionStats").find({"dob.age": {$gt: 60}})
اما اگر به دنبال افراد بالای 20 سال بگردیم چطور؟
db.contacts.explain("executionStats").find({"dob.age": {$gt: 20}})
گزارشی که از کوئری بالا دریافت می شود نکات جالبی را برای ما دارد. مثلا "totalKeysExamined" روی 5000 است بنابراین تمام سند ها برگردانده شده اند و هیچ فردی جوان تر از 20 سال در داده های ما نیست. همچنین "executionTimeMillis" نیز برای من روی 13 میلی ثانیه است که نشان می دهد این کوئری نسبت به کوئری برای جست و جوی افراد بالای 60 سال زمان بیشتری برده است (تقریبا 2 برابر). نکته جالب تر اینجاست که اگر index را از فیلد age حذف کنیم، با نتایج غیر منتظره ای روبرو می شویم. برای حذف ایندکس ها می توانید از دستور dropIndex استفاده کرده و همان آرگومانی را به آن پاس می دهیم که در هنگام ساخت ایندکس پاس داده بودیم:
db.contacts.dropIndex({"dob.age": 1})
اجرای کوئری بالا نتیجه زیر را به ما می دهد:
{ "nIndexesWas" : 2, "ok" : 1 }
این یعنی index ما با موفقیت حذف شده است. حالا می توانیم دوباره کوئری قبلی خود را اجرا کنیم:
db.contacts.explain("executionStats").find({"dob.age": {$gt: 20}})
گزارشی که از کوئری بالا دریافت می کنیم واقعا عجیب است! مقدار "executionTimeMillis" روی 4 است که یعنی اجرای این کوئری 4 میلی ثانیه زمان برده است. چطور ممکن است که کوئری ما بدون ایندکس سه برابر سریع تر شده باشد!؟ دلیل پر سرعت تر بودن این کوئری، این است که دیگر نیازی نیست به index رفته و آن را بررسی کنیم و این مرحله را کاملا حذف کرده ایم. نکته اصلی اینجاست که اگر یک کوئری داشته باشیم که قسمت بسیار بزرگ یا بیشتر کالکشن شما را برگرداند، index ها باعث کُند شدن آن کوئری خواهند شد. چرا؟ اگر قرار باشد همه اسناد را برگردانیم، چرا باید به لیست index ها رفته و آدرس تمام 5 هزار سند را بگیریم تا دوباره به 5 هزار سند برویم و همه آن ها را برگردانیم؟ این یک مرحله اضافی است.
به عنوان قانونی کلی به یاد داشته باشید که اگر کوئری های شما فقط قسمتی کوچکی از داده ها را برمی گردانند (مثلا 10 یا 20 درصد کالکشن) index ها به شما کمک خواهند کرد و سرعت اجرای کوئری هایتان به شدت بالا می رود اما اگر کوئری های شما قسمت اعظم داده ها را برمی گردانند (مثلا حدود 70 یا 80 درصد کالکشن) بهتر است برایشان از index استفاده نکنید.
تا اینجا ما برای age یک index ساخته بودیم که در این جلسه آن را حذف کرده بودیم. احتمالا شما هم حدس زده اید که ساختن ایندکس برای فیلد های رشته ای نیز ممکن است و حتما نیازی نیست فیلد ما مانند age عددی باشد. از نظر منطقی هم ایندکس برای فیلد های رشته ای کار درستی است چرا که ما به صورت مکرر رشته ها را جست و جو می کنیم. البته برای فیلد های Boolean استفاده از ایندکس خیلی عاقلانه نیست چرا که دو مقدار true یا false داریم و استفاده از index ها آنقدر سرعت برنامه را بالا نمی برد (گرچه که استفاده از ایندکس برای Boolean ها غلط نیست).
مثلا برای این قسمت یک index را برای فیلد gender (به معنی جنسیت) تعریف می کنم:
db.contacts.createIndex({gender: 1})
احتمالا با خودتان می گویید مگر نگفتی برای مقادیر Boolean استفاده از ایندکس خیلی به صرفه نیست چرا که فقط دو مقدار داریم؟ مگر gender نیز غیر این است؟ حرفتان درست است و gender یا male (مذکر) یا female (مونث) است. به طور مثال اگر کوئری زیر را اجرا کنیم، تقریبا نصب کالکشن را دریافت می کنیم چرا که نصف داده های ما مرد هستند:
db.contacts.explain("executionStats").find({gender: "male"})
گزارش دریافتی از کوئری بالا برای من به شکل زیر است:
"queryPlanner" : { "plannerVersion" : 1, "namespace" : "contactData.contacts", "indexFilterSet" : false, "parsedQuery" : { "gender" : { "$eq" : "male" } }, "winningPlan" : { "stage" : "FETCH", "inputStage" : { "stage" : "IXSCAN", "keyPattern" : { "gender" : 1 }, "indexName" : "gender_1", "isMultiKey" : false, "multiKeyPaths" : { "gender" : [ ] }, "isUnique" : false, "isSparse" : false, "isPartial" : false, "indexVersion" : 2, "direction" : "forward", "indexBounds" : { "gender" : [ "[\"male\", \"male\"]" ] } } }, "rejectedPlans" : [ ] }, "executionStats" : { "executionSuccess" : true, "nReturned" : 2435, "executionTimeMillis" : 5, "totalKeysExamined" : 2435, "totalDocsExamined" : 2435, "executionStages" : { "stage" : "FETCH", "nReturned" : 2435, "executionTimeMillisEstimate" : 0, "works" : 2436, "advanced" : 2435, "needTime" : 0, "needYield" : 0, "saveState" : 19, "restoreState" : 19, "isEOF" : 1, "docsExamined" : 2435, "alreadyHasObj" : 0, "inputStage" : { "stage" : "IXSCAN", "nReturned" : 2435, "executionTimeMillisEstimate" : 0, "works" : 2436, "advanced" : 2435, "needTime" : 0, "needYield" : 0, "saveState" : 19, "restoreState" : 19, "isEOF" : 1, "keyPattern" : { "gender" : 1 }, "indexName" : "gender_1", "isMultiKey" : false, "multiKeyPaths" : { "gender" : [ ] }, "isUnique" : false, "isSparse" : false, "isPartial" : false, "indexVersion" : 2, "direction" : "forward", "indexBounds" : { "gender" : [ "[\"male\", \"male\"]" ] }, "keysExamined" : 2435, "seeks" : 1, "dupsTested" : 0, "dupsDropped" : 0 } } }, "serverInfo" : { "host" : "Amir-PC", "port" : 27017, "version" : "4.2.5", "gitVersion" : "2261279b51ea13df08ae708ff278f0679c59dc32" }, "ok" : 1
یعنی 2435 نفر مذکر یا male هستند و برگردانده شده اند. از طرفی همانطور که گفتیم gender فقط می تواند دو مقدار بگیرد، تعریف این ایندکس آنقدر ها هم صحیح نیست و بیشتر باعث کند شدن بقیه کوئری های ما خواهد شد تا اینکه بخواهد سرعت کلی برنامه را بالا ببرد. بنابراین بهتر است index ای که تازه ساختیم را حذف کنیم:
db.contacts.dropIndex({gender: 1})
اما نوعی دیگر از index ها را داریم که به compound index یا ایندکس های ترکیبی یا ایندکس های چند وجهی معروف هستند. در جلسه بعد آن ها را بررسی می کنیم.
در این قسمت، به پرسشهای تخصصی شما دربارهی محتوای مقاله پاسخ داده نمیشود. سوالات خود را اینجا بپرسید.