18 β Ma'lumotlar bazasi: SQL vs NoSQL, modellashtirish¶
β¬ οΈ Oldingi: 17 β Servislararo aloqa va API dizayni Β· π README Β· Keyingi: 19 β Masshtablash va load balancing β‘οΈ
Bu bobda: tizimingiz uchun ma'lumotni qayerda va qanday saqlash kerakligini qaror qilishni o'rganamiz. Relyatsion (SQL) bazalarning kuchli tomonlari (yaxlitlik, murakkab so'rov, yetuklik) va NoSQL oilalari β document (MongoDB), key-value (Redis), column-family (Cassandra), graph (Neo4j) β har biri qaysi muammoni hal qilishini ko'rib chiqamiz. Eng muhim konseptual chigallikni β ACID va BASE farqini, ayniqsa ACID'dagi C (yaxlitlik qoidalari) CAP'dagi C (replikalar bir xil) emas ekanligini β aniqlashtiramiz. So'ng modellashtirishning ikki uslubini (normalizatsiya vs denormalizatsiya, "access pattern bo'yicha modellashtirish"), polyglot persistence va mikroservislardagi database-per-service g'oyasini o'rganamiz.
Trade-off eslatmasi / Halollik: bu bob konseptual va trade-off haqida β bu yerda "to'g'ri javob" yo'q, faqat kontekstga bog'liq tanlov bor. "SQL eskirgan", "NoSQL tezroq", "mikroservisda har doim NoSQL" kabi mutlaq da'volar β noto'g'ri; har birining narxi va foydasi bor. Bobdagi kichik kod-misollar (TypeScript) modellashtirish g'oyasini ko'rsatish uchun haqiqatan ishga tushirilib tekshirilgan (natijalar matnda); DB tanlovi qarorlari esa "ishlab ko'rib bo'lmaydigan" dizayn qarorlari β ularni trade-off tahlili sifatida beramiz, "bu eng yaxshisi" demaymiz. Tarqoq (distributed) ma'lumot bo'yicha da'volar Martin Kleppmann, Designing Data-Intensive Applications (DDIA) bilan moslangan.
Nega bu qaror shunchalik muhim?¶
Arxitekturada ko'p qarorni keyin o'zgartirish mumkin: framework, til, hatto qatlamlar tuzilishi. Lekin ma'lumotlar bazasi tanlovi β eng qiyin qaytariladigan qarorlardan biri. Sababi oddiy: kod vaqtinchalik, ma'lumot esa abadiy. Servisni qayta yozish mumkin, lekin yillar davomida to'plangan millionlab yozuvni boshqa modelga ko'chirish β og'riqli, xavfli va qimmat migratsiya.
Shuning uchun bu bobda biz "qaysi DB eng yaxshi?" degan noto'g'ri savoldan voz kechamiz. To'g'ri savol β "shu ma'lumot, shu so'rovlar, shu masshtab va shu yaxlitlik talabi uchun qaysi model mos?".
Eslatma: "SQL vs NoSQL" β biroz chalg'ituvchi qarama-qarshilik. SQL β bu so'rov tili; NoSQL esa "faqat-SQL-emas" degan keng soyabon atama bo'lib, ichida bir nechta butunlay boshqa model bor. Document-bazani key-value bilan solishtirish β olma va apelsinni solishtirishdek. Shuning uchun "NoSQL tez" degan gap mantiqsiz: qaysi NoSQL, qaysi vazifa uchun?
1-qism. Relyatsion (SQL) bazalar¶
Relyatsion model 1970-yilda E. F. Codd tomonidan taklif qilingan va shu kungacha eng keng tarqalgan saqlash modeli bo'lib qolmoqda. Asosiy g'oya: ma'lumot jadvallar (relation'lar) ko'rinishida, har jadval β qatorlar (yozuvlar) va ustunlar (atributlar) to'plami.
Uchta tayanch tushuncha:
- Sxema (schema): jadval tuzilishi oldindan belgilanadi β qaysi ustun, qaysi tur, qaysi cheklov (constraint). Ma'lumot kirishdan oldin "shaklga" mos kelishi shart (schema-on-write).
- JOIN: ma'lumot bir necha jadvalga bo'linadi (foydalanuvchilar, buyurtmalar, mahsulotlar β alohida), so'rov paytida ular chet kalit (foreign key) orqali birlashtiriladi. Bu β relyatsion modelning yuragi.
- Tranzaksiya: bir nechta o'zgarishni bitta "hammasi yoki hech nima" birligiga jamlash. Bu odatda ACID kafolatlari bilan keladi (pastda batafsil).
-- Relyatsion: ma'lumot bo'linadi, JOIN bilan birlashtiriladi
SELECT u.name, o.id, o.total
FROM users AS u
JOIN orders AS o ON o.user_id = u.id
WHERE u.city = 'Toshkent';
SQL kuchli tomonlari¶
- Kuchli yaxlitlik (consistency). Cheklovlar (FK, UNIQUE, CHECK) ma'lumotni "buzilishdan" baza darajasida himoya qiladi. Kod xato qilsa ham, baza qoidani o'tkazmaydi.
- Murakkab, oldindan ko'zlanmagan so'rov. SQL β kuchli, deklarativ til. Bugun "shaharlar bo'yicha o'rtacha buyurtma", ertaga butunlay boshqa kesim β modelni o'zgartirmasdan so'raysiz. Bu moslashuvchanlik o'qish tomonida.
- Yetuklik (maturity). O'nlab yillik optimizatsiya, ishonchli tranzaksiya, boy asboblar, katta jamoa bilimi. "Zerikarli texnologiya" β ko'pincha eng aqlli tanlov.
Amaliyotda: ko'pchilik loyiha uchun standart, yaxshi tanlov β relyatsion baza (PostgreSQL yoki MySQL). NoSQL'ga o'tish β odatda aniq bir muammoni (masshtab, shakl, maxsus access pattern) hal qilish uchun ongli qaror bo'lishi kerak, "moda" yoki "yangi-shu uchun yaxshi" emas. SQL bo'yicha chuqurroq:
../sql/README.md, DB modellashtirish bo'yicha alohida kitob:../db-dizayni/README.md.
2-qism. NoSQL oilalari β har biri qachon¶
"NoSQL" bitta narsa emas. Eng kamida to'rt oila bor va ularning ehtiyojlari butunlay boshqacha.
Document (hujjatli) β masalan MongoDB¶
Ma'lumot hujjat (odatda JSON/BSON) sifatida saqlanadi. Bir-biriga aloqador narsalar bitta hujjatga joylanadi (embedded), JOIN o'rniga.
- Kuchli tomoni: moslashuvchan shakl (har hujjat boshqacha bo'lishi mumkin), "butun obyekt" bilan o'qish/yozish (profil, content, katalog).
- Qachon: ma'lumot tabiatan iyerarxik/o'zgaruvchan; ko'pincha "bitta obyektni butunligicha" o'qiysiz.
Key-value (kalit-qiymat) β masalan Redis¶
Eng sodda model: kalit -> qiymat. Baza qiymat ichini "tushunmaydi" β faqat saqlaydi va kalit bo'yicha qaytaradi. Ko'pincha xotirada ishlaydi, juda tez.
- Kuchli tomoni: tezlik, soddalik.
- Qachon: cache (kesh), sessiya, rate-limiting (so'rov chegarasi), navbat, leaderboard β kalit bo'yicha tez kirish.
Column-family (ustun-oilali) β masalan Cassandra¶
Kalit bo'yicha taqsimlangan, keng ustunli qatorlar. Gorizontal masshtablash uchun loyihalangan β ko'p tugunga oson tarqaladi, juda katta yozuv oqimini ko'taradi.
- Kuchli tomoni: ulkan masshtab, yuqori yozuv tezligi, gorizontal kengayish.
- Qachon: vaqt-qatorlar (metrikalar), loglar, IoT, juda katta yozuv hajmi.
Graph (graf) β masalan Neo4j¶
Tugun (entity) va bog'lanish (qirra) β birinchi darajali fuqarolar. Bog'lanishlar bo'ylab yurish (traversal) tabiiy va tez.
- Kuchli tomoni: chuqur, ko'p qatlamli bog'lanishlarni samarali so'rash.
- Qachon: ijtimoiy tarmoq ("do'stning do'sti"), tavsiya tizimi, yo'l topish, firibgarlik (fraud) tahlili.
Diqqat β keng tarqalgan xato: "NoSQL sxemasiz, demak modellashtirish kerak emas". Aksincha β NoSQL'da modellashtirish muhimroq va qiyinroq, chunki baza sizni xatodan himoya qilmaydi. Sxema yo'qoladi degani emas β u kod ichiga ko'chadi (schema-on-read). Modellashtirish yo'qolmaydi, faqat mas'uliyat sizga o'tadi.
3-qism. ACID vs BASE β va ikki "C" chalkashligi¶
Bu β bobning eng muhim konseptual bo'limi. Ikki saqlash falsafasi bor.
ACID (odatda relyatsion bazalar) β to'rt kafolat:
- A β Atomicity (atomarlik): tranzaksiya "hammasi yoki hech nima". Yarim bajarilmaydi. Pul A'dan yechilib, B'ga qo'shilmasa β ikkalasi ham bekor.
- C β Consistency (yaxlitlik): tranzaksiya bazani bir yaroqli holatdan boshqasiga o'tkazadi, yaxlitlik qoidalarini (FK, UNIQUE, CHECK) buzmasdan.
- I β Isolation (izolyatsiya): parallel tranzaksiyalar bir-birini sezmaydi β natija ketma-ket bajarilgandek bo'ladi.
- D β Durability (mustahkamlik): commit bo'lgach, ma'lumot nosozlik (masalan, elektr o'chishi) dan keyin ham saqlanadi.
BASE (ko'p NoSQL) β boshqa muvozanat:
- BA β Basically Available: tizim deyarli doim javob beradi (eski qiymat bo'lsa ham, xato emas).
- S β Soft state: holat tashqi kirish bo'lmasa ham vaqt o'tib o'zgarishi mumkin (replikatsiya jarayoni davom etayotgani uchun).
- E β Eventual consistency: yangi yozuv bo'lmasa, barcha nusxalar oxir-oqibat bir xil qiymatga yetib oladi.
Eng muhim aniqlashtirish: ACID-C β CAP-C¶
Bu β eng ko'p adashtiriladigan joy, hatto tajribali muhandislar ham. Ikki butunlay boshqa narsa bir xil harf bilan ataladi:
- ACID'dagi C (Consistency) β yaxlitlik qoidalari. Bitta baza ichida tranzaksiya cheklovlarni (FK, CHECK, balans manfiy bo'lmasligi) buzmaydi. Bu β ma'lumotning mantiqiy to'g'riligi haqida.
- CAP'dagi C (Consistency) β replikalar bir xil. Tarqoq tizimda har o'qish eng so'nggi yozuvni ko'radi; barcha nusxalar bir vaqtning o'zida bitta qiymatni qaytaradi. Bu β ma'lumotning nusxalar bo'yicha mosligini haqida (texnik nomi: linearizability).
Boshqacha aytganda: ACID-C "qoida buzilmadimi?" deb so'raydi; CAP-C "uchchala serverning javobi bir xilmi?" deb so'raydi. Bular mustaqil tushunchalar. Bitta serverdagi relyatsion baza ACID-C ni beradi, lekin u haqida CAP-C "replikalar" so'rog'i umuman ma'noga ega emas (replika yo'q). DDIA ham bu chalkashlikni alohida ta'kidlaydi: "consistency" so'zi haddan ortiq yuklangan atama (overloaded).
Eslatma: CAP teoremasining o'zi (Eric Brewer) β bu bobning mavzusi emas; uni 22-bobda chuqur ko'ramiz. Hozir faqat bitta narsani eslab qoling: ikki "C" β ikki boshqa narsa. Birovni "ACID consistency bor, demak CAP consistency ham bor" deganini eshitsangiz β bu xato. Eslatib o'tamiz: tarqoq tizimda partitsiya muqarrar, shuning uchun "C vs A" tanlovi partitsiya paytida yuzaga keladi β bu ham 22-bobda.
Eventual consistency intuitsiyasi (tekshirilgan)¶
BASE'ning "yetib oladi" g'oyasini kichik simulyatsiya bilan ko'rsatamiz. Primary'ga yozamiz; follower (replika) hali eski qiymatni ko'rsatadi; keyin replikatsiya yetib oladi:
class Replica {
private value = 0;
read(): number { return this.value; }
apply(v: number): void { this.value = v; }
}
const primary = new Replica();
const follower = new Replica();
primary.apply(42); // primary'ga yozdik
console.log(primary.read()); // 42
console.log(follower.read()); // 0 <- vaqtincha MOS KELMAYDI (kechikish)
follower.apply(primary.read()); // replikatsiya yetib oladi
console.log(follower.read()); // 42 <- endi mos
Ishga tushirsak (haqiqatan olingan natija):
Ana shu "yozdim, lekin boshqa replika hali eskisini ko'rsatadi" oynasi β eventual consistency'ning mohiyati. Foydalanuvchi izohini yozib, sahifani yangilaganda ba'zan u yo'qdek ko'rinishi β aynan shu. Bu xato emas, balki ongli trade-off: mavjudlik va tezlik uchun lahzali moslikdan vaqtincha voz kechish.
Trade-off: ACID β to'g'rilikka urg'u (pul, buyurtma, inventar). BASE β mavjudlik va masshtabga urg'u (feed, like soni, "ko'rishlar"). Mutlaq "ACID yaxshi" yoki "BASE zamonaviy" β noto'g'ri. Bank balansini eventual consistency bilan saqlamang; "necha kishi ko'rdi" hisoblagichini esa qattiq tranzaksiya bilan saqlash isrof. Muhimi: ma'lumotning har bo'lagi uchun alohida qaror.
4-qism. Modellashtirish β ikki dunyo¶
Saqlash modeli tanlovi tugaydi; endi ma'lumotni qanday tuzish kerak? Bu yerda SQL va NoSQL falsafasi tubdan ajraladi.
Relyatsion uslub: normalizatsiya¶
Normalizatsiya β ma'lumotni takrorlanmaslik uchun bo'lib tashlash: har fakt faqat bir joyda saqlanadi. Foydalanuvchi nomi β users jadvalida; buyurtma faqat user_id ga ishora qiladi. Nom o'zgarsa β bitta joyda o'zgartirasiz, hamma joyda yangilanadi.
- Foyda: yozish oson va xavfsiz (bitta nuqta), takror yo'q, ma'lumot ziddiyatga tushmaydi.
- Narx: o'qishda JOIN kerak β bir necha jadvalni birlashtirish. Ko'p JOIN β sekinroq o'qish.
NoSQL uslub: denormalizatsiya va "access pattern bo'yicha modellashtirish"¶
NoSQL'da (ayniqsa document/column-family) tartib teskari:
- Avval so'rovlaringizni (access pattern) bilib oling: ilovam ma'lumotni qanday o'qiydi? Eng tez-tez qaysi so'rov?
- Keyin modelni shu so'rovga moslab tuzing β ko'pincha denormalizatsiya bilan: o'qishda kerak bo'ladigan narsani oldindan birga saqlash (nusxalash), toki o'qish bitta operatsiya bo'lsin, JOIN bo'lmasin.
Misol β "foydalanuvchi profil sahifasi" so'rovi: profil + oxirgi 3 buyurtma. Document modelda ularni bitta hujjatda saqlaymiz, shunda sahifa bitta o'qish bilan tayyor:
interface OrderSummary { id: string; total: number; status: string; }
interface UserProfileDoc {
userId: string;
name: string;
city: string;
recentOrders: OrderSummary[]; // DENORMALIZATSIYA: nusxalangan
}
const profiles = new Map<string, UserProfileDoc>();
profiles.set("u1", {
userId: "u1", name: "Oqil", city: "Toshkent",
recentOrders: [
{ id: "o9", total: 120000, status: "yetkazildi" },
{ id: "o8", total: 45000, status: "yo'lda" },
],
});
// "Profil sahifasi" access pattern: JOIN yo'q, bitta kalit-o'qish
function getProfilePage(userId: string) { return profiles.get(userId); }
Ishga tushirsak (haqiqatan olingan natija):
Diqqat: denormalizatsiyaning narxi β yangilanish. Foydalanuvchi nomi
recentOrdersichida ham nusxalangan bo'lsa, nom o'zgarganda bir nechta joyni yangilashga to'g'ri keladi (yoki "buyurtma paytidagi nom" deb ataylab eski qoldiriladi). Ya'ni denormalizatsiya o'qishni tezlashtiradi, yozishni qiyinlashtiradi. Bu β sof trade-off, "yaxshi/yomon" emas.
Asosiy farqni eslab qoling¶
| Relyatsion (normalizatsiya) | NoSQL (denormalizatsiya) | |
|---|---|---|
| Tartib | model -> keyin so'rov | so'rov -> keyin model |
| Optimizatsiya | yozish (bitta nuqta) | o'qish (oldindan birga) |
| Takror | yo'q | ataylab bor |
| O'qish | JOIN bilan birlashtiriladi | bitta o'qish, JOIN yo'q |
| Yangilash | oson (bir joy) | qiyin (ko'p nusxa) |
Trade-off: "denormalizatsiya har doim tezroq" β xato. U o'qish-og'ir (read-heavy) va so'rovlar oldindan ma'lum bo'lganda foydali. So'rovlar oldindan noma'lum yoki yozish-og'ir bo'lsa β normalizatsiya + SQL ko'pincha to'g'ri. DDIA ham buni shunday qo'yadi: relyatsion model "oldindan ko'zlanmagan so'rovlar"ga, document model "ma'lum, qat'iy access pattern"ga yaxshi mos.
Indeks β bir og'iz so'z¶
Ikkala dunyoda ham indeks β ma'lum so'rovni tezlashtiradigan yordamchi tuzilma. Tasavvur qiling: kitobning orqasidagi alifboli ko'rsatkich. Har sahifani varaqlamasdan, kerakli so'zni tez topasiz. Ko'pchilik baza B-tree (balansli daraxt) indeksidan foydalanadi β bu logarifmik qidiruvni beradi. Lekin indeks bepul emas: u o'qishni tezlashtiradi, yozishni sekinlashtiradi (har yozuvda indeks ham yangilanadi) va joy egallaydi. Indekslar bu kitob mavzusi emas β chuqur tushuntirish SQL kitobida: ../sql/README.md.
5-qism. Polyglot persistence¶
Yuqoridagi hammasi bitta xulosaga olib keladi: bitta tizimda bitta baza shart emas. Har vazifa uchun unga eng mos modelni tanlash mumkin β bu polyglot persistence (ko'p tilli saqlash; atamani Martin Fowler ommalashtirgan).
Misol β e-commerce backend:
- Buyurtma va to'lov -> PostgreSQL (SQL): pul, ACID, kuchli yaxlitlik shart.
- Mahsulot katalogi -> MongoDB (document): har mahsulot turli atributli, shakl o'zgaruvchan.
- Savat va sessiya -> Redis (key-value): tez, vaqtinchalik (TTL bilan o'chadi).
- Qidiruv va loglar -> Elasticsearch: to'liq matnli qidiruv, analitika.
- Tavsiya ("buni olganlar yana...") -> Neo4j (graph): chuqur bog'lanish.
Trade-off β bu g'oyaning qorong'i tomoni: har yangi baza β yangi operatsion yuk. Backup, monitoring, yangilash, xavfsizlik, jamoa tajribasi β har biri uchun alohida. Beshta baza β beshta "ishlamay qolishi mumkin" nuqtasi va beshta o'rganish egri chizig'i. Ko'pchilik loyiha uchun bitta yaxshi relyatsion baza + bitta Redis etarli. Polyglot β masshtab yoki aniq ehtiyoj majbur qilganda keladi, "qiziq, deb" emas. PostgreSQL'ning o'zi JSON, to'liq-matnli qidiruv, hatto ba'zi graf-so'rovlarni qo'llab-quvvatlaydi β ko'p hollarda "bitta baza" yetarli darajada ko'p tilli.
6-qism. Database-per-service (mikroservislar)¶
16-bobda mikroservislarni ko'rgan edik. Ularning markaziy qoidasi β database-per-service: har servis o'z bazasiga ega va boshqa servisning bazasiga to'g'ridan-to'g'ri tegmaydi. Ma'lumot faqat servisning API'si orqali olinadi.
Monolit (umumiy baza): Mikroservis (database-per-service):
[Order] [User] [Pay] [Order svc] [User svc] [Pay svc]
\ | / | | |
\ | / [Order DB] [User DB] [Pay DB]
[ Bitta DB ] (har biri mustaqil, izolyatsiya)
Nega? Servislar haqiqatan mustaqil bo'lishi uchun. Umumiy baza bo'lsa, bitta servis sxemani o'zgartirsa β boshqalari buziladi; bu yashirin tight coupling (qattiq bog'liqlik, 04-bob). Alohida baza β har jamoa o'z modelini, hatto o'z baza turini (polyglot!) erkin tanlaydi.
Diqqat β bu g'oyaning kelib chiqaradigan ikki muammosi:
- Cross-service JOIN yo'q. "Foydalanuvchi + uning buyurtmalari"ni bitta JOIN bilan ololmaysiz β ular ikki bazada. Yechim: API orqali ikki marta so'rab, kodda birlashtirish (yoki o'qish uchun maxsus denormalizatsiyalangan ko'rinish β ba'zan CQRS deb ataladi).
- Distributed (tarqoq) tranzaksiya. "Buyurtma yarat va to'lovni yech" β ikki bazaga tegadi. Klassik ACID tranzaksiya bu yerda ishlamaydi. Yechim β Saga namunasi: har qadamni alohida lokal tranzaksiya qilib, xato bo'lsa kompensatsiya (orqaga qaytaruvchi amal) bajarish. Saga va navbatlar (queue) β 21-bobning mavzusi.
Trade-off: database-per-service mustaqillik beradi, lekin tarqoq ma'lumotning butun og'irligini β eventual consistency, saga, ko'p so'rov, ma'lumot dublikatsiyasi β olib keladi. Shuning uchun mikroservis (16-bob) "yengil" qaror emas. Ko'pgina tizim uchun modulli monolit (bitta baza, lekin toza ichki chegaralar) β ancha sodda va arzon. "Mikroservis = zamonaviy = yaxshi" β bu aynan biz qochadigan mutlaq da'vo.
7-qism. Tanlov mezoni β amaliy ramka¶
Endi hammasini bitta qaror ramkasiga jamlaymiz. Yangi ma'lumot to'plami uchun beshta savol so'rang:
- Ma'lumot tuzilmasi qanday? Qat'iy, jadval-shaklmi (-> SQL)? O'zgaruvchan, iyerarxikmi (-> document)? Sof kalit-qiymatmi (-> key-value)? Asosan bog'lanishlarmi (-> graph)?
- So'rovlar qanday? Oldindan noma'lum, murakkab, analitik (-> SQL kuchli)? Ma'lum, sodda, kalit bo'yicha (-> NoSQL mos)?
- Yaxlitlik talabi qanchalik qattiq? Pul/buyurtma/inventar β kuchli (-> ACID). Feed/like/ko'rishlar β yumshoq bo'lsa bo'ladi (-> BASE qabul qilinadi).
- Masshtab qancha? Bir nechta gigabayt va minglab so'rov β deyarli har qanday baza yetadi. Petabayt va millionlab yozuv/sekund β gorizontal masshtablash (column-family) jiddiy argument.
- Jamoa nimani biladi? Bu eng kam baholanadigan, lekin eng amaliy mezon. Jamoa PostgreSQL'ni a'lo biladimi, Cassandra'ni β yo'q? Notanish baza bilan "to'g'ri" yechim ko'pincha tanish baza bilan "yetarlicha yaxshi" yechimdan yomonroq ishlaydi β operatsion xatolar tufayli.
Amaliyotda β Telegram-bot backend misoli. Bot foydalanuvchilari, ularning sozlamalari va xabar tarixini saqlaysiz. (a) Foydalanuvchi + sozlama β qat'iy tuzilma, so'rovlar oddiy -> PostgreSQL (SQL) tabiiy. (b) "Aktiv sessiya" / rate-limit "shu chat soatiga necha so'rov" -> Redis (key-value, TTL). (c) Agar "do'st kim-kimni taklif qilgan" tarmog'i kerak bo'lsa va u chuqur bo'lsa -> graf-savol; lekin oddiy holatda buni ham SQL'da bemalol qilasiz. Ya'ni: avval bitta SQL bilan boshlang, real ehtiyoj paydo bo'lganda qo'shing. Real bot misoli:
../tgbot-js/README.md.Trade-off β yakuniy halollik: bu bobdagi hech bir mezon "formula" emas. Ikki tajribali arxitektor bir xil talabga boshqa baza tanlashi mumkin β va ikkalasi ham haq bo'lishi mumkin, chunki ular boshqa narsani (tezlik vs sodlik vs jamoa bilimi vs kelajak masshtab) og'irroq deb hisoblagan. Sizning vazifangiz β "to'g'ri javob"ni topish emas, balki ongli, asoslangan, hujjatlashtirilgan trade-off qilish (ADR β 03-bob).
Mashqlar¶
Oson¶
1. Quyidagi har bir ma'lumot uchun qaysi NoSQL oilasi (document / key-value / column-family / graph) eng tabiiy? (a) foydalanuvchi sessiyasi, 30 daqiqada o'chadi; (b) "do'stning do'stlari" tarmog'i; (c) IoT sensorlardan sekundiga millionlab o'lchov; (d) har biri turli maydonli mahsulot katalogi.
2. "NoSQL β sxemasiz, demak modellashtirish kerak emas." Bu da'voni tuzating va nega xato ekanligini bir-ikki gap bilan tushuntiring.
3. Quyidagilardan qaysi biri ACID'dagi C (yaxlitlik qoidalari), qaysi biri CAP'dagi C (replikalar bir xil)? (a) "Hisob balansi hech qachon manfiy bo'lmaydi." (b) "Uchchala replikam ham bir vaqtda aynan bitta qiymatni qaytaradi." (c) "Buyurtmaning user_id si mavjud foydalanuvchiga ishora qilishi shart."
4. "SQL eskirgan, NoSQL β zamonaviy va tezroq." Bu jumlada nechta xato bor? Sanab, har birini qisqa tuzating.
5. Normalizatsiya va denormalizatsiya β qaysi biri o'qishni, qaysi biri yozishni osonlashtiradi? Bir jumla bilan tushuntiring.
O'rta¶
6. Bank o'tkazmasi (A'dan B'ga pul) uchun: (a) ACID'ning qaysi to'rt harfi shu yerda nimani kafolatlaydi (har biriga shu kontekstda bir misol)? (b) Bu ma'lumotni eventual consistency (BASE) bilan saqlash nega xavfli?
7. Sizda e-commerce uchun bu access pattern bor: "mahsulot sahifasini ochganda β mahsulot nomi, narxi va oxirgi 5 ta sharhni bitta o'qishda ko'rsatish". (a) Document modelda buni qanday tuzasiz? (b) Sharh egasi (foydalanuvchi) o'z ismini o'zgartirsa, qanday muammo chiqadi va uni qanday hal qilardingiz (ikki variant)?
8. "Polyglot persistence har doim yaxshi β har vazifaga ideal baza." Bu da'voga qarshi uchta amaliy argument keltiring (operatsion nuqtai nazardan).
9. Mikroservislarda "foydalanuvchi + uning buyurtmalari"ni bitta sahifada ko'rsatish kerak, lekin User va Order β ikki alohida servis, ikki baza. SQL JOIN ishlamaydi. Ikki yechim taklif qiling va har birining trade-off'ini ayting.
10. Bir kichik startup "kelajakda Google'dek masshtab kerak bo'ladi" deb birinchi kundan Cassandra (column-family) tanladi. Bu qaror bilan bog'liq qanday xavf bor? Qanday maslahat berardingiz va nega? (Tushunchalar: YAGNI β 06-bob, jamoa tajribasi.)
Qiyin¶
11. Access pattern bo'yicha modellashtiring (kod). "Ijtimoiy feed" tizimi: foydalanuvchi o'z feed'ini ochganda β uni bitta o'qish bilan olishi kerak (eng tez-tez so'rov). TypeScript'da fan-out on write yondashuvini yozing: publishPost(authorId, followers, post) har follower'ning feed'iga postni nusxalasin; getFeed(userId) bitta kalit-o'qish bo'lsin (JOIN yo'q). Ikki post chiqarib, bitta follower'ning feed uzunligi va eng yangi postini tekshiring. So'ng: bu yondashuvning yozish tomonidagi narxi nima?
12. Trade-off tahlili (kod yo'q). Yangi "to'lov hisob-kitoblari" (ledger) servisi loyihalayapsiz: har tranzaksiya o'zgarmas (immutable), balans har doim to'g'ri bo'lishi shart, oylik hisobotlar uchun murakkab analitik so'rovlar kerak, hozircha masshtab kichik. Qaysi baza modelini tanlaysiz va nega? Qaysi NoSQL oilalarini rad etasiz va nega? Yaxlitlik talabini ACID/BASE tilida ifodalang.
13. Ikki "C" ni amaliy ajrating. Bir muhandis aytadi: "Bizning bazamiz ACID, demak replikalararo har doim mos β CAP'da ham C kafolatlangan." Bu xulosa qayerda buziladi? ACID-C bitta serverda nimani kafolatlaydi va u nega avtomatik ravishda replikalararo moslikni (CAP-C) bermaydi? (Maslahat: bitta primary + bitta follower replikatsiya kechikishi bilan.)
14. Polyglot loyihalang. "Onlayn ta'lim platformasi" uchun bu ma'lumotlar bor: (i) o'quvchi profili va kurslari (qat'iy, pulli β to'lov bilan bog'liq); (ii) video stream sessiya holati (vaqtinchalik, tez); (iii) kurs qidiruvi (to'liq matnli); (iv) "shu kursni olganlar yana shu kursni oldi" tavsiyasi. Har biri uchun model/baza tanlang, asoslang. So'ng: bu polyglot qarorni soddalashtirishning bir yo'lini ham taklif qiling (kamroq baza bilan).
Yechimlar
1-mashq yechimi¶
(a) Key-value (Redis) β kalit bo'yicha tez kirish, TTL bilan avtomatik o'chish. (b) Graph (Neo4j) β chuqur bog'lanishlar bo'ylab yurish tabiiy va tez. (c) Column-family (Cassandra) β ulkan yozuv oqimi, vaqt-qatorlar, gorizontal masshtab. (d) Document (MongoDB) β har hujjat turli shaklda bo'lishi mumkin.
2-mashq yechimi¶
Tuzatilgan: "NoSQL ko'pincha schema-on-read β sxema baza darajasida majburlanmaydi, lekin yo'qolmaydi: u kod ichiga ko'chadi." Xato sababi β modellashtirish g'oyib bo'lmaydi, mas'uliyat sizga o'tadi. Aksincha, baza sizni xatodan himoya qilmagani uchun NoSQL'da to'g'ri modellashtirish muhimroq va qiyinroq: access pattern'ni oldindan bilish va shunga moslash kerak.
3-mashq yechimi¶
(a) ACID-C β bitta baza ichidagi yaxlitlik qoidasi (balans manfiy bo'lmasligi cheklovi). (b) CAP-C β replikalararo moslik (uchchala nusxa bir xil qiymat). (c) ACID-C β chet kalit (FK) yaxlitlik qoidasi. Eslatma: (a) va (c) bitta server haqida, (b) esa nusxalararo moslik haqida β butunlay boshqa o'lcham.
4-mashq yechimi¶
Kamida uch xato:
- "SQL eskirgan" β yo'q; relyatsion bazalar faol rivojlanadi va eng keng ishlatiladi. Yetuklik β kamchilik emas, kuch.
- "NoSQL" β bitta narsa emas; to'rtta butunlay boshqa oila. "NoSQL tezroq" β qaysi NoSQL, qaysi vazifa uchun?
- "tezroq" β kontekstsiz ma'nosiz. NoSQL ba'zi access pattern uchun tezroq (kalit-o'qish), lekin murakkab analitik so'rovda SQL ko'pincha kuchliroq. Tezlik β vazifaga bog'liq, mutlaq emas.
5-mashq yechimi¶
Normalizatsiya β yozishni osonlashtiradi: har fakt bitta joyda, bir nuqtani yangilash kifoya. Denormalizatsiya β o'qishni osonlashtiradi: kerakli narsa oldindan birga saqlangani uchun JOIN'siz bitta o'qishda olinadi (lekin yozishda ko'p nusxani yangilash kerak).
6-mashq yechimi¶
(a) A (Atomicity): A'dan yechish va B'ga qo'shish β ikkalasi birga, yoki hech biri; pul "yo'qolmaydi". C (Consistency): umumiy balans yig'indisi o'zgarmaydi (yaxlitlik qoidasi), balans manfiy bo'lmaydi. I (Isolation): ayni vaqtda boshqa o'tkazma shu hisoblar bilan ishlasa, ular bir-birini buzmaydi (natija ketma-ket bajarilgandek). D (Durability): commit bo'lgach, server o'chsa ham o'tkazma saqlanadi.
(b) Eventual consistency bilan: bir replika o'tkazmani ko'rgan, boshqasi hali yo'q. Foydalanuvchi ikki "kassada" bir xil pulni ikki marta sarflashi (double-spend) mumkin, yoki balans vaqtincha noto'g'ri ko'rinadi. Pul uchun lahzali mos to'g'rilik shart β bu yerda kuchli yaxlitlik (ACID) zarur, "oxir-oqibat to'g'ri bo'ladi" yetarli emas.
7-mashq yechimi¶
(a) Document modelda mahsulot hujjatiga sharhlarni (yoki oxirgi 5 tasini) joylab (embed) saqlash mumkin:
{ id: "p1", nomi: "Kitob", narx: 50000,
oxirgiSharhlar: [ {muallif:"ali", matn:"zo'r"}, ... 5 ta ... ] }
Sahifa bitta o'qish bilan tayyor β JOIN yo'q.
(b) Muammo: muallif ismi sharh ichida nusxalangan; ism o'zgarsa, ko'p joyda eski qoladi. Variant 1 (denormalizatsiyani qabul qilish): "sharh paytidagi ism" deb ataylab eski qoldirish β tarixiy to'g'ri, yangilash shart emas. Variant 2 (ID saqlash): sharhda faqat muallifId saqlab, ismni ko'rsatishda alohida olish β moslik saqlanadi, lekin "bitta o'qish" buziladi (qo'shimcha so'rov). Bu β sof denormalizatsiya trade-off'i.
8-mashq yechimi¶
- Operatsion yuk ko'payadi: har baza uchun alohida backup, monitoring, yangilash, xavfsizlik β N marta ish.
- Nosozlik nuqtalari ko'payadi: beshta baza β beshta "ishlamay qolishi mumkin" joy va ular orasidagi moslikni saqlash murakkabligi.
- Jamoa tajribasi suyuladi: har bazani chuqur bilish kerak; ko'p baza bilan jamoa hech birini yaxshi bilmay qolishi va operatsion xato qilishi mumkin. Ko'pincha "bitta yaxshi SQL + Redis" arzonroq va ishonchliroq.
9-mashq yechimi¶
Yechim 1 (API kompozitsiyasi): User servisidan foydalanuvchini, Order servisidan buyurtmalarni alohida so'rab, kodda birlashtirish. Trade-off: sodda, lekin ikki tarmoq-so'rov (sekinroq, ikkalasi ham ishlashi kerak) va "JOIN'ni qo'lda qilish".
Yechim 2 (o'qish uchun denormalizatsiyalangan ko'rinish / CQRS): hodisalar (event) orqali maxsus "o'qish modeli" tuzish β foydalanuvchi + buyurtmalarini birga saqlaydigan oldindan tayyorlangan ko'rinish. Trade-off: o'qish juda tez (bitta o'qish), lekin qo'shimcha murakkablik va bu ko'rinish eventual consistency bilan yangilanadi (vaqtincha eskirgan bo'lishi mumkin). Tanlov β so'rov chastotasi va eskirishga chidamlilikka bog'liq.
10-mashq yechimi¶
Xavf: "Google masshtabi" β ehtimol hech qachon kelmaydi (YAGNI β 06-bob: kerak bo'lmaydigan narsani oldindan qurma). Cassandra murakkab access pattern'larni (ad-hoc so'rov, JOIN, tranzaksiya) qiyinlashtiradi va operatsion jihatdan og'ir. Kichik jamoa uni noto'g'ri sozlab, sekinroq va xatoliroq tizim qurishi mumkin β masshtab muammosi hatto kelmasdan.
Maslahat: PostgreSQL bilan boshlang. U million qatorgacha (va to'g'ri sozlanganda undan ko'p) bemalol ishlaydi, jamoa uni biladi, so'rovlar erkin. Masshtab real muammo bo'lganda (o'lchab, isbotlab) β o'sha vaqt aniq qism uchun ko'chiring. "Premature scaling" β premature optimization'ning bir turi.
11-mashq yechimi¶
interface FeedItem { postId: string; author: string; text: string; }
const feeds = new Map<string, FeedItem[]>();
function publishPost(authorId: string, followers: string[], post: FeedItem): void {
for (const f of followers) { // fan-out on write
const list = feeds.get(f) ?? [];
list.unshift(post); // har follower feed'iga nusxa
feeds.set(f, list);
}
}
function getFeed(userId: string): FeedItem[] {
return feeds.get(userId) ?? []; // bitta kalit-o'qish, JOIN yo'q
}
publishPost("oqil", ["ali", "vali"], { postId: "p1", author: "oqil", text: "Salom" });
publishPost("oqil", ["ali", "vali"], { postId: "p2", author: "oqil", text: "Arxitektura" });
console.log(getFeed("ali").length); // 2
console.log(getFeed("ali")[0].text); // Arxitektura
Ishga tushirilganda (haqiqatan olingan): ali feed uzunligi: 2, ali eng yangi post: Arxitektura, hechkim feed: 0.
Yozish tomonidagi narx: har post har bir follower uchun nusxalanadi. Million obunachisi bor mashhur foydalanuvchi bitta post yozsa β million yozuv! Bu "fan-out on write" o'qishni juda tezlashtiradi, lekin mashhur hisoblar uchun yozish portlashiga olib keladi. Amaliyotda gibrid ishlatiladi: oddiy foydalanuvchilar uchun fan-out on write, mashhurlar uchun "fan-out on read" (o'qishda yig'ish). Bu β sof access-pattern trade-off'i (DDIA shu Twitter misolini batafsil yoritadi).
12-mashq yechimi¶
Tanlov: relyatsion (SQL), masalan PostgreSQL. Sabablar: (1) balans har doim to'g'ri bo'lishi shart -> kuchli yaxlitlik = ACID; (2) murakkab analitik oylik hisobotlar -> SQL'ning deklarativ, ad-hoc so'rovi ideal; (3) masshtab kichik -> gorizontal masshtablash (NoSQL'ning asosiy ustunligi) hozir kerak emas; (4) o'zgarmas tranzaksiyalar (append-only ledger) β bu SQL'da ham tabiiy (faqat INSERT).
Rad etiladigan oilalar: Key-value β analitik so'rov yo'q, ledger uchun yetarli emas. Document β ACID va murakkab JOIN-so'rov SQL'dagidek kuchli emas. Column-family β masshtab uchun mo'ljallangan, lekin ad-hoc analitika va kuchli tranzaksiyani qiyinlashtiradi; masshtab muammosi yo'q ekan, narxi foydasidan ko'p. Graph β bu yerda bog'lanish-og'ir muammo yo'q.
ACID/BASE tilida: bu ma'lumot ACID talab qiladi (ayniqsa Atomicity va Consistency); eventual consistency (BASE) pul/balans uchun qabul qilib bo'lmaydi.
13-mashq yechimi¶
Xulosa replikatsiyada buziladi. ACID-C bitta server (bitta baza nusxasi) ichida yaxlitlik qoidalari buzilmasligini kafolatlaydi β FK, CHECK, balans qoidasi. Bu bitta nusxa haqida; u boshqa nusxalar haqida hech narsa demaydi.
Misol: primary'ga yozasiz, ACID-C bajariladi (qoida buzilmadi). Lekin follower replika kechikish bilan yangilanadi β shu lahzada follower'dan o'qigan mijoz eski qiymatni ko'radi. Ya'ni replikalar mos emas (CAP-C buzildi), garchi har bir nusxada ACID-C ** to'liq saqlangan bo'lsa ham. Demak ACID-C (yaxlitlik qoidasi) va CAP-C (replikalararo moslik = linearizability) β mustaqil**; biri ikkinchisini bermaydi. CAP-C uchun maxsus mexanizm (sinxron replikatsiya, konsensus) kerak β bu mavjudlik/latency narxiga keladi (22-bob).
14-mashq yechimi¶
Namunaviy yechim: (i) profil + kurslar + to'lov β qat'iy, pul bilan bog'liq -> PostgreSQL (SQL, ACID). (ii) video sessiya holati β vaqtinchalik, tez, TTL -> Redis (key-value). (iii) kurs qidiruvi β to'liq matnli -> Elasticsearch (search-indeks). (iv) tavsiya β bog'lanish bo'ylab -> Neo4j (graph) yoki oddiy holatda SQL'dagi agregatsiya.
Soddalashtirish: PostgreSQL'ning o'zi ko'p narsani qoplaydi β JSON ustun (yarim-tuzilgan ma'lumot), tsvector to'liq-matnli qidiruv, hatto rekursiv CTE bilan oddiy graf-so'rovlar. Boshlang'ich uchun: PostgreSQL + Redis (sessiya uchun) β ikki baza yetarli. Elasticsearch va Neo4j'ni faqat qidiruv hajmi yoki tavsiya murakkabligi haqiqatan talab qilganda qo'shing. Bu β polyglot'ning to'g'ri qo'llanishi: kerak bo'lganda kengay, birdan beshta baza emas. Bu qarorni ADR (03-bob) bilan hujjatlashtiring.
Muqobil va trade-off: kimdir (iii) va (iv) ni ham PostgreSQL'da qilib, faqat bitta baza ishlatishni afzal ko'rishi mumkin β operatsion soddalik uchun qidiruv/tavsiya sifatidan biroz voz kechib. Bu ham haqli tanlov: sodlik vs maxsus-vosita kuchi trade-off'i.
β¬ οΈ Oldingi: 17 β Servislararo aloqa va API dizayni Β· π README Β· Keyingi: 19 β Masshtablash va load balancing β‘οΈ