ایندکسها یکی از مهمترین ابزارهای بهینهسازی در MongoDB هستند. بدون ایندکس، بسیاری از کوئریها مجبور میشوند کل کالکشن را اسکن کنند که به آن Collection Scan (COLLSCAN) گفته میشود و در دیتابیسهای بزرگ میتواند بسیار کند باشد.
در این مقاله مراحل ساخت ایندکس، مشاهده ایندکسها، بررسی استفاده از آنها و همچنین مشکلات رایج هنگام ساخت همزمان ایندکسها بررسی میشود.
ساخت ایندکس در MongoDB
فرض کنید کالکشنی به نام sale_request دارید و میخواهید روی فیلد uuid ایندکس بسازید.
دستور ساخت ایندکس:
db.sale_request.createIndex({ uuid: 1 })
توضیح:
- createIndex دستور ساخت ایندکس است.
- { uuid: 1 } مشخص میکند که ایندکس روی فیلد uuid ساخته شود.
- عدد 1 یعنی ایندکس صعودی (Ascending).
- مقدار -1 برای نزولی (Descending) استفاده میشود.
ساخت ایندکس در حالت Background
در دیتابیسهای بزرگ ساخت ایندکس ممکن است زمانبر باشد. برای جلوگیری از قفل شدن عملیات دیتابیس میتوان ایندکس را در پسزمینه ساخت.
db.sale_request.createIndex(
{ uuid: 1 },
{ background: true }
)
توضیح:
- background: true باعث میشود ساخت ایندکس بدون بلاک کردن عملیات دیگر انجام شود.
- در نسخههای جدید MongoDB این رفتار بهصورت پیشفرض بهینه شده است، اما این گزینه همچنان در مشخصات ایندکس ذخیره میشود.
ساخت ایندکس یکتا (Unique Index)
اگر مقدار uuid برای هر رکورد یکتا است، بهتر است ایندکس را unique بسازید:
db.sale_request.createIndex(
{ uuid: 1 },
{ unique: true }
)
مزایا:
- جلوگیری از درج داده تکراری
- بهینهتر شدن query planner
- افزایش صحت دادهها
مشاهده ایندکسهای موجود روی کالکشن
برای مشاهده تمام ایندکسهای یک کالکشن:
db.sale_request.getIndexes()
نمونه خروجی:
[
{ v: 2, key: { _id: 1 }, name: "_id_" },
{ v: 2, key: { traceId: 1 }, name: "traceId_1" },
{ v: 2, key: { username: 1 }, name: "idx_username" },
{ v: 2, key: { uuid: 1 }, name: "uuid_1", background: true }
]
توضیح فیلدها:
key: فیلد یا فیلدهای ایندکسname: نام ایندکسv: نسخه ساختار ایندکسbackground: مشخص میکند ایندکس به صورت background ساخته شده است
وجود background: true به معنی در حال اجرا بودن ایندکس نیست، بلکه فقط مشخصات ساخت آن را نشان میدهد.
بررسی حجم ایندکسها
برای دیدن حجم هر ایندکس:
db.sale_request.stats().indexSizes
نمونه خروجی:
{
_id_: 21811200,
traceId_1: 16089088,
idx_username: 9773056,
uuid_1: 29663232
}
توضیح:
- اعداد بر حسب بایت هستند.
- این مقدار نشان میدهد هر ایندکس چه مقدار فضا اشغال کرده است.
اگر ایندکس در این خروجی دیده شود، یعنی ساخت آن کامل شده و قابل استفاده است.
بررسی استفاده از ایندکس در کوئریها
برای بررسی اینکه آیا یک کوئری از ایندکس استفاده میکند:
db.sale_request
.find({ uuid: "some-uuid" })
.explain("executionStats")
در خروجی باید به این بخش توجه کنید:
stage: IXSCAN
اگر IXSCAN باشد یعنی از ایندکس استفاده شده است.
اگر مشاهده شود:
stage: COLLSCAN
یعنی کل کالکشن اسکن شده و ایندکس استفاده نشده است.
همچنین این دو مقدار مهم هستند:
totalDocsExamined
totalKeysExamined
در حالت بهینه معمولاً مقدار اینها بسیار کم است.
مشاهده آمار استفاده از ایندکسها
برای دیدن اینکه هر ایندکس چند بار استفاده شده:
db.sale_request.aggregate([
{ $indexStats: {} }
])
نمونه خروجی:
{
name: "uuid_1",
accesses: {
ops: 120
}
}
توضیح:
opsتعداد دفعاتی است که ایندکس در کوئریها استفاده شده است.
بررسی عملیاتهای در حال اجرای MongoDB
اگر بخواهید ببینید آیا ایندکسی در حال ساخته شدن است:
db.currentOp()
یا به صورت دقیقتر:
db.currentOp({
"command.createIndexes": { $exists: true }
})
اگر خروجی خالی باشد یعنی هیچ ایندکسی در حال ساخته شدن نیست.
مشکل رایج: گیر کردن ساخت ایندکسها
در برخی موارد چندین دستور createIndex به صورت همزمان اجرا میشوند. اگر سیستم منابع محدودی داشته باشد، ممکن است این عملیاتها گیر کنند.
در چنین شرایطی ممکن است MongoDB خطایی مشابه این بدهد:
Too many index builds running
یا اجازه ساخت ایندکس جدید ندهد.
دلیل این موضوع این است که MongoDB برای جلوگیری از مصرف بیش از حد منابع، تعداد index build های همزمان را محدود میکند.
محدودیت تعداد ایندکسهایی که همزمان ساخته میشوند
در MongoDB معمولاً تعداد محدودی ایندکس میتواند به صورت همزمان ساخته شود. این مقدار به نسخه MongoDB و تنظیمات سرور بستگی دارد.
اگر چندین عملیات ساخت ایندکس همزمان اجرا شوند:
- برخی از آنها در صف قرار میگیرند
- برخی ممکن است خطا بدهند
- گاهی عملیاتها بسیار کند میشوند
بررسی ایندکسهایی که در حال ساخت هستند
برای دیدن اینکه چه ایندکسهایی در حال ساخت هستند:
db.currentOp({
"command.createIndexes": { $exists: true }
})
در خروجی اطلاعاتی مانند:
- namespace
- collection
- progress
- duration
نمایش داده میشود.
متوقف کردن یک عملیات ساخت ایندکس
اگر یک عملیات مشکلدار باشد میتوان آن را متوقف کرد.
ابتدا شناسه عملیات را پیدا کنید:
db.currentOp()
سپس آن را متوقف کنید:
db.killOp(opid)
حذف یک ایندکس
برای حذف ایندکس:
db.sale_request.dropIndex("uuid_1")
یا حذف همه ایندکسها (به جز _id):
db.sale_request.dropIndexes()
نکات مهم در طراحی ایندکس
۱. روی فیلدهایی ایندکس بسازید که در query یا filter زیاد استفاده میشوند.
۲. روی فیلدهایی که selectivity بالا دارند ایندکس بهتر عمل میکند.
۳. از ساخت ایندکسهای زیاد خودداری کنید، زیرا:
- مصرف RAM را افزایش میدهد
- سرعت write را کاهش میدهد
۴. برای کوئریهای ترکیبی بهتر است از Compound Index استفاده شود.
مثال:
db.sale_request.createIndex({
uuid: 1,
status: 1
})
ایندکسها نقش حیاتی در بهبود عملکرد MongoDB دارند. با استفاده از دستوراتی مانند:
createIndexgetIndexesexplainindexStatscurrentOp
میتوان هم ایندکسها را مدیریت کرد و هم عملکرد آنها را مانیتور کرد.
همچنین باید توجه داشت که اجرای همزمان چندین عملیات ساخت ایندکس میتواند باعث مصرف زیاد منابع یا محدودیت در ایجاد ایندکسهای جدید شود. بنابراین در سیستمهای تولیدی بهتر است ساخت ایندکسها با برنامهریزی و به صورت کنترلشده انجام شود.