Mundarija (32)
- Bu darsda
- 1. Nega bu kerak?
- 2. Sharding asoslari
- 2.1 Atamalar
- 2.2 Partitioning, replika va sharding
- 2.3 Shard kalitini tanlash
- 3. Ilova darajasida sharding: kanon tajribasi
- 3.1 Tajriba sxemasi
- 3.2 Kod
- 3.3 Natija
- 3.4 Taqsimotni brauzerda takrorlaymiz
- 3.5 Natijani halol o'qish
- 4. Shard qo'shish: hash % N va consistent hashing
- 4.1 hash % N muammosi
- 4.2 Consistent hashing
- 4.3 Virtual tugunlar va muvozanat
- 5. Shardlararo muammolar
- 5.1 Noyoblik faqat shard ichida
- 5.2 ID to'qnashuvi
- 5.3 Shardlararo tranzaksiya
- 5.4 Hisobotlar
- 6. Taqsimlangan SQL bazalari
- 7. Sharding'ga hali ertami?
- 8. Ko'p uchraydigan xatolar
- 9. Mashqlar
- 1-mashq (oson): bo'shliqlarni to'ldiring
- 2-mashq (o'rta): mantiqiy shardlar
- 3-mashq (qiyin): scatter-gather bilan o'rtacha
- 10. Loyiha qadami: kanon tajribasi
- 11. Real ishda
- Xulosa
- Manbalar
Sharding va taqsimlangan SQL: ma'lumotni serverlarga bo'lish
Qisqacha: Sharding (bo'laklash) — ma'lumotni bir nechta mustaqil bazaga (shardga) bo'lish: har foydalanuvchining vazifalari faqat bitta shardda turadi. Yozish va hajm serverlar orasida bo'linadi, lekin narxi katta: kalitsiz so'rov hamma shardga boradi, noyoblik faqat shard ichida ishlaydi, yangi shard qo'shilganda ma'lumot ko'chadi. Kanon o'lchovida 2 → 3 shardga o'tishda oddiy
hash % Nkalitlarning 66.7 % ini ko'chirdi, consistent hashing — 33.7 % ini. Xulosa:vazifalaruchun sharding hozircha kerak emas — bu oxirgi chora.
Bu darsda
- Sharding partitioning va replikadan nimasi bilan farq qilishini tushuntirasiz.
- Shard kalitini tanlash mezonlarini bilasiz.
- Ilova darajasidagi sharding'ni kanon tajribasi orqali ko'rasiz: marshrutlash va scatter-gather.
hash % Nva consistent hashing'ni brauzerda modellab, ko'chadigan kalitlarni sanaysiz.- Shardlararo cheklovlar (noyoblik, ID, tranzaksiya) va taqsimlangan SQL bazalarini bilasiz.
- "Sharding'ga hali ertami?" degan savolga o'lchov bilan javob berasiz.
Oldin bilishingiz kerak: Partitioning, Replikatsiya va read replica'lar, Hash table ichidan, crypto 1: tasodif, hash va HMAC.
Oldingi darsda katta jadvalni bitta server ichida bo'limlarga bo'ldik. Endi bir qadam nariga o'tamiz: bo'limlar boshqa-boshqa serverlarda tursa nima bo'ladi?
1. Nega bu kerak?
Tasavvur qiling: vazifalar-api juda mashhur bo'ldi. 50 mln foydalanuvchi, sekundiga 30 000 yozuv. Siz hamma narsani qildingiz: indekslar, ulanish hovuzi, o'qish uchun replikalar, katta jadvallar bo'limlarga bo'lingan. Lekin baribir:
- Yozish bitta primary'da. Replika yozishni tezlashtirmaydi — har yozuv baribir primary'dan o'tadi (Replikatsiya darsidagi xulosa).
- Hajm bitta serverning diskiga, xotirasiga sig'maydi.
- Eng katta serverni sotib oldingiz (vertikal masshtablash) — keyingi qadam yo'q.
Shunda ma'lumotni bir nechta serverga bo'lish kerak bo'ladi. O'xshatish: katta shahar pochtasi. Bitta bo'lim butun shahar xatlarini ko'tara olmaydi. Shahar tumanlarga bo'linadi, har tumanning o'z pochta bo'limi bor. Xat qaysi bo'limga borishini manzildagi indeks hal qiladi. Lekin "shahardagi hamma xatlarni sanang" degan topshiriq endi har bo'limni aylanib chiqishni talab qiladi.
2. Sharding asoslari
2.1 Atamalar
- Sharding (bo'laklash) — ma'lumotni bir nechta mustaqil bazaga bo'lish. Bu so'zni Playwright II: auth, tarmoq, trace va CI darsidan eslarsiz — u yerda e2e testlar mashinalarga bo'laklangan edi, g'oya bir xil.
- Shard — shu bo'laklardan biri: o'z serveri, o'z diski, o'z indekslari bor to'liq Postgres bazasi. Inglizcha so'zning o'zi "siniq parcha" degani: butun idish sinib, har parcha alohida yotadi.
- Shard kaliti (shard key) — qator qaysi shardga tegishli ekanini hal qiladigan ustun.
vazifalaruchun tabiiy tanlov —egasi_id. - Marshrutlash (routing) — so'rovni to'g'ri shardga yuborish. Buni kim qiladi: ilova o'zi, alohida proksi yoki bazaning o'zi.
2.2 Partitioning, replika va sharding
| Partitioning | Replika | Sharding | |
|---|---|---|---|
| Nima bo'linadi | jadval, bitta server ichida | hech nima — to'liq nusxa | ma'lumot, serverlar orasida |
| Yozishni tezlatadimi | qisman | yo'q | ha |
| Hajm chegarasi | bitta disk | bitta disk | ko'p disk |
| Murakkablik | past | o'rta | yuqori |
Partitioning — bitta bazaning ichki ishi: so'rov, tranzaksiya, cheklovlar hamon bitta Postgres'da. Sharding'da esa har shard alohida baza. Ular bir-birini bilmaydi.
2.3 Shard kalitini tanlash
Yaxshi shard kaliti uch shartga javob beradi:
- Ko'p so'rovda bor. "Azizning vazifalari" —
egasi_idbilan; so'rov bitta shardga boradi. - Tekis taqsimlanadi. Bitta qiymat butun yukni olmasin. Agar shard kaliti
viloyatbo'lsa, Toshkent shardi qolganlaridan bir necha baravar og'ir bo'ladi — bu issiq nuqta (hot spot). - O'zgarmaydi. Kalit o'zgarsa, qatorni boshqa shardga ko'chirish kerak.
egasi_id uchalasiga javob beradi: foydalanuvchi faqat o'z vazifalarini ko'radi, foydalanuvchilar ko'p, ID o'zgarmaydi. Yomon tanlov — yaratilgan_vaqt: hamma yangi yozuv oxirgi shardga tushadi, qolganlari bo'sh turadi.
Tekshirib ko'ring: Telegram bot uchun xabarlar jadvali. Shard kaliti
chat_idmi yokixabar_idmi?
Javob
chat_id. Eng ko'p so'rov — "shu chatning oxirgi 50 xabari": chat_id bilan bu bitta shard. xabar_id bo'yicha bo'linsa, bitta chat xabarlari hamma shardga sochiladi va har chat ochilishi scatter-gather bo'ladi. Xavfi: juda katta guruh chati issiq nuqtaga aylanishi mumkin — buni alohida hal qilishadi (masalan, katta chatni alohida shardga).
3. Ilova darajasida sharding: kanon tajribasi
3.1 Tajriba sxemasi
Kanon eng oddiy variantni sinadi: sharding'ni ilovaning o'zi qiladi. Ikki shard — shard_a va shard_b. Muhim cheklov: ikkalasi ham bitta Postgres serveri ichidagi ikki alohida baza. Haqiqiy alohida serverlar emas — tarmoq kechikishi yo'q. Bu tajriba tezlikni emas, mantiqni va so'rovlar sonini ko'rsatadi.
flowchart LR
I["Ilova"] --> M{"sha256(egasi_id) % 2"}
M -->|"0"| A[("shard_a")]
M -->|"1"| B[("shard_b")]
I -.->|"kalitsiz so'rov"| A
I -.->|"kalitsiz so'rov"| BUzluksiz strelkalar — oddiy yo'l: ilova egasi_id xeshini hisoblaydi va bitta shardni tanlaydi. Uzuq strelkalar — shard kaliti yo'q so'rov: ilova ikkala shardga boradi va natijalarni o'zi birlashtiradi.
3.2 Kod
Kanon skriptining yadrosi (qatorlar telefon uchun bo'lindi, CREATE DATABASE qismi olib tashlandi):
import pg from "pg";
import { createHash } from "node:crypto";
const SHARDLAR = ["shard_a", "shard_b"];
const pool = Object.fromEntries(SHARDLAR.map((b) => [b,
new pg.Pool({
connectionString: `postgres://postgres@127.0.0.1:5496/${b}`,
max: 4,
})]));
const h32 = (s) =>
createHash("sha256").update(String(s)).digest().readUInt32BE(0);
const shardMod = (egasi, n = SHARDLAR.length) =>
SHARDLAR[h32(egasi) % n];
for (const p of Object.values(pool)) {
await p.query(`CREATE TABLE vazifalar (
id bigint GENERATED ALWAYS AS IDENTITY, egasi_id bigint NOT NULL,
matn text NOT NULL, PRIMARY KEY (id), UNIQUE (egasi_id, matn))`);
}
for (let e = 1; e <= 200; e++) {
const p = pool[shardMod(e)];
await p.query(
"INSERT INTO vazifalar (egasi_id, matn) " +
"SELECT $1, 'vazifa ' || g FROM generate_series(1, 50) g",
[e],
);
}h32 — SHA-256 xeshning birinchi 4 baytini butun songa aylantiradi. shardMod — shu sonning shardlar soniga bo'linishidan qolgan qoldiq. Har egasining 50 ta vazifasi faqat uning shardiga yoziladi. Har shardda jadval bir xil: shard — to'liq baza, faqat ma'lumotning bir qismi bilan. Bo'lingan nusxani biz o'z PostgreSQL 17.6 bazamizda ikki baza bilan ishga tushirdik — taqsimot kanondagidek chiqdi: 4 450 va 5 550 qator.
Keyin ikki xil so'rov o'lchandi — shard kaliti bilan va kalitsiz:
// shard kaliti bor: bitta shardga
await pool[shardMod(e)].query(
"SELECT count(*) FROM vazifalar WHERE egasi_id = $1", [e]);
// kalitsiz (scatter-gather): hamma shardga parallel
await Promise.all(SHARDLAR.map((b) => pool[b].query(
"SELECT count(*) FROM vazifalar WHERE matn = 'vazifa 7'")));Scatter-gather (sochib-yig'ish) — so'rovni hamma shardga yuborish va javoblarni ilovada birlashtirish. Har biri 200 marta takrorlandi.
3.3 Natija
1) 200 egasi × 50 vazifa yozildi (1528 ms); taqsimot: shard_a=4450, shard_b=5550
bitta egasi so'rovi (shard kaliti bor) × 200: 1.54 ms/so'rov, 1 shard
shard kalitisiz so'rov (scatter-gather, 2 shard parallel) × 200: 2.21 ms/so'rov — har so'rov 2 ta bazagaIkki xulosa. Birinchisi — taqsimot: 4 450 ga 5 550, ya'ni 89 va 111 egasi. Xesh "tekis" deyiladi, lekin bu katta sonlarda. 200 egasida 45 % ga 55 % bo'ldi. Ikkinchisi — kalitsiz so'rov har safar ikkala bazaga bordi. 2 ta shardda bu sezilmaydi. 20 ta shardda har bunday so'rov 20 ta bazani band qiladi, eng sekin shard javobini kutadi.
3.4 Taqsimotni brauzerda takrorlaymiz
Kanon node:crypto ishlatgan. Brauzerda xuddi shu SHA-256 — crypto.subtle.digest (Web Crypto g'oyasi). Kodni ishga tushiring — natija kanon bilan bir xil chiqishi kerak:
async function h32(s) {
const bayt = new TextEncoder().encode(String(s));
const xesh = await crypto.subtle.digest("SHA-256", bayt);
return new DataView(xesh).getUint32(0);
}
const SHARDLAR = ["shard_a", "shard_b"];
const sanoq = { shard_a: 0, shard_b: 0 };
for (let egasi = 1; egasi <= 200; egasi++) {
const shard = SHARDLAR[(await h32(egasi)) % SHARDLAR.length];
sanoq[shard]++;
}
console.log(sanoq);
console.log("vazifalar:", sanoq.shard_a * 50, sanoq.shard_b * 50);Konsolda:
{ shard_a: 89, shard_b: 111 }
vazifalar: 4450 5550getUint32(0) — readUInt32BE(0) ning brauzerdagi juftligi: dastlabki 4 baytni katta tartibda (big-endian) o'qiydi. Xesh bir xil, demak taqsimot ham bir xil: 4 450 va 5 550.
3.5 Natijani halol o'qish
1.54 va 2.21 ms ni solishtirmang. Ikki so'rov boshqa-boshqa ish qiladi. Birinchisi egasi_id bo'yicha qidiradi — UNIQUE (egasi_id, matn) indeksi bor. Ikkinchisi matn = 'vazifa 7' bo'yicha — faqat matn ga indeks yo'q, har shardda butun jadval o'qiladi. Ya'ni vaqt farqining bir qismi — scatter-gather'dan emas, indeks yo'qligidan. To'g'ri solishtirish uchun bir xil so'rovni bir va ikki shardga yuborish kerak edi.
Tarmoq yo'q. Ikkala "shard" bitta serverda. Haqiqiy shardlar alohida mashinalarda bo'lsa, har qo'shimcha shard qo'shimcha tarmoq aylanishi (round trip) degani.
Tajribadan oladigan aniq fakt — so'rovlar soni: kalit bilan 1 ta baza, kalitsiz 2 ta. Bu raqam shardlar soni bilan birga o'sadi.
4. Shard qo'shish: hash % N va consistent hashing
4.1 hash % N muammosi
Ikki shard to'ldi, uchinchisini qo'shamiz. shardMod(egasi, 3) endi boshqa javob beradi. Qancha egasi boshqa shardga ko'chishi kerak?
async function h32(s) {
const bayt = new TextEncoder().encode(String(s));
const xesh = await crypto.subtle.digest("SHA-256", bayt);
return new DataView(xesh).getUint32(0);
}
const N = 10000;
let kochdi = 0;
for (let egasi = 1; egasi <= N; egasi++) {
const h = await h32(egasi);
if (h % 2 !== h % 3) kochdi++;
}
console.log(`hash % N, 2 → 3: ${(100 * kochdi / N).toFixed(1)} %`);Konsolda:
hash % N, 2 → 3: 66.7 %Kanon ham aynan shu raqamni oldi: 66.7 %. Uchta egasidan ikkitasining ma'lumoti boshqa serverga ko'chishi kerak. Sabab matematik: xesh 2 ga va 3 ga bo'linganda bir xil qoldiq berishi — taxminan uchdan bir holatda. Ko'chish paytida ilova qaysi egasi qayerda ekanini bilmay qoladi — bu eng xavfli payt.
4.2 Consistent hashing
Consistent hashing (izchil xeshlash) — shardlarni va kalitlarni bitta "halqaga" joylash usuli. Halqa — 0 dan 2³² gacha sonlar, oxiri boshiga ulangan. Har shard halqada bir nechta nuqtani egallaydi. Kalit xeshidan boshlab soat yo'nalishida yuramiz — birinchi uchragan shard nuqtasi kalitning egasi.
flowchart LR
K["egasi 42: xesh = 1.9 mlrd"] --> Q["halqada keyingi nuqta"]
Q --> A["a#17: 2.0 mlrd"]
A --> S[("shard a")]
Y["yangi shard c"] -.->|"faqat o'z nuqtalari oldidagi kalitlarni oladi"| QYangi shard qo'shilsa, u halqaga o'z nuqtalarini qo'yadi. Faqat shu nuqtalardan oldingi kalitlar unga o'tadi, qolganlari joyida qoladi. Bitta shard halqada faqat bitta nuqtada bo'lsa, oraliqlar notekis chiqadi. Shuning uchun har shard ko'p virtual tugun (virtual node) bilan qo'yiladi: a#0, a#1, … a#99.
Kanon algoritmi — 100 virtual tugun bilan:
async function h32(s) {
const bayt = new TextEncoder().encode(String(s));
const xesh = await crypto.subtle.digest("SHA-256", bayt);
return new DataView(xesh).getUint32(0);
}
async function halqa(tugunlar, v = 100) {
const nuqtalar = [];
for (const n of tugunlar) {
for (let i = 0; i < v; i++) {
nuqtalar.push({ n, h: await h32(`${n}#${i}`) });
}
}
return nuqtalar.sort((a, b) => a.h - b.h);
}
function egasi(halqaNuqtalari, h) {
const nuqta = halqaNuqtalari.find((x) => x.h >= h);
return (nuqta ?? halqaNuqtalari[0]).n;
}
const r2 = await halqa(["a", "b"]);
const r3 = await halqa(["a", "b", "c"]);
let kochdi = 0;
for (let e = 1; e <= 10000; e++) {
const h = await h32(e);
if (egasi(r2, h) !== egasi(r3, h)) kochdi++;
}
console.log(`consistent hashing: ${(kochdi / 100).toFixed(1)} %`);Konsolda:
consistent hashing: 33.7 %Kanon natijasi bilan aynan bir xil: 33.7 %. Ideal — 33.3 %: uchta shard bo'lsa, yangisi har biridan teng ulush olishi kerak, ya'ni hamma kalitlarning uchdan biri. Consistent hashing idealga juda yaqin keldi va hash % N dan ikki baravar kam ko'chirdi.
nuqta ?? halqaNuqtalari[0] qatori — halqaning "ulanishi": xesh eng katta nuqtadan ham katta bo'lsa, birinchi nuqtaga qaytamiz.
- hash % N66,7 %
- consistent hashing (100 virtual tugun)33,7 %
- ideal33,3 %
Manba: IlmHamroh kanon o'lchovi, 2026-11-03: 10 000 egasi, sha256 xeshining dastlabki 4 bayti; brauzer modelida biz ham shu natijani oldik (Node 24.21.0)
4.3 Virtual tugunlar va muvozanat
Virtual tugunlar soni muvozanatga qanday ta'sir qiladi? 10 000 egasini uchta shardga taqsimlab ko'ramiz:
async function h32(s) {
const bayt = new TextEncoder().encode(String(s));
const xesh = await crypto.subtle.digest("SHA-256", bayt);
return new DataView(xesh).getUint32(0);
}
async function halqa(tugunlar, v) {
const nuqtalar = [];
for (const n of tugunlar) {
for (let i = 0; i < v; i++) {
nuqtalar.push({ n, h: await h32(`${n}#${i}`) });
}
}
return nuqtalar.sort((a, b) => a.h - b.h);
}
const xeshlar = [];
for (let e = 1; e <= 10000; e++) xeshlar.push(await h32(e));
for (const v of [1, 10, 100]) {
const r = await halqa(["a", "b", "c"], v);
const sanoq = { a: 0, b: 0, c: 0 };
for (const h of xeshlar) {
sanoq[(r.find((x) => x.h >= h) ?? r[0]).n]++;
}
console.log(`v=${v}:`, sanoq);
}Konsolda:
v=1: { a: 5585, b: 4050, c: 365 }
v=10: { a: 3492, b: 3076, c: 3432 }
v=100: { a: 3850, b: 2778, c: 3372 }Bitta nuqta bilan (v=1) uchinchi shard deyarli bo'sh qoldi: 365 ta egasi, 4 % ga yaqin. 10 va 100 virtual tugun bilan taqsimot ancha tekislandi. Lekin e'tibor bering: bu yugurishda 10 tugun 100 tadan ham tekisroq chiqdi. Virtual tugunlar o'rtacha muvozanatni yaxshilaydi, har aniq holatda kafolat bermaydi. Real tizimlarda shuning uchun shard yukini kuzatib, kerak bo'lsa qo'lda qayta muvozanatlashadi.
Tekshirib ko'ring: Consistent hashing bilan ham uchinchi shard qo'shilganda 33.7 % kalit ko'chadi. Bu qayerdan "yutuq"?
Javob
Uchinchi shard hamma ma'lumotning uchdan birini olishi kerak — aks holda u bo'sh turadi. Shuning uchun 33.3 % — ko'chirishning eng kam chegarasi. Undan qochib bo'lmaydi. hash % N esa 66.7 % ko'chiradi: kerakli uchdan birdan tashqari, eski ikki shard o'rtasida ham keraksiz almashinuv bo'ladi (a dan b ga, b dan a ga). Consistent hashing faqat keraklisini ko'chiradi.
5. Shardlararo muammolar
5.1 Noyoblik faqat shard ichida
Kanon uchinchi sinovda marshrut xatosini modelladi: egasi 1 ning vazifasini ('vazifa 1') noto'g'ri shardga yozdi.
3) UNIQUE (egasi_id, matn) noto'g'ri shardga yozuvda: global nusxalar soni = 2 (cheklov faqat shard ichida — marshrut xatosi jim o'tadi)Ikkala shardda ham UNIQUE (egasi_id, matn) bor. Lekin har shard faqat o'zini tekshiradi. Natija: bir xil vazifa ikki nusxada, hech qanday xato yo'q. Partitioning'da Postgres buni hech bo'lmasa xato bilan rad etardi (Partitioning darsidagi must include all partitioning columns). Sharding'da baza hatto bilmaydi. Tashqi kalit (FK) ham xuddi shunday: boshqa sharddagi qatorga FK qo'yib bo'lmaydi.
5.2 ID to'qnashuvi
Har shardda GENERATED ALWAYS AS IDENTITY — har biri o'z hisoblagichi bilan. Ikki "shard"ni bitta bazadagi ikki sxema bilan modellaymiz (biz ishga tushirdik, PostgreSQL 17.6):
CREATE SCHEMA shard_a;
CREATE SCHEMA shard_b;
CREATE TABLE shard_a.vazifalar (
id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
egasi_id bigint NOT NULL, matn text NOT NULL);
CREATE TABLE shard_b.vazifalar (LIKE shard_a.vazifalar
INCLUDING ALL);
INSERT INTO shard_a.vazifalar (egasi_id, matn)
VALUES (2, 'Non olish');
INSERT INTO shard_b.vazifalar (egasi_id, matn)
VALUES (5, 'Dars tayyorlash');
SELECT 'shard_a' AS shard, id, egasi_id FROM shard_a.vazifalar
UNION ALL
SELECT 'shard_b', id, egasi_id FROM shard_b.vazifalar;CREATE SCHEMA
CREATE SCHEMA
CREATE TABLE
CREATE TABLE
INSERT 0 1
INSERT 0 1
shard | id | egasi_id
---------+----+----------
shard_a | 1 | 2
shard_b | 1 | 5
(2 rows)Ikki xil vazifa, ikkalasining ham id si 1. /v1/vazifalar/1 qaysi biri? Yechimlar uchta. ID'ga shard raqamini qo'shish yoki har shardga alohida oraliq berish. Yoki markaziy hisoblagichsiz noyob ID: UUID (ayniqsa vaqt tartibli UUID v7). Yana bir yo'l — Twitter'ning Snowflake usuli: vaqt + mashina raqami + tartib raqami. Primary key tanlovi darsidagi UUID va son kalit bahsi sharding'da yangi ma'no oladi.
5.3 Shardlararo tranzaksiya
Aziz o'z vazifasini Malikaga o'tkazmoqchi. Aziz shard_a da, Malika shard_b da. BEGIN ... COMMIT faqat bitta baza ichida ishlaydi. Ikki bazada bir vaqtda yoki ikkalasida, yoki hech birida o'zgarish bo'lishi uchun ikki fazali commit (two-phase commit, 2PC) kerak. Postgres'da buning uchun PREPARE TRANSACTION va COMMIT PREPARED bor. Hujjat ularni oddiy ilovada ishlatishni tavsiya etmaydi — tranzaksiya menejeri uchun mo'ljallangan (sinalmagan, 2026-11). Amalda shardlararo amallar ko'pincha Transactional outbox va kompensatsiya bilan qilinadi: biri muvaffaqiyatli, ikkinchisi yiqilsa — birinchisini qaytaradigan amal.
5.4 Hisobotlar
"Bu oy jami nechta vazifa bajarildi?" — hamma shardga so'rov, keyin ilovada yig'indi. count va sum ni oddiy qo'shish mumkin. O'rtacha (avg) esa yo'q: har shard sum va count ni alohida qaytarishi kerak. Mediana va DISTINCT yanada qiyin. Shuning uchun katta tizimlarda hisobotlar alohida analitik bazada qilinadi: shardlardan ma'lumot u yerga CDC bilan oqib keladi.
6. Taqsimlangan SQL bazalari
Ilova darajasidagi sharding'da hamma og'irlik ilovada: marshrutlash, scatter-gather, ko'chirish. Buni bazaning o'ziga yuklaydigan tizimlar bor. Quyidagilar rasmiy hujjat bo'yicha (sinalmagan, 2026-11):
| Tizim | G'oya |
|---|---|
| Citus | Postgres kengaytmasi: jadval "taqsimlangan" deb e'lon qilinadi, koordinator so'rovni shardlarga bo'lib yuboradi |
| CockroachDB | Postgres protokoliga mos, ma'lumotni avtomatik oraliqlarga bo'lib tarqatadi, taqsimlangan tranzaksiyalar |
| YugabyteDB | Postgres so'rov qatlami (PostgreSQL 15 kodi asosida) + taqsimlangan saqlash |
| Vitess | MySQL uchun sharding qatlami (YouTube'da paydo bo'lgan) |
Citus'da jadval bitta funksiya bilan taqsimlanadi: SELECT create_distributed_table('vazifalar', 'egasi_id');. Shard kalitini tanlash qoidasi o'zgarmaydi — bu tizimlar ham kalitsiz so'rovni hamma tugunga yuboradi. CockroachDB va YugabyteDB "Postgres'ga mos" deyiladi, lekin hammasi ham emas: ba'zi kengaytmalar va xususiyatlar yo'q. Ko'chishdan oldin o'z sxemangizni sinab ko'ring. Kanon ulardan hech birini sinamadi: har biri alohida klaster va sozlash talab qiladi.
7. Sharding'ga hali ertami?
Sharding — qaytarib bo'lmaydigan murakkablik. Har so'rov, har migratsiya, har hisobot endi "qaysi shardda?" degan savol bilan yoziladi. Shuning uchun undan oldin hamma arzon yo'llar sinaladi:
| Qadam | Nima beradi | Kursda |
|---|---|---|
| Indeks va so'rovni tuzatish | ko'pincha 10–100 baravar | EXPLAIN ANALYZE |
| Ulanish hovuzi | ko'p ulanish | Connection pooling |
| Kesh | o'qish yuki kamayadi | Caching naqshlari |
| Read replica | o'qish bo'linadi | Replikatsiya |
| Partitioning | katta jadval, tozalash | Partitioning |
| Kattaroq server | hamma narsa | — |
| Sharding | yozish va hajm bo'linadi | shu dars |
Kanon xulosasi: vazifalar uchun sharding kerak emas. Bitta yaxshi sozlangan Postgres va replika millionlab foydalanuvchini ko'taradi. Vazifalar ro'yxati — kichik yozuvlar, foydalanuvchi faqat o'zinikini ko'radi. Sharding kerak bo'lsa, shard kaliti tayyor — egasi_id. 28-qismda qo'yilgan RLS ham har so'rovni aynan shu ustun bilan cheklaydi.
8. Ko'p uchraydigan xatolar
| Belgi | Sabab | Davosi |
|---|---|---|
| Bitta shard qizib ketgan, qolganlari bo'sh | shard kaliti notekis (viloyat, vaqt) | kalitni o'zgartirish, katta qiymatni bo'lish |
| Har so'rov hamma shardga boradi | so'rovlarda shard kaliti yo'q | kalitni so'rov yo'liga qo'shish |
| Bir xil yozuv ikki shardda | marshrut xatosi, cheklov faqat shard ichida | marshrutni bitta joyda saqlash, global tekshiruv |
| Ikki xil yozuvning ID'si bir xil | har shardda alohida hisoblagich | UUID v7, Snowflake, oraliqlar |
| Yangi shard qo'shganda hamma narsa ko'chdi | hash % N |
consistent hashing yoki oldindan ko'p mantiqiy shard |
Oxirgi qatordagi "oldindan ko'p mantiqiy shard" — keng tarqalgan usul: boshidanoq, masalan, 256 ta mantiqiy shard qilinadi va ular 2 ta serverga taqsimlanadi. Server qo'shilganda butun mantiqiy shardlar ko'chadi, kalitlar qayta xeshlanmaydi.
9. Mashqlar
Mashqlar kurs/mashqlar/31/06-sharding/ da. 2- va 3-mashq yechimlari Node 24.21.0 da tekshirilgan (biz ishga tushirdik).
1-mashq (oson): bo'shliqlarni to'ldiring
- Ma'lumotni bir nechta mustaqil bazaga bo'lish —
vazifalaruchun tabiiy shard kaliti —- So'rovni hamma shardga yuborib, natijani ilovada yig'ish —
- Kanon o'lchovida
hash % Nbilan 2 → 3 shardda ko'chgan kalitlar — % - Halqadagi har shardning ko'p nuqtalari — virtual
Yechim
- sharding — bo'laklash; har bo'lak — to'liq baza.
- egasi_id — so'rovlarda bor, tekis, o'zgarmaydi.
- scatter-gather — kalitsiz so'rovning narxi.
- 66.7 — consistent hashing bilan 33.7.
- tugun — virtual tugunlar taqsimotni tekislaydi.
2-mashq (o'rta): mantiqiy shardlar
256 ta mantiqiy shard bor: mantiqiy = xesh % 256. Ular jismoniy serverlarga xarita orqali taqsimlanadi. Avval 2 server: 0–127 — pg1, 128–255 — pg2. Uchinchi server qo'shildi: pg2 dagi 192–255 oraliq pg3 ga o'tdi. server(xesh, xarita) funksiyasini yozing: xarita — { dan, gacha, server } oraliqlar massivi. 10 000 kalit uchun (xesh sifatida k * 2654435761 % 2**32 — oddiy ko'paytirma xesh) nechta kalit server almashtirganini sanang. Ishora: find (Massivda qidirish va tekshirish).
Yechim
const ikki = [
{ dan: 0, gacha: 127, server: "pg1" },
{ dan: 128, gacha: 255, server: "pg2" },
];
const uch = [
{ dan: 0, gacha: 127, server: "pg1" },
{ dan: 128, gacha: 191, server: "pg2" },
{ dan: 192, gacha: 255, server: "pg3" },
];
function server(xesh, xarita) {
const m = xesh % 256;
return xarita.find((o) => m >= o.dan && m <= o.gacha).server;
}
let kochdi = 0;
const yangi = { pg1: 0, pg2: 0, pg3: 0 };
for (let k = 1; k <= 10000; k++) {
const xesh = (k * 2654435761) % 2 ** 32;
const s = server(xesh, uch);
yangi[s]++;
if (server(xesh, ikki) !== s) kochdi++;
}
console.log("ko'chdi:", kochdi);
console.log(yangi);Konsolda:
ko'chdi: 2500
{ pg1: 5000, pg2: 2500, pg3: 2500 }Faqat pg3 ga o'tgan kalitlar ko'chdi — taxminan chorak qism (64/256). pg1 umuman tegilmadi. Bu sxemada muvozanat ideal emas (pg1 yarmini ushlab turibdi), lekin keyin pg1 ning ham bir qismini pg3 ga berish mumkin — faqat xaritani o'zgartirib. Kalitlar qayta xeshlanmaydi, butun mantiqiy shardlar ko'chadi.
3-mashq (qiyin): scatter-gather bilan o'rtacha
Uchta shard "bu oy bajarilgan vazifalar"ning o'rtacha bajarilish vaqtini (daqiqada) hisoblashi kerak. Har shard o'z natijasini beradi. notogriOrtacha(natijalar) — har shard o'rtachasining o'rtachasi (xato usul). togriOrtacha(natijalar) — har shard { soni, yigindi } qaytaradi, ilova jami yig'indini jami soniga bo'ladi. Ikkalasini quyidagi ma'lumot bilan solishtiring va farqni tushuntiring. Ishora: reduce (reduce).
Yechim
const shardlar = [
{ soni: 1000, yigindi: 30000 },
{ soni: 10, yigindi: 1200 },
{ soni: 90, yigindi: 3600 },
];
const notogriOrtacha = (n) =>
n.reduce((s, x) => s + x.yigindi / x.soni, 0) / n.length;
function togriOrtacha(n) {
const jami = n.reduce(
(s, x) => ({
soni: s.soni + x.soni,
yigindi: s.yigindi + x.yigindi,
}),
{ soni: 0, yigindi: 0 },
);
return jami.yigindi / jami.soni;
}
console.log("noto'g'ri:", notogriOrtacha(shardlar).toFixed(1));
console.log("to'g'ri:", togriOrtacha(shardlar).toFixed(1));Konsolda:
noto'g'ri: 63.3
to'g'ri: 31.6Ikkinchi shardda atigi 10 ta vazifa, lekin o'rtachasi katta (120 daqiqa). O'rtachalarning o'rtachasida u birinchi shard bilan teng og'irlikka ega bo'lib qoldi va natijani deyarli ikki baravar oshirdi. To'g'ri usul — shardlardan "qo'shiladigan" qiymatlarni (soni, yigindi) olish. Citus kabi tizimlar ham avg ni ichida aynan shunday hisoblaydi: shardlardan yig'indi va sonni oladi, bo'lishni oxirida bajaradi.
10. Loyiha qadami: kanon tajribasi
Bu darsda vazifalar-api o'zgarmaydi va commit yo'q. Sabab — «Sharding'ga hali ertami?» bo'limidagi xulosa: loyiha bitta lokal bazada ishlaydi, foydalanuvchilar soni sharding'ni oqlamaydi. Sharding — infratuzilma va arxitektura qarori, uni hech kim "bir kunda" qo'shmaydi.
Kanon tajribasi: bitta Postgres 18.6 ichida ikki baza (shard_a, shard_b), 200 egasi × 50 vazifa, shard kaliti bilan va kalitsiz so'rovlar, 10 000 kalit uchun hash % N va consistent hashing, noto'g'ri shardga yozuv. Oxirida ikkala baza o'chirildi. Citus, CockroachDB, YugabyteDB va Vitess — sinalmagan.
O'zingiz takrorlaydigan qism Docker'siz. Darsdagi brauzer modellari kanon raqamlarini aynan qaytaradi — 89/111, 66.7 %, 33.7 %. Uchinchi modelga to'rtinchi shard d ni qo'shing (["a", "b", "c", "d"]) va 3 → 4 o'tishda qancha kalit ko'chishini hisoblang. Natijani oldindan taxmin qiling (ideal — chorak qism), keyin ishga tushiring.
11. Real ishda
- Qayerda uchraydi: juda katta platformalar — messenjerlar, ijtimoiy tarmoqlar, to'lov tizimlari. Instagram foydalanuvchi bo'yicha Postgres sharding'i, YouTube — Vitess bilan MySQL sharding'i haqida ochiq yozishgan. O'zbekistondagi o'rta loyihalarning aksariyatiga bitta Postgres + replika yetadi.
- Intervyu savollari: "Sharding va partitioning farqi?", "Shard kalitini qanday tanlaysiz?", "Consistent hashing nima uchun kerak?", "Shardlar orasida unique va tranzaksiyani qanday hal qilasiz?", "Sharding'dan oldin nimalarni sinab ko'rasiz?"
- Tizim dizayni intervyusida: "Twitter/Telegram'ni loyihalang" savolida sharding deyarli doim chiqadi — shard kaliti va issiq nuqta haqida gapirishga tayyor bo'ling.
- Kod review'da: "bu so'rovda
egasi_idyo'q — sharding bo'lsa hamma shardga boradi", "bu ID ketma-ket son — shardlarda to'qnashadi".
Xulosa
- Sharding — ma'lumotni mustaqil bazalarga bo'lish; yozish va hajmni bo'ladi, lekin har so'rovga "qaysi shardda?" savolini qo'shadi.
- Shard kaliti — ko'p so'rovda bor, tekis, o'zgarmas;
vazifalaruchun —egasi_id. - Kalitsiz so'rov — scatter-gather: hamma shardga boradi.
hash % Nshard qo'shganda 66.7 % kalitni ko'chirdi, consistent hashing — 33.7 % (ideal 33.3 %).- Noyoblik, FK, ID va tranzaksiya — faqat shard ichida; ularni ilova hal qiladi.
- Sharding — oxirgi chora: indeks, hovuz, kesh, replika va partitioning'dan keyin.
Keyingi dars: Ma'lumotlar bazasi xavfsizligi — bazaga ulanishni TLS bilan shifrlaymiz, ustunni pgcrypto bilan yashiramiz va ilova rolining huquqlarini tekshiramiz.
Manbalar
- PostgreSQL 18 hujjati — PREPARE TRANSACTION (ikki fazali commit): https://www.postgresql.org/docs/18/sql-prepare-transaction.html
- Citus hujjati — Choosing Distribution Column: https://docs.citusdata.com/en/stable/sharding/data_modeling.html (sinalmagan, 2026-11)
- CockroachDB hujjati — PostgreSQL Compatibility: https://docs.cockroachlabs.com/docs/stable/postgresql-compatibility (sinalmagan, 2026-11)
- YugabyteDB hujjati — YSQL: https://docs.yugabyte.com/stable/api/ysql/ (sinalmagan, 2026-11)
- Vitess hujjati — Sharding: https://vitess.io/docs/reference/features/sharding/ (sinalmagan, 2026-11)
- Karger va boshq., "Consistent Hashing and Random Trees" (1997): https://dl.acm.org/doi/10.1145/258533.258660
- Kanon o'lchovi (IlmHamroh, 2026-11-03): PostgreSQL 18.6 (bitta konteyner, ikki baza), pg 8.23.1, Node 24.21.0. Brauzer modellari, SQL bloki va mashq yechimlari — biz ishga tushirdik (2026-11-03, PostgreSQL 17.6, Node 24.21.0).
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!