10-bo‘lim

Sxema dizayni - ichiga joylash yoki havola

Embedding va referencing tanlovi, 16 MB chegarasi, cheksiz o'sadigan massiv muammosi, ataylab takrorlash va bir nechta naqsh.

🕑 12 daqiqa o‘qish 📄 932 so‘z 👁 0 marta ko‘rilgan
Ushbu bo‘lim mundarijasi
  1. Asosiy savol
  2. Ichiga joylash
  3. Havola
  4. Qaysi birini tanlash
  5. Cheksiz massiv muammosi
  6. Ataylab takrorlash
  7. Bir necha foydali naqsh
  8. Xulosa

MongoDB sxemani majburlamaydi, lekin bu "sxema kerak emas" degani emas. Aksincha - sxemani siz o'ylab topasiz, va bu tanlov dastur tezligini butunlay belgilaydi.

JavaScript
db.mijoz.insertMany([
  { _id: 1, ism: "Husanboy Qodirov", shahar: "Namangan",
    manzil: { kocha: "Navoiy 12", indeks: "160100" },
    telefon: ["+998901112233", "+998932223344"] },
  { _id: 2, ism: "Malika Tursunova", shahar: "Toshkent",
    manzil: { kocha: "Amir Temur 5", indeks: "100000" },
    telefon: ["+998907778899"] }
])

db.buyurtma.insertMany([
  { _id: 101, mijoz_id: 1, sana: ISODate("2026-02-10T09:00:00Z"),
    qatorlar: [
      { kitob: "Otkan kunlar", soni: 1, narx: 85000 },
      { kitob: "Navoiy",       soni: 2, narx: 95000 }
    ] },
  { _id: 102, mijoz_id: 2, sana: ISODate("2026-02-14T13:30:00Z"),
    qatorlar: [
      { kitob: "Ufq", soni: 1, narx: 64000 }
    ] },
  { _id: 103, mijoz_id: 1, sana: ISODate("2026-03-02T11:15:00Z"),
    qatorlar: [
      { kitob: "Sarob", soni: 3, narx: 59000 }
    ] }
])

Asosiy savol #

Relyatsion bazada javob oldindan ma'lum: har narsa o'z jadvalida, JOIN bilan birlashtiriladi.

MongoDB da esa har safar tanlash kerak:

YondashuvQanday
Ichiga joylash (embedding)Ma'lumot bitta hujjat ichida
Havola (referencing)Alohida to'plam, _id orqali bog'lanish
Bitta ma'lumot, ikki xil joylashtirish Ichiga joylash { _id: 101, sana: ..., qatorlar: [ { kitob: "Navoiy", soni: 2 } ] } Bitta o'qish - hammasi keladi Havola buyurtma: { _id: 101, mijoz_id: 1 } mijoz: { _id: 1, ism: "Husanboy" } Ikki to'plam, ikki o'qish yoki $lookup Ma'lumot bir joyda saqlanadi Tanlov qoidasi Birga o'qiladigan ma'lumot birga saqlansin Alohida kerak bo'ladigan yoki cheksiz o'sadigan ma'lumot alohida tursin
Sxemani jadval emas, dasturning so'rovlari belgilaydi

Ichiga joylash #

Buyurtma qatorlari buyurtma ichida yotadi - shuning uchun bitta o'qishda hammasi keladi:

JavaScript
db.buyurtma.findOne({ _id: 101 })
Natija
{
  _id: 101,
  mijoz_id: 1,
  sana: ISODate('2026-02-10T09:00:00.000Z'),
  qatorlar: [
    { kitob: 'Otkan kunlar', soni: 1, narx: 85000 },
    { kitob: 'Navoiy', soni: 2, narx: 95000 }
  ]
}

Ichidagi massiv bo'yicha hisoblash ham shu yerda bajariladi:

JavaScript
db.buyurtma.aggregate([
  { $match: { _id: 101 } },
  { $project: {
      qatorlar_soni: { $size: "$qatorlar" },
      jami: { $sum: { $map: {
        input: "$qatorlar",
        as: "q",
        in: { $multiply: ["$$q.soni", "$$q.narx"] }
      } } }
  } }
])
Natija
[ { _id: 101, qatorlar_soni: 2, jami: 275000 } ]

Havola #

Mijoz ma'lumoti alohida to'plamda, buyurtmada esa faqat mijoz_id:

JavaScript
db.buyurtma.aggregate([
  { $match: { _id: 101 } },
  { $lookup: {
      from: "mijoz", localField: "mijoz_id",
      foreignField: "_id", as: "mijoz"
  } },
  { $project: { sana: 1, "mijoz.ism": 1, "mijoz.shahar": 1 } }
])
Natija
[
  {
    _id: 101,
    sana: ISODate('2026-02-10T09:00:00.000Z'),
    mijoz: [ { ism: 'Husanboy Qodirov', shahar: 'Namangan' } ]
  }
]

$lookup - SQL dagi LEFT JOIN ning muqobili. U haqida 13-bobda batafsil gaplashamiz.

Qaysi birini tanlash #

SavolIchiga joylashHavola
Ma'lumot birga o'qiladimiHaYo'q
Ichki ro'yxat cheklanganmiHaYo'q, cheksiz o'sadi
Ma'lumot tez-tez o'zgaradimiYo'qHa
Ko'p joyda takrorlanadimiYo'qHa
Alohida qidiriladimiYo'qHa

Amaliy misollar:

Ma'lumotTanlovSabab
Buyurtma qatorlariIchigaHar doim birga o'qiladi, soni chekli
Mijoz manziliIchigaMijozning bir qismi
Mijoz ma'lumotiHavolaKo'p buyurtmada ishlatiladi
Maqola izohlariHavolaCheksiz o'sishi mumkin
Mahsulot kategoriyasiHavolaNomi o'zgarishi mumkin
16 MB - qattiq chegara

Bitta BSON hujjatning maksimal hajmi 16 MB. Bu sozlama emas - uni oshirib bo'lmaydi.

Ko'pchilik "16 MB juda katta" deb o'ylaydi va ichiga joylashni cheklovsiz ishlatadi. Muammo esa chegaraga yetganda emas, ancha oldin boshlanadi:

Hujjat hajmiNima bo'ladi
100 KBHar o'qishda 100 KB tarmoqqa chiqadi
1 MBBitta izoh qo'shish uchun 1 MB qayta yoziladi
8 MBXotira keshiga kam hujjat sig'adi
16 MBYozib bo'lmaydi - xato

Eng yomoni oxirgisi: dastur ishlab turgan holda, ma'lum bir foydalanuvchida to'satdan ishlamay qoladi.

Qoida: massiv cheksiz o'sishi mumkin bo'lsa, uni ichiga joylamang. Izohlar, loglar, hodisalar, xabarlar - hammasi alohida to'plamda bo'lishi kerak.

Cheksiz massiv muammosi #

Noto'g'ri sxema shunday ko'rinadi:

Natija
{
  _id: 1,
  sarlavha: "Maqola",
  izohlar: [ ... 50 000 ta izoh ... ]
}

To'g'ri sxema - izohlar alohida to'plamda:

JavaScript
db.izoh.insertMany([
  { _id: 1, maqola_id: 7, kim: "Nodira", matn: "Ajoyib" },
  { _id: 2, maqola_id: 7, kim: "Aziza",  matn: "Rahmat" },
  { _id: 3, maqola_id: 8, kim: "Kamola", matn: "Foydali" }
])
db.izoh.countDocuments({ maqola_id: 7 })
db.izoh.find({ maqola_id: 7 }, { _id: 0, kim: 1, matn: 1 })
Natija
{ acknowledged: true, insertedIds: { '0': 1, '1': 2, '2': 3 } }
2
[ { kim: 'Nodira', matn: 'Ajoyib' }, { kim: 'Aziza', matn: 'Rahmat' } ]

O'rtacha yechim ham bor: oxirgi bir nechtasini ichiga joylab, qolganini alohida to'plamda saqlash. Sahifa ochilganda oxirgi 5 ta izoh darhol ko'rinadi, "hammasini ko'rsatish" bosilganda esa ikkinchi so'rov ketadi.

Ataylab takrorlash #

Relyatsion bazada takrorlash - xato. MongoDB da esa u ba'zan to'g'ri yechim:

JavaScript
db.buyurtma.findOne({ _id: 101 }, { "qatorlar.kitob": 1, "qatorlar.narx": 1 })
Natija
{
  _id: 101,
  qatorlar: [
    { kitob: 'Otkan kunlar', narx: 85000 },
    { kitob: 'Navoiy', narx: 95000 }
  ]
}

Kitob nomi kitob to'plamida ham bor, buyurtmada ham bor. Bu takrorlash ataylab:

SababIzoh
Tarixiy aniqlikKitob nomi o'zgarsa, eski chek o'zgarmasligi kerak
TezlikChekni ko'rsatish uchun $lookup kerak emas
MustaqillikKitob o'chirilsa ham buyurtma to'liq qoladi
Takrorlash qachon to'g'ri, qachon xato

Savol bitta: qiymat o'zgarganda nima bo'lishi kerak?

HolatQaror
Eski yozuv eski qiymatni saqlashi kerakTakrorlang
Hamma joyda yangi qiymat ko'rinishi kerakHavola qiling

Buyurtmadagi narx - birinchi holat. Kitob narxi ertaga oshsa, o'tgan oydagi chek o'zgarmasligi kerak.

Mahsulot kategoriyasining nomi - ikkinchi holat. Nom tuzatilsa, u hamma joyda tuzatilishi kerak.

Agar takrorlangan qiymatni yangilash kerak bo'lsa, buni updateMany bilan qilish mumkin - lekin bu millionlab hujjatda qimmat amal. Shuning uchun takrorlashni faqat o'zgarmas qiymatlar uchun ishlating.

Bir necha foydali naqsh #

NaqshQachon
Hisoblangan qiymatizohlar_soni ni hujjatda saqlash - har safar sanamaslik uchun
Oxirgi N taOxirgi 5 izohni ichiga joylab, qolganini alohida
Bo'lak (bucket)Sensor ma'lumotini soatlik hujjatlarga yig'ish
Kengaytirilgan havolamijoz_id yoniga mijoz_ism ni ham yozish

Oxirgisi eng ko'p ishlatiladigan naqsh:

JavaScript
db.buyurtma.updateOne(
  { _id: 101 },
  { $set: { mijoz_ism: "Husanboy Qodirov" } }
)
db.buyurtma.findOne({ _id: 101 }, { mijoz_id: 1, mijoz_ism: 1, sana: 1 })
Natija
{
  acknowledged: true,
  insertedId: null,
  matchedCount: 1,
  modifiedCount: 1,
  upsertedCount: 0
}
{
  _id: 101,
  mijoz_id: 1,
  sana: ISODate('2026-02-10T09:00:00.000Z'),
  mijoz_ism: 'Husanboy Qodirov'
}

Endi buyurtmalar ro'yxatini ko'rsatish uchun $lookup kerak emas - ism allaqachon shu yerda. To'liq mijoz ma'lumoti kerak bo'lgandagina ikkinchi so'rov ketadi.

Sxemani so'rovlardan boshlab loyihalang

Relyatsion bazada odat shunday: avval ma'lumotni normallashtiramiz, keyin so'rovlarni yozamiz.

MongoDB da tartib teskari:

  1. Dastur qanday ekranlarni ko'rsatadi?
  2. Har ekran uchun qanday ma'lumot kerak?
  3. Shu ma'lumot bitta o'qishda kelishi uchun sxema qanday bo'lishi kerak?

"Buyurtmalar ro'yxati" ekranida mijoz ismi kerak bo'lsa - demak ism buyurtma hujjatida bo'lishi kerak. Bu normallashtirish qoidasini buzadi, lekin MongoDB da bu to'g'ri qaror.

Eng ko'p uchraydigan xato - relyatsion sxemani o'zgartirmasdan MongoDB ga ko'chirish. Natijada har ekran uchun beshta $lookup yoziladi va tizim SQL dan ham sekin ishlaydi.

Amaliy topshiriq
  1. Ichiga joylash va havolaning ta'rifini yozing.
  2. Buyurtma qatorlari nega ichiga joylanganini tushuntiring.
  3. Mijoz ma'lumoti nega alohida to'plamda ekanini yozing.
  4. Bitta hujjatning maksimal hajmini ayting.
  5. Cheksiz o'sadigan massivning uchta xavfini sanang.
  6. Izohlarni to'g'ri joylashtiring va so'rov yozing.
  7. "Oxirgi N ta" naqshini o'z so'zingiz bilan tushuntiring.
  8. Takrorlash qachon to'g'ri ekanini misol bilan ko'rsating.
  9. Kengaytirilgan havola naqshini qo'llab ko'ring.
  10. Sxemani loyihalashda birinchi savol nima bo'lishi kerakligini yozing.

Xulosa #

  • MongoDB sxemani majburlamaydi - sxemani siz loyihalaysiz.
  • Ikki yo'l bor: ichiga joylash va havola.
  • Birga o'qiladigan ma'lumot birga saqlansin.
  • Cheksiz o'sadigan yoki alohida qidiriladigan ma'lumot alohida tursin.
  • Bitta hujjat 16 MB dan oshmasligi kerak - bu qattiq chegara.
  • Muammo 16 MB da emas, ancha oldin boshlanadi: har o'qishda butun hujjat keladi.
  • Izoh, log, hodisa kabi cheksiz ro'yxatlarni hech qachon ichiga joylamang.
  • Takrorlash MongoDB da xato emas - o'zgarmas qiymatlar uchun to'g'ri yechim.
  • Qaror qoidasi: eski yozuv eski qiymatni saqlashi kerakmi?
  • Sxemani so'rovlardan boshlab loyihalang, normallashtirish qoidasidan emas.

Keyingi bo'limda sxemani $jsonSchema bilan qanday majburlashni 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.