Index nima, qanday ishlaydi, qachon kerak

~20 daq · MySQL · Index va Performance

Muammodan boshlaylik

Saytingiz ishga tushdi, hammasi tez ishlardi. Bir yildan keyin buyurtmalar jadvalida 5 million qator. Mijoz "Mening buyurtmalarim" sahifasini ochadi:

SELECT * FROM buyurtmalar WHERE mijoz_id = 48213;

Sahifa 4 soniya yuklanadi. Kod o'zgarmagan. Server o'zgarmagan. Faqat ma'lumot ko'paydi.

Sabab: MySQL 48213-mijozning buyurtmalarini topish uchun 5 million qatorning har birini ko'rib chiqyapti. Bu — full table scan. Yechim — indeks.


Kitobdagi mundarija

1000 sahifalik kitobda "rekursiya" so'zi qayerda ekanini topish kerak.

Indekssiz: 1-sahifadan boshlab har bir sahifani o'qib chiqasiz. 1000 sahifa.

Indeks bilan: kitob oxiridagi alifbo ko'rsatkichini ochasiz. "R" harfi → "rekursiya — 412, 587-betlar". Alifbo tartibida bo'lgani uchun topish bir necha soniya. Keyin to'g'ridan-to'g'ri 412-betga o'tasiz.

MySQL indeksi — aynan shu ko'rsatkich: ustun qiymatlarining tartiblangan ro'yxati va har biri jadvaldagi qayerda turganiga ishora.


Indeks yaratish

CREATE INDEX idx_buyurtmalar_mijoz ON buyurtmalar (mijoz_id);

Yoki jadval yaratishda:

CREATE TABLE buyurtmalar (
    id          INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
    mijoz_id    INT UNSIGNED NOT NULL,
    holat       VARCHAR(20) NOT NULL,
    yaratilgan  DATETIME NOT NULL,
    INDEX idx_mijoz (mijoz_id),
    INDEX idx_yaratilgan (yaratilgan)
);

Endi xuddi shu so'rov:

Indekssiz:   5 000 000 qator tekshirildi — 4.2 soniya
Indeks bilan:       12 qator tekshirildi — 0.001 soniya

Ming barobar tez. So'rovni o'zgartirish shart emas — MySQL indeksni o'zi topib ishlatadi.


Ichida nima bor: B-tree

MySQL (InnoDB) indekslari B+ tree tuzilmasida saqlanadi — muvozanatlashgan daraxt:

                    [ 500 | 1000 ]                   ← ildiz
                   /       |       \
     [100|250|400]   [600|750|900]   [1200|1500]     ← oraliq tugunlar
       /  |  |  \       ...              ...
   [1..99][100..249]...                              ← barglar: qiymat → qator manzili

mijoz_id = 750 ni qidirish:

  1. Ildiz: 750 — 500 va 1000 orasida → o'rta shoxga
  2. Oraliq: 750 topildi → bargga
  3. Barg: 750 → qatorlar manzili

3-4 qadam — 5 million qator bo'lsa ham. Har bir daraja yuzlab shoxchaga bo'linadi, shuning uchun daraxt juda "keng va past". Milliard qator — taxminan 4-5 daraja.

Barglar tartiblangan va bir-biriga bog'langan — shuning uchun indeks oraliq qidiruvlarni ham tezlashtiradi:

WHERE yaratilgan >= '2026-10-01' AND yaratilgan < '2026-11-01'   -- ✅ indeks ishlaydi
ORDER BY yaratilgan                                             -- ✅ tartiblash shart emas

Clustered index — PRIMARY KEY

InnoDB da jadvalning o'zi primary key bo'yicha tartiblangan B-tree. Bu clustered index: barglarda butun qator saqlanadi.

Boshqa indekslar (secondary index) barglarida esa qatorning o'zi emas, uning primary key qiymati:

idx_mijoz:  mijoz_id=48213 → id=[1043, 2981, 5502]
                                    ↓
PRIMARY:    id=1043 → [butun qator]

Shuning uchun:

  • WHERE id = 5 — eng tez qidiruv (bitta daraxt)
  • WHERE mijoz_id = 48213 — ikki qadam: secondary indeks → primary key → qator
  • Primary key qisqa bo'lsin (INT, BIGINT) — u har bir secondary indeksda takrorlanadi

Indeksning narxi

Indeks bepul emas:

Ta'siri
SELECT ✅ Tezlashadi
INSERT ❌ Sekinlashadi — har bir indeksga yozish kerak
UPDATE (indekslangan ustun) ❌ Sekinlashadi
DELETE ❌ Sekinlashadi
Disk ❌ Qo'shimcha joy (ba'zan jadvalning o'zidan katta)
Xotira (buffer pool) ❌ Indeks sahifalari ham keshda joy egallaydi

10 ta indeksli jadvalga har bir INSERT — 11 ta B-tree ga yozish.

Qoida: har bir ustunga indeks qo'ymang. Haqiqatan qidiriladigan ustunlarga qo'ying.


Qachon indeks kerak

✅ Kerak:

  • WHERE da tez-tez ishlatiladigan ustunlar: email, holat, yaratilgan
  • JOIN ... ON dagi ustunlar — foreign key lar (MySQL avtomatik yaratadi)
  • ORDER BY dagi ustunlar (ayniqsa LIMIT bilan)
  • UNIQUE bo'lishi kerak bo'lgan ustunlar

❌ Kerak emas yoki foydasiz:

  • Kichik jadvallar (bir necha yuz qator) — to'liq ko'rib chiqish baribir tez
  • Kam qiymatli ustunlar (past selectivity) — jinsi (2 qiymat), faol (true/false). Qatorlarning yarmini indeks orqali o'qish to'liq ko'rib chiqishdan sekinroq
  • Faqat yoziladigan, deyarli o'qilmaydigan jadvallar (loglar)
  • Hech qachon WHERE da ishlatilmaydigan ustunlar

Selectivity

SELECT
    COUNT(DISTINCT email) / COUNT(*)   AS email_selectivity,    -- ~1.0 — a'lo
    COUNT(DISTINCT shahar) / COUNT(*)  AS shahar_selectivity,   -- ~0.001 — yomon
    COUNT(DISTINCT holat) / COUNT(*)   AS holat_selectivity     -- ~0.000001
FROM mijozlar;

1 ga qanchalik yaqin bo'lsa — indeks shunchalik foydali.


Indeks ishlamay qoladigan holatlar

Indeks bor, lekin MySQL undan foydalana olmaydi:

-- 1. Ustun funksiyaga o'ralgan
WHERE YEAR(yaratilgan) = 2026            -- ❌
WHERE yaratilgan >= '2026-01-01' AND yaratilgan < '2027-01-01'   -- ✅

-- 2. Ustun ustida hisob
WHERE narx * 1.12 > 1000000              -- ❌
WHERE narx > 1000000 / 1.12              -- ✅

-- 3. Boshida % bilan LIKE
WHERE nom LIKE '%phone'                  -- ❌
WHERE nom LIKE 'iPhone%'                 -- ✅

-- 4. Tur mos emas — matn ustunni son bilan solishtirish
WHERE telefon = 998901112233             -- ❌ (har bir qator songa aylantiriladi)
WHERE telefon = '998901112233'           -- ✅

Umumiy qoida: indekslangan ustun WHERE da "yalang'och" tursin.


Indekslarni ko'rish va o'chirish

SHOW INDEX FROM buyurtmalar;

DROP INDEX idx_buyurtmalar_mijoz ON buyurtmalar;

MySQL so'rovda indeksdan foydalanyaptimi — EXPLAIN bilan tekshiriladi (32-dars):

EXPLAIN SELECT * FROM buyurtmalar WHERE mijoz_id = 48213;
-- key: idx_buyurtmalar_mijoz, rows: 12   ✅
-- key: NULL, rows: 5000000, type: ALL    ❌ full table scan

Nima uchun shunday?

Nima uchun MySQL barcha ustunlarga avtomatik indeks qo'ymaydi, agar u bunchalik foydali bo'lsa?

Chunki indeksning foydasi so'rovlarga bog'liq, MySQL esa siz qanday so'rov yozishingizni oldindan bilmaydi. Har bir ustunga indeks — har bir yozishni o'nlab marta sekinlashtirish va diskni bir necha barobar to'ldirish. Indeks — almashuv: o'qish tezligi evaziga yozish tezligi va joy. Bu almashuvni faqat ilovani biladigan odam — siz — to'g'ri qila olasiz.


Xulosa

Eslab qoling:

  • Indeks — ustun qiymatlarining tartiblangan ko'rsatkichi (B+ tree)
  • Millionlab qatorda ham 3-4 qadamda topadi; oraliq va ORDER BY ni ham tezlashtiradi
  • PRIMARY KEY — clustered index: jadvalning o'zi shu tartibda
  • Secondary index → primary key → qator
  • Narxi: sekinroq INSERT/UPDATE/DELETE, qo'shimcha disk va xotira
  • WHERE, JOIN, ORDER BY dagi, selectivity yuqori ustunlarga qo'ying
  • Ustunni funksiyaga o'rasangiz yoki LIKE '%...' — indeks ishlamaydi

Keyingi dars

31-dars: Index turlari — PRIMARY, UNIQUE, COMPOSITE, FULLTEXT