Index turlari — PRIMARY, UNIQUE, COMPOSITE, FULLTEXT

~20 daq · MySQL · Index va Performance

Muammodan boshlaylik

O'tgan darsda oddiy indeksni ko'rdik. Lekin real so'rovlar murakkabroq:

-- Mijozning yetkazilmagan buyurtmalari, eng yangisi birinchi
SELECT * FROM buyurtmalar
WHERE mijoz_id = 42 AND holat = 'yangi'
ORDER BY yaratilgan DESC;

-- Mahsulot nomida va tavsifida "simsiz quloqchin" qidiruvi
SELECT * FROM mahsulotlar WHERE tavsif LIKE '%simsiz%quloqchin%';

Uchta alohida indeks yaratish (mijoz_id, holat, yaratilgan) — birinchi so'rov uchun yetarli emas. Ikkinchisi uchun oddiy indeks umuman ishlamaydi. Har bir vazifaga — o'z indeks turi.


PRIMARY KEY

id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY
  • Jadvalda bitta, NOT NULL + UNIQUE
  • InnoDB da — clustered: jadvalning o'zi shu tartibda saqlanadi (30-dars)
  • Primary key bo'lmasa, InnoDB yashirin 6 baytlik ID yaratadi — doim o'zingiz bering

Nima uchun AUTO_INCREMENT yaxshi primary key? Har yangi qator jadval oxiriga qo'shiladi — B-tree tartibli o'sadi. Tasodifiy qiymat (masalan, UUID v4) esa har safar daraxtning o'rtasiga tushadi — sahifalar bo'linadi, yozish sekinlashadi, jadval "parchalanadi". UUID kerak bo'lsa — vaqtga asoslangan UUID v7 yoki UUID_TO_BIN(UUID(), 1).


UNIQUE

CREATE UNIQUE INDEX uq_mijozlar_email ON mijozlar (email);
-- yoki
ALTER TABLE mijozlar ADD CONSTRAINT uq_email UNIQUE (email);

Ikki vazifa birdan:

  1. Cheklov — takror qiymat kira olmaydi (6-dars)
  2. Indeks — WHERE email = ? tez

Login so'rovi WHERE email = ? — UNIQUE indeks tufayli bir zumda.


COMPOSITE — bir nechta ustunli indeks

CREATE INDEX idx_buyurtma_mijoz_holat_vaqt
    ON buyurtmalar (mijoz_id, holat, yaratilgan);

Indeks qiymatlari ustunlar tartibida saralanadi — telefon kitobi kabi: avval familiya, keyin ism, keyin otasining ismi.

(mijoz_id, holat,      yaratilgan)
(42,       'bekor',    2026-03-01)
(42,       'yangi',    2026-09-10)
(42,       'yangi',    2026-10-25)   ← mijoz 42, yangi — shu yerda, vaqt bo'yicha tartiblangan
(42,       'yetkazildi', ...)
(43,       ...)

WHERE mijoz_id = 42 AND holat = 'yangi' ORDER BY yaratilgan DESC — MySQL kerakli joyga sakraydi va natijalar allaqachon tartiblangan. Alohida saralash shart emas.

Chap prefiks qoidasi

Composite indeks faqat chapdan boshlab ishlaydi:

So'rov sharti (mijoz_id, holat, yaratilgan) ishlaydimi?
mijoz_id = 42 ✅
mijoz_id = 42 AND holat = 'yangi' ✅
mijoz_id = 42 AND holat = 'yangi' AND yaratilgan > ... ✅
mijoz_id = 42 ORDER BY holat ✅
holat = 'yangi' ❌ — mijoz_id o'tkazib yuborildi
yaratilgan > '2026-10-01' ❌
mijoz_id = 42 AND yaratilgan > ... ⚠️ faqat mijoz_id qismi

Telefon kitobidan "ismi Aziz bo'lganlar"ni (familiyasiz) topib bo'lmaydi — kitob familiya bo'yicha tartiblangan.

Shuning uchun (a, b) indeks bo'lsa, alohida (a) indeks keraksiz — (a, b) uning ishini ham bajaradi.

Ustunlar tartibini tanlash

  1. Tenglik (=) shartlari — birinchi
  2. Oraliq (>, <, BETWEEN) yoki ORDER BY — oxirida
  3. Teng holatda — ko'proq so'rovlarda ishlatiladigani chapda
-- So'rov: WHERE holat = ? AND yaratilgan > ? 
CREATE INDEX idx_holat_vaqt ON buyurtmalar (holat, yaratilgan);   -- ✅
CREATE INDEX idx_vaqt_holat ON buyurtmalar (yaratilgan, holat);   -- ❌ oraliqdan keyin holat ishlamaydi

Covering index — jadvalga umuman kirmaslik

Agar so'rovga kerakli barcha ustunlar indeksda bo'lsa — MySQL jadvalning o'ziga murojaat qilmaydi:

CREATE INDEX idx_mijoz_holat ON buyurtmalar (mijoz_id, holat);

SELECT holat, COUNT(*) FROM buyurtmalar WHERE mijoz_id = 42 GROUP BY holat;

mijoz_id va holat — ikkalasi ham indeksda. EXPLAIN da Extra: Using index — eng yaxshi holat. Secondary indeks → primary key → qator degan ikkinchi qadam yo'q.

SELECT * esa covering index imkoniyatini yo'qotadi — yana bir sabab ustunlarni sanab yozish uchun.


Prefix index — uzun matnlar uchun

CREATE INDEX idx_url ON sahifalar (url(100));

Uzun VARCHAR / TEXT ning faqat birinchi 100 belgisi indekslanadi. TEXT ustunlarga oddiy indeks qo'yishning yagona yo'li.


Functional index (MySQL 8.0.13+)

30-darsda: WHERE LOWER(email) = ? indeksni ishlatmaydi. MySQL 8 da ifodaning o'zini indekslash mumkin:

CREATE INDEX idx_email_lower ON mijozlar ((LOWER(email)));   -- ikki qavat qavs!

SELECT * FROM mijozlar WHERE LOWER(email) = 'aziz@mail.uz';   -- ✅ endi indeks ishlaydi

CREATE INDEX idx_yil ON buyurtmalar ((YEAR(yaratilgan)));

So'rovdagi ifoda indeksdagi bilan aynan mos bo'lishi kerak.


FULLTEXT — matn bo'yicha qidiruv

LIKE '%so'z%' — sekin (indeks ishlamaydi) va aqlsiz ("quloqchin" so'rasangiz "quloqchinlar" ni topmaydi, natijalarni muvofiqlik bo'yicha saralamaydi).

ALTER TABLE mahsulotlar ADD COLUMN tavsif TEXT;
CREATE FULLTEXT INDEX ft_mahsulot ON mahsulotlar (nom, tavsif);

Natural language mode

SELECT nom, MATCH(nom, tavsif) AGAINST ('simsiz quloqchin') AS muvofiqlik
FROM mahsulotlar
WHERE MATCH(nom, tavsif) AGAINST ('simsiz quloqchin')
ORDER BY muvofiqlik DESC;

So'zlarni alohida topadi va muvofiqlik balli bo'yicha saralaydi — ko'proq mos keladigani birinchi.

Boolean mode

SELECT nom FROM mahsulotlar
WHERE MATCH(nom, tavsif) AGAINST ('+quloqchin -simli' IN BOOLEAN MODE);
Operator Ma'nosi
+so'z Albatta bo'lsin
-so'z Bo'lmasin
so'z* Shu bilan boshlansin: quloq* → quloqchin, quloqlar
"ibora" Aynan shu ibora

Cheklovlar

  • Standart holatda 3 belgidan qisqa so'zlar indekslanmaydi (innodb_ft_min_token_size)
  • "Stop words" (the, and, is...) e'tiborsiz — inglizcha ro'yxat
  • O'zbek tili morfologiyasini tushunmaydi: "kitob" va "kitoblarim" — har xil so'z (kitob* yordam beradi)
  • Juda katta va murakkab qidiruv uchun — Elasticsearch, Meilisearch, Typesense

SPATIAL — geografik ma'lumot

CREATE TABLE dokonlar (
    id   INT PRIMARY KEY,
    nom  VARCHAR(100),
    joy  POINT NOT NULL SRID 4326,
    SPATIAL INDEX (joy)
);

"Menga eng yaqin 5 ta filial" kabi so'rovlar uchun. Yetkazib berish va xarita ilovalarida.


Indekslar ro'yxati

Tur Nima uchun
PRIMARY KEY Qatorning noyob ID si; clustered
UNIQUE Takrorlanmaslik + tez qidiruv
INDEX Oddiy tezlashtirish
Composite Bir nechta ustunli shartlar va ORDER BY
Covering Jadvalga kirmasdan javob
Prefix Uzun matnlar
Functional LOWER(email), YEAR(sana) kabi ifodalar
FULLTEXT So'z bo'yicha matn qidiruvi
SPATIAL Geografik koordinatalar

Nima uchun shunday?

Nima uchun composite indeksda ustunlar tartibi bunchalik muhim?

Chunki B-tree — bir o'lchovli tartiblangan ro'yxat. (mijoz_id, holat) — avval mijoz_id bo'yicha, bir xil mijoz_id ichida — holat bo'yicha. Barcha "yangi" buyurtmalar ro'yxat bo'ylab tarqalib ketgan: har bir mijozning bo'lagida bittadan. Ularni bir joydan olib bo'lmaydi. Ikki o'lchovli qidiruvni bir o'lchovli tuzilmada bajarish uchun — ustunlar tartibini so'rovga moslab tanlash kerak.


Xulosa

Eslab qoling:

  • PRIMARY KEY — clustered, qisqa va ketma-ket (AUTO_INCREMENT)
  • UNIQUE — cheklov + indeks
  • Composite (a, b, c) — chap prefiks qoidasi: a, a+b, a+b+c ishlaydi; b yolg'iz — yo'q
  • Tartib: tenglik shartlari chapda, oraliq / ORDER BY o'ngda
  • Covering index — kerakli hamma ustun indeksda → Using index
  • Functional index: ((LOWER(email)))
  • FULLTEXT: MATCH ... AGAINST, boolean mode +, -, *
  • (a, b) bor bo'lsa, (a) keraksiz

Keyingi dars

32-dars: EXPLAIN va EXPLAIN ANALYZE — so'rovni tahlil qilish