16-bo‘lim
explain bilan so'rovni tahlil qilish
explain rejimlari, reja daraxtini o'qish, g'olib va rad etilgan rejalar, qoplovchi so'rov, hint va reja keshi.
Ushbu bo‘lim mundarijasi
So'rov sekin. Taxmin qilish o'rniga bazadan so'rash kerak: u so'rovni qanday bajarganini aniq aytib beradi.
const filiallar = ["Namangan", "Toshkent", "Samarqand"]
const hujjatlar = []
for (let i = 1; i <= 20000; i++) {
hujjatlar.push({
_id: i,
mijoz_id: 1 + (i % 2000),
filial: filiallar[i % 3],
holat: (i % 10 === 0) ? "bekor" : "yakunlandi",
summa: 10000 + (i % 500) * 100,
sana: new Date(Date.UTC(2026, 0, 1 + (i % 90)))
})
}
db.buyurtma.insertMany(hujjatlar)
Uchta rejim #
| Rejim | So'rovni bajaradimi | Nima beradi |
|---|---|---|
"queryPlanner" | Yo'q | Faqat tanlangan reja (odatiy) |
"executionStats" | Ha | Reja + haqiqiy sonlar |
"allPlansExecution" | Ha | Yuqoridagi + sinalgan barcha rejalar |
db.buyurtma.createIndex({ filial: 1 })
const e = db.buyurtma.find({ filial: "Namangan" }).explain()
Object.keys(e)
e.queryPlanner.winningPlan.stage
filial_1
[
'explainVersion',
'queryPlanner',
'queryShapeHash',
'command',
'serverInfo',
'serverParameters',
'ok',
'$clusterTime',
'operationTime'
]
FETCH
executionStats bo'limi yo'q - chunki odatiy rejimda so'rov
bajarilmaydi.
Reja daraxtini o'qish #
Reja - ichma-ich bosqichlar. Uni ichkaridan tashqariga o'qish kerak:
function bosqichlar(reja) {
const r = []
let n = reja
while (n) { r.push(n.stage); n = n.inputStage }
return r
}
db.buyurtma.createIndex({ filial: 1 })
const k = db.buyurtma.find({ filial: "Namangan" }).sort({ summa: 1 })
const e = k.explain()
bosqichlar(e.queryPlanner.winningPlan)
[Function: bosqichlar]
filial_1
[ 'SORT', 'FETCH', 'IXSCAN' ]
O'qish tartibi - oxirgisidan birinchisiga:
| Bosqich | Kim bajaradi |
|---|---|
IXSCAN | Indeksdan mos kalitlarni topadi |
FETCH | Kalitlar bo'yicha hujjatlarni o'qiydi |
SORT | Natijani xotirada saralaydi |
G'olib va rad etilgan rejalar #
Bir nechta indeks mos kelsa, MongoDB ularning hammasini qisqa vaqt sinab ko'radi va eng yaxshisini tanlaydi:
db.buyurtma.createIndex({ filial: 1 })
db.buyurtma.createIndex({ holat: 1 })
const e = db.buyurtma.find({ filial: "Namangan", holat: "bekor" }).explain()
e.queryPlanner.winningPlan.inputStage.indexName
e.queryPlanner.rejectedPlans.map(p => p.inputStage.indexName)
filial_1
holat_1
holat_1
[ 'filial_1' ]
holat_1 yutdi. Sabab: bekor holatida atigi 2 000 hujjat
bor, Namangan filialida esa 6 666 ta. Kamroq kalit -
kamroq ish.
MongoDB narxni hisoblamaydi. U haqiqatan poyga o'tkazadi:
- Mos keladigan har bir indeks uchun reja tuziladi.
- Har bir reja bir oz muddat haqiqatan bajariladi.
- Birinchi bo'lib yetarli natija topgani g'olib bo'ladi.
- G'olib reja keshga yoziladi.
Shuning uchun explain() chiqishida rejectedPlans bor -
bular sinovda yutqazgan rejalar.
Kesh so'rov shakli bo'yicha saqlanadi. Ya'ni
{ filial: "Namangan" } va { filial: "Toshkent" } -
bitta shakl, bitta kesh yozuvi.
Bu ba'zan muammo tug'diradi: kesh bir xil qiymat uchun tanlangan reja boshqa qiymat uchun yomon bo'lishi mumkin. MongoDB buni sezadi va reja yomonlashsa keshni qayta baholaydi.
executionStats - haqiqiy sonlar #
db.buyurtma.createIndex({ mijoz_id: 1 })
const e = db.buyurtma.find({ mijoz_id: 42 }).explain("executionStats")
const s = e.executionStats
s.nReturned
s.totalKeysExamined
s.totalDocsExamined
s.executionStages.stage
mijoz_id_1
10
10
10
FETCH
Uchta sonning nisbati so'rov sifatini ko'rsatadi:
| Nisbat | Ma'nosi |
|---|---|
keys == docs == returned | Ideal |
keys > returned | Indeks ortiqcha kalit ko'rmoqda |
docs > returned | Filtr hujjat o'qilgandan keyin qo'llanmoqda |
docs = 0 | Qoplovchi so'rov - hujjat umuman o'qilmadi |
Qoplovchi so'rov #
Agar so'rov uchun kerak bo'lgan hamma maydon indeksda bo'lsa, MongoDB hujjatlarni umuman o'qimaydi:
db.buyurtma.createIndex({ filial: 1, summa: 1 })
const q = db.buyurtma.find(
{ filial: "Namangan" },
{ _id: 0, filial: 1, summa: 1 }
).explain("executionStats")
q.queryPlanner.winningPlan.stage
q.executionStats.totalKeysExamined
q.executionStats.totalDocsExamined
filial_1_summa_1
PROJECTION_COVERED
6666
0
totalDocsExamined - nol. 6 666 ta hujjat qaytdi,
lekin ularning hech biri diskdan o'qilmadi.
Solishtiring - proyeksiyasiz xuddi shu so'rov:
db.buyurtma.createIndex({ filial: 1, summa: 1 })
const q = db.buyurtma.find({ filial: "Namangan" }).explain("executionStats")
q.queryPlanner.winningPlan.stage
q.executionStats.totalDocsExamined
filial_1_summa_1
FETCH
6666
Uning sharti uchta:
- Filtr maydonlari indeksda bo'lsin.
- Qaytariladigan maydonlar ham o'sha indeksda bo'lsin.
_idochiq ravishda o'chirilgan bo'lsin (_id: 0)- agar u indeksda bo'lmasa.
Uchinchi shart eng ko'p unutiladi. _id odatiy holda
qaytadi, u esa indeksda yo'q - shuning uchun baza hujjatni
o'qishga majbur bo'ladi va qoplash buziladi.
Bu naqsh ro'yxat ekranlari uchun juda qulay: jadvalda 3-4 ta ustun ko'rsatiladi, ularning hammasini bitta indeksga kiritsangiz, sahifa diskga umuman tegmasdan chiziladi.
Narxi: indeks kattalashadi. Shuning uchun uni faqat haqiqatan tez-tez ochiladigan ekranlar uchun qiling.
hint - indeksni majburlash #
db.buyurtma.createIndex({ filial: 1 })
db.buyurtma.createIndex({ holat: 1 })
const kursor = db.buyurtma.find({ filial: "Namangan", holat: "bekor" })
const e = kursor.hint({ filial: 1 }).explain("executionStats")
e.queryPlanner.winningPlan.inputStage.indexName
e.executionStats.totalKeysExamined
e.executionStats.nReturned
filial_1
holat_1
filial_1
6666
666
hint bilan biz ataylab yomonroq indeksni tanladik:
6 666 kalit ko'rildi, 666 ta hujjat qaytdi.
hint - oxirgi chorahint rejalashtiruvchini butunlay chetlab o'tadi. Bu
ba'zan kerak, lekin u muzlatilgan qaror:
| Vaqt | Nima bo'ladi |
|---|---|
| Bugun | hint to'g'ri indeksni tanlaydi |
| Olti oydan keyin | Ma'lumot taqsimoti o'zgaradi |
| Bir yildan keyin | Yaxshiroq indeks qo'shiladi - lekin hint uni ko'rmaydi |
Rejalashtiruvchi o'zgarishlarga moslashadi, hint esa
yo'q.
hint ni faqat ikki holatda ishlating:
- Sinov - "bu indeks bilan qanday bo'lardi?" degan savolga javob olish uchun.
- Aniqlangan muammo - rejalashtiruvchi izchil noto'g'ri tanlayotgani isbotlangan bo'lsa, vaqtinchalik yechim sifatida.
Ikkinchi holatda ham asosiy vazifa - nima uchun noto'g'ri tanlayotganini topish. Odatda sabab indeksning o'zida bo'ladi.
Agregatsiyani tahlil qilish #
db.buyurtma.createIndex({ filial: 1 })
const e = db.buyurtma.explain("executionStats").aggregate([
{ $match: { filial: "Namangan" } },
{ $group: { _id: "$holat", soni: { $sum: 1 } } }
])
e.stages ? e.stages.length : "bitta bosqichga siqildi"
e.executionStats.totalDocsExamined
filial_1
bitta bosqichga siqildi
6666
Agregatsiyada $match va $sort ko'pincha birinchi
bosqichga birlashtiriladi - shuning uchun ular indeksdan
foydalana oladi.
Amaliy tartib #
| Qadam | Nima qilish |
|---|---|
| 1 | explain("executionStats") ni oling |
| 2 | nReturned va totalDocsExamined ni solishtiring |
| 3 | COLLSCAN bormi - tekshiring |
| 4 | SORT bormi - indeks tartibini ko'rib chiqing |
| 5 | Indeks qo'shing va qayta o'lchang |
Oxirgi qadamni tashlab ketmang: "indeks qo'shdim, endi tez bo'lishi kerak" degan taxmin ko'pincha noto'g'ri chiqadi.
explain()ni odatiy rejimda chaqiring.executionStatsbo'limi nega yo'qligini yozing.- Reja bosqichlarini ro'yxat qilib chiqaring.
SORTbosqichi nimani anglatishini tushuntiring.- Ikkita indeks qo'yib, qaysi biri yutganini toping.
rejectedPlansda nima borligini ko'ring.- Reja tanlash qanday ishlashini o'z so'zingiz bilan yozing.
- Qoplovchi so'rov yozing va
totalDocsExaminedni ko'ring. _id: 0ni olib tashlang - qoplash saqlandimi?hintnega doimiy yechim bo'la olmasligini yozing.
Xulosa #
explain()odatiy holda so'rovni bajarmaydi - faqat rejani ko'rsatadi.- Haqiqiy sonlar uchun
explain("executionStats")kerak. - Reja - ichma-ich bosqichlar;
inputStage- "meni oziqlantiradigan bosqich". COLLSCAN- butun to'plam o'qilyapti;SORT- xotirada saralanyapti.- MongoDB rejani hisoblamaydi, balki rejalar orasida poyga o'tkazadi.
- G'olib reja keshga so'rov shakli bo'yicha yoziladi.
nReturned,totalKeysExamined,totalDocsExamined- uchta asosiy son.totalDocsExamined: 0- qoplovchi so'rov, eng tez variant.- Qoplash uchun
_id: 0yozish ko'pincha unutiladi. hint- oxirgi chora: u rejalashtiruvchining moslashuv qobiliyatini o'ldiradi.
Keyingi bo'limda tranzaksiyalar va replika to'plamini ko'ramiz.
O‘qish tarixini saqlamoqchimisiz?
Tizimga kirsangiz, tugatgan bo‘limlaringiz saqlanadi va qoldirgan joyingizdan davom etasiz.
Xatolik topdingizmi?
Imlo xatosi, ishlamaydigan kod yoki noto‘g‘ri ma‘lumotni ko‘rsangiz - bizga xabar bering. Har bir xabar administrator tomonidan ko‘rib chiqiladi.