IlmHamroh
JavaScript Full-stack/27-qism. Ma'lumotlar bazasi chuqur: Postgres, ORM, Mongo, Redis55/65-dars14 daqiqa
Mundarija (23)

Redis Hash, List, Set va Sorted Set: har vazifa uchun tayyor tuzilma

Qisqacha: Redis'da satrdan tashqari to'rtta asosiy tur bor. Hash — bitta kalit ichida maydonlar (obyekt kabi: HSET vz:vazifa:7 matn … versiya 1). List — tartibli ro'yxat, ikki uchidan qo'shish/olish (LPUSH + LTRIM — "oxirgi 3 ta hodisa"). Set — takrorsiz to'plam (SADD — noyob tashrifchilar, SINTER — kesishma). Sorted Set — har elementning bali bor va doim tartiblangan (ZINCRBY + ZRANGE … REV — reyting jadvali). Har tur o'z vazifasini bitta buyruq bilan, server ichida bajaradi — ma'lumotni Node'ga olib kelib saralash shart emas.

Bu darsda

  • Vazifani hash'da maydonlari bilan saqlaysiz va bitta maydonni o'zgartirasiz.
  • List bilan "oxirgi N ta" lentasini yasaysiz.
  • Set bilan noyob tashrifchilarni sanaysiz va kunlarni solishtirasiz.
  • Sorted Set bilan reyting jadvali (leaderboard) qurasiz.
  • Qaysi vazifaga qaysi tur mosligini tanlay olasiz.

Oldin bilishingiz kerak: String, TTL va kalit nomlash, Set.

Oldingi darsda bitta kalit — bitta satr bilan ishladik va kalitlarni vz:obyekt:id shaklida nomladik. Endi qiymatning o'zi murakkab tuzilma bo'ladi.

1. Nega bu kerak?

vazifalar ilovasida uchta yangi talab paydo bo'ldi:

  1. Har foydalanuvchining oxirgi 3 ta harakati ("Non olish yaratildi", "Hisobot bajarildi") bosh sahifada.
  2. Kuniga nechta noyob foydalanuvchi kirdi — bir odam 10 marta kirsa ham, bitta.
  3. Haftaning eng faol foydalanuvchilari reytingi.

Faqat satrlar bilan buni qilish mumkin: JSON massivni o'qib, Node'da o'zgartirib, qayta yozish. Lekin har safar butun massiv tarmoqdan ikki marta o'tadi. Ikki server bir vaqtda yozsa, birining o'zgarishi yo'qoladi — "o'qi-o'zgartir-yoz" poygasi (String, TTL va kalit nomlash darsida INCR bilan ko'rganmiz).

Redis'ning maxsus turlari bu amallarni server ichida, bitta buyruq bilan bajaradi. Har buyruq atomik — poyga yo'q.

O'xshatish: String — bitta quti. Hash — tortmali shkaf. List — navbat. Set — mehmonlar ro'yxati (bir odam ikki marta yozilmaydi). Sorted Set — musobaqa jadvali, ballar bilan.

flowchart TD
  S{Nima saqlaysiz?} --> A[Bitta qiymat yoki hisob]
  S --> B[Maydonli obyekt]
  S --> C[Tartibli ketma-ketlik]
  S --> D[Takrorsiz elementlar]
  S --> E[Ball bo'yicha tartib]
  A --> A1[String]
  B --> B1[Hash]
  C --> C1[List]
  D --> D1[Set]
  E --> E1[Sorted Set]

Diagrammaga qarang: tanlov "ma'lumot qanday ko'rinishda" savolidan boshlanadi. Hamma chiqishlar — kanon redis-cli tajribasidan (Redis 8.10.2).

2. Hash — maydonli obyekt

2.1 HSET, HGET, HGETALL

text
127.0.0.1:6379> HSET vz:vazifa:7 matn Non olish #uy bajarildi 0 versiya 1
3
127.0.0.1:6379> HGET vz:vazifa:7 matn
Non olish #uy
127.0.0.1:6379> HGETALL vz:vazifa:7
matn
Non olish #uy
bajarildi
0
versiya
1

Kanon "Non olish #uy" ni qo'shtirnoq bilan bergan. Hash — bitta kalit ichida maydon → qiymat juftlari, JavaScript obyektiga o'xshash.

  • HSET kalit maydon qiymat [maydon qiymat …] — qaytargan son: nechta yangi maydon qo'shildi (3). Mavjud maydonni yangilasa — sanalmaydi.
  • HGET — bitta maydon. HMGET — bir nechtasi.
  • HGETALL — hamma juftlar, ketma-ket: maydon, qiymat, maydon, qiymat… node-redis buni obyektga yig'ib beradi.

2.2 Bitta maydonni o'zgartirish

text
127.0.0.1:6379> HINCRBY vz:vazifa:7 versiya 1
2
127.0.0.1:6379> HDEL vz:vazifa:7 bajarildi
1

Satrda JSON saqlasangiz, versiya ni oshirish uchun butun obyektni o'qib, parse qilib, qayta yozish kerak. Hash'da — HINCRBY, bitta atomik buyruq. HDEL — maydonni o'chiradi.

Hash qiymatlari ham satr: HGETALL dan keyin versiya — "2", son emas. Ichki obyektlar yo'q — hash bir darajali. Chuqur JSON kerak bo'lsa — Redis 8 dagi JSON turi (Maxsus tuzilmalar).

2.3 Maydon muddati: HEXPIRE

Oldin TTL faqat butun kalitga qo'yilardi. Redis 7.4 dan (Redis 8 da bor) alohida maydonga ham muddat berish mumkin:

text
127.0.0.1:6379> HEXPIRE vz:vazifa:7 60 FIELDS 1 matn
1
127.0.0.1:6379> HTTL vz:vazifa:7 FIELDS 2 matn versiya
60
-1

matn maydoni 60 soniyada o'zi o'chadi, versiya — abadiy (-1). FIELDS 2 — keyin nechta maydon nomi kelishini bildiradi. Bu, masalan, foydalanuvchi hash'idagi vaqtinchalik tasdiqlash kodiga qulay. 7.4 dan eski Redis'da bu buyruqlar yo'q; Valkey yoki boshqa Redis-mos serverda bor-yo'qligini o'sha versiya hujjatidan tekshiring (kanon faqat Redis 8.10.2 da sinadi).

Node'da:

js
await redis.hSet("vz:vazifa:7", {
  matn: "Non olish #uy",
  bajarildi: "0",
  versiya: "1",
});
const v = await redis.hGetAll("vz:vazifa:7");
// v — oddiy obyekt: { matn: '…', bajarildi: '0', versiya: '1' }
await redis.hIncrBy("vz:vazifa:7", "versiya", 1);

Tekshirib ko'ring: HSET vz:vazifa:7 versiya 5 ni bersangiz, javob nima bo'ladi? versiya maydoni allaqachon bor.

Javob

0. HSET faqat yangi qo'shilgan maydonlarni sanaydi. Qiymat baribir 5 ga o'zgaradi — lekin javobda bu ko'rinmaydi.

3. List — tartibli ro'yxat

3.1 LPUSH va LRANGE

text
127.0.0.1:6379> LPUSH vz:faollik:1 yaratdi:7 bajardi:3 izoh:5
3
127.0.0.1:6379> LRANGE vz:faollik:1 0 -1
izoh:5
bajardi:3
yaratdi:7

List — elementlar qo'shilgan tartibda saqlanadigan ro'yxat. LPUSH (Left PUSH) — chap tomonga (boshiga) qo'shadi. Uchta element ketma-ket boshga qo'shildi, shuning uchun oxirgisi (izoh:5) birinchi turibdi — eng yangi hodisa tepada. RPUSH — o'ng tomonga (oxiriga).

LRANGE kalit 0 -1 — boshidan oxirigacha. Indekslar 0 dan; manfiy — oxiridan (-1 — oxirgi). Xuddi JavaScript'dagi at(-1) kabi.

3.2 LTRIM — "oxirgi N ta"

text
127.0.0.1:6379> LPUSH vz:faollik:1 yaratdi:8
4
127.0.0.1:6379> LTRIM vz:faollik:1 0 2
OK
127.0.0.1:6379> LRANGE vz:faollik:1 0 -1
yaratdi:8
izoh:5
bajardi:3
127.0.0.1:6379> RPOP vz:faollik:1
bajardi:3

Yangi hodisa qo'shildi (4 ta bo'ldi), keyin LTRIM 0 2 — faqat 0–2 indekslarni qoldirib, qolganini kesdi. yaratdi:7 (eng eski) chiqib ketdi. LPUSH + LTRIM — qat'iy uzunlikdagi lenta: ro'yxat hech qachon 3 tadan oshmaydi, xotira cheklangan.

RPOP — o'ng uchidan bitta olib, o'chiradi. LPUSH + RPOP — oddiy navbat (FIFO, Queue va deque). Lekin ishonchli navbat uchun bu yetmaydi: ishchi elementni olib yiqilsa, element yo'qoladi. Shuning uchun navbatlar Streams (XREADGROUP + XACK) yoki BullMQ ustida quriladi (Pub/Sub va Redis Streams).

Diqqat: LRANGE uchlari — tez, o'rtasi — sekin. List ichida bog'langan ro'yxat (yoki kichik bo'lsa ixcham massiv) bor: 1 000 000 elementli ro'yxatning o'rtasidagi LINDEX hamma elementlarni sanab boradi. "N-element" kerak bo'lsa — boshqa tur.

4. Set — takrorsiz to'plam

text
127.0.0.1:6379> SADD vz:tashrif:2026-10-25 u1 u2 u3 u1
3
127.0.0.1:6379> SCARD vz:tashrif:2026-10-25
3
127.0.0.1:6379> SISMEMBER vz:tashrif:2026-10-25 u2
1
127.0.0.1:6379> SADD vz:tashrif:2026-10-24 u2 u4
2
127.0.0.1:6379> SINTER vz:tashrif:2026-10-25 vz:tashrif:2026-10-24
u2
127.0.0.1:6379> SUNIONSTORE vz:tashrif:hafta vz:tashrif:2026-10-25 vz:tashrif:2026-10-24
4

Set — har element bir martagina bo'ladigan to'plam (JavaScript'dagi Set kabi, Set).

  • SADD — 4 ta berdik (u1 ikki marta), 3 tasi qo'shildi. Takror o'zi tashlandi.
  • SCARD — elementlar soni: kunlik noyob tashrifchilar.
  • SISMEMBER — "u2 bugun kirganmi?" — 1 (ha). Tezligi to'plam kattaligiga bog'liq emas.
  • SINTER — kesishma: ikki kunda ham kirganlar — faqat u2. Bu "qaytib kelgan foydalanuvchilar" (retention) hisobining asosi.
  • SUNIONSTORE — birlashmani yangi kalitga yozadi: hafta davomida 4 ta noyob odam. SDIFF — farq ham bor.

Set'ning narxi — xotira: har element to'liq saqlanadi. 100 000 noyob foydalanuvchi uchun kanon Set'da 3 961 846 bayt (≈ 3.8 MB) o'lchadi. Agar faqat son kerak bo'lsa (kim ekani emas), HyperLogLog 14 KB bilan ishlaydi — Maxsus tuzilmalar darsida.

5. Sorted Set — ball bilan tartiblangan to'plam

5.1 Reyting jadvali

Sorted Set (ZSET) — Set kabi takrorsiz, lekin har elementning bali (score) bor va elementlar doim ball bo'yicha tartiblangan turadi.

text
127.0.0.1:6379> ZADD vz:reyting 12 Aziz 9 Dilnoza 15 Sardor
3
127.0.0.1:6379> ZINCRBY vz:reyting 5 Dilnoza
14
127.0.0.1:6379> ZRANGE vz:reyting 0 -1 REV WITHSCORES
Sardor
15
Dilnoza
14
Aziz
12
  • ZADD kalit ball element … — ball avval keladi. Bu haftada bajarilgan vazifalar soni: Aziz 12, Dilnoza 9, Sardor 15.
  • ZINCRBY — Dilnoza yana 5 ta vazifa bajardi, bali 14 bo'ldi. Atomik — ikki server bir vaqtda oshirsa ham ikkala oshirish saqlanadi.
  • ZRANGE … REV WITHSCORES — kattadan kichikka, ballari bilan. Dilnoza Aziz'dan oldinga o'tdi — qayta saralash shart bo'lmadi: tartibni Redis yozish paytida o'zi saqlaydi.

5.2 O'rin, ball va oraliq

text
127.0.0.1:6379> ZREVRANK vz:reyting Aziz
2
127.0.0.1:6379> ZSCORE vz:reyting Sardor
15
127.0.0.1:6379> ZRANGE vz:reyting 10 14 BYSCORE WITHSCORES
Aziz
12
Dilnoza
14
  • ZREVRANK — kattadan sanaganda nechanchi o'rinda (0 dan): Aziz — 2, ya'ni uchinchi. "Siz reytingda 3-o'rindasiz" — bitta buyruq, million o'yinchida ham tez (hujjat: O(log N)).
  • ZSCORE — elementning bali.
  • ZRANGE … BYSCORE — ball oralig'i bo'yicha: 10 dan 14 gacha (ikkala chegara ham kiradi).

Ballar teng bo'lsa, Redis elementlarni nomi bo'yicha (bayt tartibida) joylaydi — reytingdagi "teng o'rin" muammosini shunda hisobga oling.

Sorted Set'ning boshqa ishlatilishi:

  • Vaqt bo'yicha indeks — ball sifatida vaqt (ms): "oxirgi 1 soatdagi hodisalar" = ZRANGE … BYSCORE. Sirpanuvchi oyna rate limit shunday quriladi (Distributed lock va rate limit).
  • Ustuvor navbat — ball = muhimlik (Heap).
  • Kechiktirilgan ishlar — ball = bajarilish vaqti.

Node'da:

js
await redis.zAdd("vz:reyting", [
  { score: 12, value: "Aziz" },
  { score: 9, value: "Dilnoza" },
]);
await redis.zIncrBy("vz:reyting", 5, "Dilnoza");
const top = await redis.zRangeWithScores("vz:reyting", 0, 2, {
  REV: true,
});
// top — [{ value, score }, …] massivi

Tekshirib ko'ring: Haftalik reyting kerak — har dushanba yangidan. Kalitni qanday nomlaysiz va eski haftalar xotirada qolib ketmasligi uchun nima qilasiz?

Javob

Kalitga hafta qo'shiladi: vz:reyting:2026-W43. Birinchi ZINCRBY dan keyin EXPIRE vz:reyting:2026-W43 1209600 NX (14 kun, faqat muddat yo'q bo'lsa) — o'tgan haftani yana bir hafta ko'rsatish mumkin, keyin kalit o'zi o'chadi. Bitta kalitni "har dushanba tozalash" esa cron va poyga xavfini qo'shadi.

6. Ichki kodlash va xotira

text
127.0.0.1:6379> OBJECT ENCODING vz:reyting
listpack

Kichik Sorted Set (bu yerda 3 element) Redis ichida listpack — ixcham ketma-ket massiv sifatida saqlanadi. Element soni yoki uzunligi chegaradan oshsa (sozlamalar: zset-max-listpack-entries, sukut 128), Redis uni avtomatik "skip list + hash" tuzilmasiga o'tkazadi. Hash, List va Set'da ham shunday kichik/katta kodlash bor.

Amaliy xulosa: kichik to'plamlar juda tejamkor. Shuning uchun "bitta ulkan hash" o'rniga "ko'p kichik hash" (vz:vazifa:7, vz:vazifa:8) ko'pincha xotirada arzonroq. Ichki kodlash nomi versiyaga bog'liq — kodda unga tayanmang, faqat xotirani tushunish uchun ishlating.

Tur Vazifa Asosiy buyruqlar
String qiymat, hisoblagich, qulf SET, GET, INCR
Hash maydonli obyekt HSET, HGETALL, HINCRBY
List lenta, oddiy navbat LPUSH, LTRIM, LRANGE
Set noyob, kesishma SADD, SCARD, SINTER
Sorted Set reyting, vaqt indeksi ZADD, ZINCRBY, ZRANGE

7. Ko'p uchraydigan xatolar

Belgi Sabab Davosi
WRONGTYPE Operation against a key … bir kalitga turli tur buyrug'i har tur — o'z kaliti
Lenta cheksiz o'sadi LPUSH bor, LTRIM yo'q har qo'shishdan keyin LTRIM 0 N-1
Hash'dan versiya + 1 → "21" qiymatlar satr HINCRBY yoki Number()
Reytingda tartib teskari ZRANGE sukut — kichikdan REV
ZADD da ball va element almashib qolgan tartib: avval ball ZADD kalit 12 Aziz
Noyob hisob uchun xotira to'ldi 100 000 lab element Set'da faqat son kerak bo'lsa — HyperLogLog
Navbatdagi ishlar yo'qoladi RPOP dan keyin ishchi yiqildi Streams yoki BullMQ

8. Mashqlar

Mashqlar kurs/mashqlar/27/55-tuzilmalar/ da.

1-mashq (oson): qaysi tur?

Har vazifaga Redis turini yozing:

  • Foydalanuvchi profili (ism, til, mavzu) —
  • Bugun kirgan noyob foydalanuvchilar ro'yxati —
  • Eng ko'p ko'rilgan 10 ta vazifa —
  • Oxirgi 20 ta bildirishnoma —
  • Sahifa ko'rishlar hisoblagichi —
Yechim
  • Hash — maydonlarni alohida o'zgartirish mumkin (HSET … til uz).
  • Set — takror o'zi tashlanadi, SCARD — soni.
  • Sorted Set — ball = ko'rishlar, ZINCRBY + ZRANGE … REV.
  • List — LPUSH + LTRIM 0 19.
  • String — INCR.

2-mashq (o'rta): lenta

Redis'dagi LPUSH + LTRIM ni massiv bilan takrorlang. lentagaQosh(lenta, maks, ...hodisalar) — hodisalarni LPUSH kabi ketma-ket boshiga qo'shib, keyin uzunlikni maks gacha kessin va yangi uzunlikni qaytarsin (massivni joyida o'zgartiradi). Kanon ketma-ketligini takrorlang: yaratdi:7, bajardi:3, izoh:5 (maks 3), keyin yaratdi:8. Har qadamdan keyin lentani chiqaring.

Yechim
js
function lentagaQosh(lenta, maks, ...hodisalar) {
  for (const h of hodisalar) {
    lenta.unshift(h); // LPUSH — har biri boshiga
  }
  lenta.length = Math.min(lenta.length, maks); // LTRIM 0 maks-1
  return lenta.length;
}

const lenta = [];
lentagaQosh(lenta, 3, "yaratdi:7", "bajardi:3", "izoh:5");
console.log(lenta.join(" "));
lentagaQosh(lenta, 3, "yaratdi:8");
console.log(lenta.join(" "));

Konsolda:

text
izoh:5 bajardi:3 yaratdi:7
yaratdi:8 izoh:5 bajardi:3

Natija kanon LRANGE chiqishi bilan bir xil. Diqqat: LPUSH bir nechta elementni birma-bir boshiga qo'yadi, shuning uchun oxirgi berilgan (izoh:5) birinchi turadi. unshift("a","b","c") esa massivni a, b, c tartibida qo'shardi — Redis'dan farqli. Shu sabab siklda bittalab qo'shdik. length ni kichraytirish — massivni kesish (Element qo'shish va olib tashlash).

3-mashq (qiyin): reyting

Reyting klassini yozing (Sorted Set'ning soddalashtirilgan nusxasi): qosh(ball, nom) (ZADD), oshir(nom, n) (ZINCRBY, yangi ballni qaytaradi), teskariTop(n) (ZRANGE 0 n-1 REV WITHSCORES — [nom, ball] juftlari), teskariOrin(nom) (ZREVRANK, yo'q bo'lsa null). Ballar teng bo'lsa — Redis'dagidek nom bo'yicha tartib (REV'da teskari). Kanon ketma-ketligini takrorlang va qo'shimcha sinang: Bobur 14 ball bilan qo'shilsa, tartib qanday bo'ladi?

Yechim
js
class Reyting {
  #ballar = new Map();
  qosh(ball, nom) {
    this.#ballar.set(nom, ball);
  }
  oshir(nom, n) {
    const yangi = (this.#ballar.get(nom) ?? 0) + n;
    this.#ballar.set(nom, yangi);
    return yangi;
  }
  #teskari() {
    return [...this.#ballar].sort(([a, x], [b, y]) =>
      y - x || (a < b ? 1 : a > b ? -1 : 0),
    );
  }
  teskariTop(n) {
    return this.#teskari().slice(0, n);
  }
  teskariOrin(nom) {
    const i = this.#teskari().findIndex(([m]) => m === nom);
    return i === -1 ? null : i;
  }
}

const r = new Reyting();
r.qosh(12, "Aziz");
r.qosh(9, "Dilnoza");
r.qosh(15, "Sardor");
console.log(r.oshir("Dilnoza", 5));
console.log(r.teskariTop(3));
console.log(r.teskariOrin("Aziz"), r.teskariOrin("Malika"));
r.qosh(14, "Bobur");
console.log(r.teskariTop(4).map(([m]) => m).join(" > "));

Konsolda:

text
14
[ [ 'Sardor', 15 ], [ 'Dilnoza', 14 ], [ 'Aziz', 12 ] ]
2 null
Sardor > Dilnoza > Bobur > Aziz

Bobur va Dilnoza teng (14). O'sish tartibida nom bo'yicha Bobur < Dilnoza; REV uni teskari qiladi — Dilnoza oldinda. Bizning versiyamiz har so'rovda butun ro'yxatni saralaydi (O(n log n)). Redis esa tartibni yozishda saqlaydi, shuning uchun ZREVRANK million elementda ham tez (O(log N)). localeCompare emas, </> ishlatdik: Redis teng ballarni bayt tartibida solishtiradi, til qoidalari bilan emas (sort va taqqoslash funksiyasi).

9. Loyiha qadami: kanon tajribasi, loyihaga hali kirmaydi

vazifalar-api ga bu darsda kod qo'shilmaydi. Faollik lentasi va reyting — ilovada hali yo'q funksiyalar; ular uchun avval auth (28-qism) kerak: "kimning" lentasi ekanini server bilishi lozim. O'zingiz sinab ko'ring: redis-cli da o'z ismingiz bilan haftalik reyting yarating (vz:reyting:2026-W43), ZINCRBY bilan uch kun "bajarilgan vazifalar"ni qo'shing va ZREVRANK bilan o'rningizni toping. Oxirida EXPIRE … NX qo'yishni unutmang.

10. Real ishda

  • O'yin va ta'lim platformalari — Sorted Set reyting jadvallari (haftalik, oylik — kalitda sana).
  • Ijtimoiy lentalar — List yoki Sorted Set (ball = vaqt) bilan "oxirgi N ta".
  • Analitika — Set bilan kunlik noyob foydalanuvchilar, SINTER bilan retention.
  • Savatcha — Hash: HINCRBY savat:<id> <mahsulot> 1.
  • Intervyuda: "Leaderboard'ni qanday qurasiz?", "List va Sorted Set farqi?", "Nega HINCRBY JSON'ni yangilashdan yaxshiroq?"

Xulosa

  • Hash — maydonli obyekt; HINCRBY — bitta maydonni atomik o'zgartirish; Redis 7.4+ da maydon TTL (HEXPIRE).
  • List — tartibli ro'yxat; LPUSH + LTRIM — qat'iy uzunlikdagi lenta.
  • Set — takrorsiz to'plam; SCARD, SISMEMBER, SINTER/SUNIONSTORE.
  • Sorted Set — ball bilan tartib; ZINCRBY, ZRANGE … REV WITHSCORES, ZREVRANK.
  • Har amal server ichida va atomik — "o'qi-o'zgartir-yoz" poygasi yo'q.
  • Kichik tuzilmalar ixcham kodlanadi (listpack) — ko'p kichik kalit ko'pincha arzonroq.

Keyingi dars: Maxsus tuzilmalar — Bitmap, HyperLogLog (100 000 noyobni 14 KB da sanash), Geo, JSON, Bloom filtr va vektorlar.

Manbalar

Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
Redis Hash, List, Set va Sorted Set: har vazifa uchun tayyor tuzilma — IlmHamroh