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.
Ushbu bo‘lim mundarijasi
MongoDB sxemani majburlamaydi, lekin bu "sxema kerak emas" degani emas. Aksincha - sxemani siz o'ylab topasiz, va bu tanlov dastur tezligini butunlay belgilaydi.
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:
| Yondashuv | Qanday |
|---|---|
| Ichiga joylash (embedding) | Ma'lumot bitta hujjat ichida |
| Havola (referencing) | Alohida to'plam, _id orqali bog'lanish |
Ichiga joylash #
Buyurtma qatorlari buyurtma ichida yotadi - shuning uchun bitta o'qishda hammasi keladi:
db.buyurtma.findOne({ _id: 101 })
{
_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:
db.buyurtma.aggregate([
{ $match: { _id: 101 } },
{ $project: {
qatorlar_soni: { $size: "$qatorlar" },
jami: { $sum: { $map: {
input: "$qatorlar",
as: "q",
in: { $multiply: ["$$q.soni", "$$q.narx"] }
} } }
} }
])
[ { _id: 101, qatorlar_soni: 2, jami: 275000 } ]
Havola #
Mijoz ma'lumoti alohida to'plamda, buyurtmada esa faqat
mijoz_id:
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 } }
])
[
{
_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 #
| Savol | Ichiga joylash | Havola |
|---|---|---|
| Ma'lumot birga o'qiladimi | Ha | Yo'q |
| Ichki ro'yxat cheklanganmi | Ha | Yo'q, cheksiz o'sadi |
| Ma'lumot tez-tez o'zgaradimi | Yo'q | Ha |
| Ko'p joyda takrorlanadimi | Yo'q | Ha |
| Alohida qidiriladimi | Yo'q | Ha |
Amaliy misollar:
| Ma'lumot | Tanlov | Sabab |
|---|---|---|
| Buyurtma qatorlari | Ichiga | Har doim birga o'qiladi, soni chekli |
| Mijoz manzili | Ichiga | Mijozning bir qismi |
| Mijoz ma'lumoti | Havola | Ko'p buyurtmada ishlatiladi |
| Maqola izohlari | Havola | Cheksiz o'sishi mumkin |
| Mahsulot kategoriyasi | Havola | Nomi o'zgarishi mumkin |
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 hajmi | Nima bo'ladi |
|---|---|
| 100 KB | Har o'qishda 100 KB tarmoqqa chiqadi |
| 1 MB | Bitta izoh qo'shish uchun 1 MB qayta yoziladi |
| 8 MB | Xotira keshiga kam hujjat sig'adi |
| 16 MB | Yozib 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:
{
_id: 1,
sarlavha: "Maqola",
izohlar: [ ... 50 000 ta izoh ... ]
}
To'g'ri sxema - izohlar alohida to'plamda:
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 })
{ 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:
db.buyurtma.findOne({ _id: 101 }, { "qatorlar.kitob": 1, "qatorlar.narx": 1 })
{
_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:
| Sabab | Izoh |
|---|---|
| Tarixiy aniqlik | Kitob nomi o'zgarsa, eski chek o'zgarmasligi kerak |
| Tezlik | Chekni ko'rsatish uchun $lookup kerak emas |
| Mustaqillik | Kitob o'chirilsa ham buyurtma to'liq qoladi |
Savol bitta: qiymat o'zgarganda nima bo'lishi kerak?
| Holat | Qaror |
|---|---|
| Eski yozuv eski qiymatni saqlashi kerak | Takrorlang |
| Hamma joyda yangi qiymat ko'rinishi kerak | Havola 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 #
| Naqsh | Qachon |
|---|---|
| Hisoblangan qiymat | izohlar_soni ni hujjatda saqlash - har safar sanamaslik uchun |
| Oxirgi N ta | Oxirgi 5 izohni ichiga joylab, qolganini alohida |
| Bo'lak (bucket) | Sensor ma'lumotini soatlik hujjatlarga yig'ish |
| Kengaytirilgan havola | mijoz_id yoniga mijoz_ism ni ham yozish |
Oxirgisi eng ko'p ishlatiladigan naqsh:
db.buyurtma.updateOne(
{ _id: 101 },
{ $set: { mijoz_ism: "Husanboy Qodirov" } }
)
db.buyurtma.findOne({ _id: 101 }, { mijoz_id: 1, mijoz_ism: 1, sana: 1 })
{
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.
Relyatsion bazada odat shunday: avval ma'lumotni normallashtiramiz, keyin so'rovlarni yozamiz.
MongoDB da tartib teskari:
- Dastur qanday ekranlarni ko'rsatadi?
- Har ekran uchun qanday ma'lumot kerak?
- 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.
- Ichiga joylash va havolaning ta'rifini yozing.
- Buyurtma qatorlari nega ichiga joylanganini tushuntiring.
- Mijoz ma'lumoti nega alohida to'plamda ekanini yozing.
- Bitta hujjatning maksimal hajmini ayting.
- Cheksiz o'sadigan massivning uchta xavfini sanang.
- Izohlarni to'g'ri joylashtiring va so'rov yozing.
- "Oxirgi N ta" naqshini o'z so'zingiz bilan tushuntiring.
- Takrorlash qachon to'g'ri ekanini misol bilan ko'rsating.
- Kengaytirilgan havola naqshini qo'llab ko'ring.
- 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.
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.