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.

🕑 13 daqiqa o‘qish 📄 854 so‘z 👁 0 marta ko‘rilgan
Ushbu bo‘lim mundarijasi
  1. Uchta rejim
  2. Reja daraxtini o'qish
  3. G'olib va rad etilgan rejalar
  4. executionStats - haqiqiy sonlar
  5. Qoplovchi so'rov
  6. hint - indeksni majburlash
  7. Agregatsiyani tahlil qilish
  8. Amaliy tartib
  9. Xulosa

So'rov sekin. Taxmin qilish o'rniga bazadan so'rash kerak: u so'rovni qanday bajarganini aniq aytib beradi.

JavaScript
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 #

RejimSo'rovni bajaradimiNima beradi
"queryPlanner"Yo'qFaqat tanlangan reja (odatiy)
"executionStats"HaReja + haqiqiy sonlar
"allPlansExecution"HaYuqoridagi + sinalgan barcha rejalar
JavaScript
db.buyurtma.createIndex({ filial: 1 })
const e = db.buyurtma.find({ filial: "Namangan" }).explain()
Object.keys(e)
e.queryPlanner.winningPlan.stage
Natija
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:

JavaScript
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)
Natija
[Function: bosqichlar]
filial_1


[ 'SORT', 'FETCH', 'IXSCAN' ]

O'qish tartibi - oxirgisidan birinchisiga:

BosqichKim bajaradi
IXSCANIndeksdan mos kalitlarni topadi
FETCHKalitlar bo'yicha hujjatlarni o'qiydi
SORTNatijani xotirada saralaydi
Reja pastdan yuqoriga bajariladi 3. SORT xotirada saralaydi 2. FETCH hujjatlarni o'qiydi 1. IXSCAN indeksdan kalit oladi Eng ichkaridagi bosqich birinchi ishlaydi inputStage - bu "meni oziqlantiradigan bosqich" degani Sekin joyni qidirganda eng ichkaridan boshlang
SORT bosqichining paydo bo'lishi - indeks tartibi so'rovga mos emasligining belgisi

G'olib va rad etilgan rejalar #

Bir nechta indeks mos kelsa, MongoDB ularning hammasini qisqa vaqt sinab ko'radi va eng yaxshisini tanlaydi:

JavaScript
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)
Natija
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.

Reja tanlash qanday ishlaydi

MongoDB narxni hisoblamaydi. U haqiqatan poyga o'tkazadi:

  1. Mos keladigan har bir indeks uchun reja tuziladi.
  2. Har bir reja bir oz muddat haqiqatan bajariladi.
  3. Birinchi bo'lib yetarli natija topgani g'olib bo'ladi.
  4. 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 #

JavaScript
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
Natija
mijoz_id_1


10
10
10
FETCH

Uchta sonning nisbati so'rov sifatini ko'rsatadi:

NisbatMa'nosi
keys == docs == returnedIdeal
keys > returnedIndeks ortiqcha kalit ko'rmoqda
docs > returnedFiltr hujjat o'qilgandan keyin qo'llanmoqda
docs = 0Qoplovchi 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:

JavaScript
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
Natija
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:

JavaScript
db.buyurtma.createIndex({ filial: 1, summa: 1 })
const q = db.buyurtma.find({ filial: "Namangan" }).explain("executionStats")
q.queryPlanner.winningPlan.stage
q.executionStats.totalDocsExamined
Natija
filial_1_summa_1

FETCH
6666
Qoplovchi so'rov - eng tez so'rov turi

Uning sharti uchta:

  1. Filtr maydonlari indeksda bo'lsin.
  2. Qaytariladigan maydonlar ham o'sha indeksda bo'lsin.
  3. _id ochiq 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 #

JavaScript
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
Natija
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 chora

hint rejalashtiruvchini butunlay chetlab o'tadi. Bu ba'zan kerak, lekin u muzlatilgan qaror:

VaqtNima bo'ladi
Bugunhint to'g'ri indeksni tanlaydi
Olti oydan keyinMa'lumot taqsimoti o'zgaradi
Bir yildan keyinYaxshiroq indeks qo'shiladi - lekin hint uni ko'rmaydi

Rejalashtiruvchi o'zgarishlarga moslashadi, hint esa yo'q.

hint ni faqat ikki holatda ishlating:

  1. Sinov - "bu indeks bilan qanday bo'lardi?" degan savolga javob olish uchun.
  2. 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 #

JavaScript
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
Natija
filial_1

bitta bosqichga siqildi
6666

Agregatsiyada $match va $sort ko'pincha birinchi bosqichga birlashtiriladi - shuning uchun ular indeksdan foydalana oladi.

Amaliy tartib #

QadamNima qilish
1explain("executionStats") ni oling
2nReturned va totalDocsExamined ni solishtiring
3COLLSCAN bormi - tekshiring
4SORT bormi - indeks tartibini ko'rib chiqing
5Indeks 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.

Amaliy topshiriq
  1. explain() ni odatiy rejimda chaqiring.
  2. executionStats bo'limi nega yo'qligini yozing.
  3. Reja bosqichlarini ro'yxat qilib chiqaring.
  4. SORT bosqichi nimani anglatishini tushuntiring.
  5. Ikkita indeks qo'yib, qaysi biri yutganini toping.
  6. rejectedPlans da nima borligini ko'ring.
  7. Reja tanlash qanday ishlashini o'z so'zingiz bilan yozing.
  8. Qoplovchi so'rov yozing va totalDocsExamined ni ko'ring.
  9. _id: 0 ni olib tashlang - qoplash saqlandimi?
  10. hint nega 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: 0 yozish ko'pincha unutiladi.
  • hint - oxirgi chora: u rejalashtiruvchining moslashuv qobiliyatini o'ldiradi.

Keyingi bo'limda tranzaksiyalar va replika to'plamini ko'ramiz.

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.