Asosiy qismga o'tish

Index va performance

11-bob 4 kod bloki 2537 so'z

20 ta row li table da har qanday query bir zumda bajariladi, shuning uchun bu qo'llanmada hali hech narsa sekin bo'lmagan. 50 million row li table da esa o'sha query bitta narsaga qarab 3 millisekund yoki 3 daqiqa ishlashi mumkin: database row larni hammasini o'qimasdan topa oladimi yoki yo'q.

Talabalaringiz kasb sifatida database tuning bilan shug'ullanmaydi. Lekin ular bir kun kelib hech qachon tugamaydigan query yozadi va nega bunday bo'layotganini bilishi kerak — hamda yechim odatda bitta qator ekanini.

Nega query sekin ishlaydi

Sabablar aslida bir necha xil, va ularni boshidanoq nomlab olish foydali:

  1. Full table scan — database har bir row ni o'qiydi, chunki siz so'ragan row larni topishning tezroq yo'li yo'q.
  2. Juda ko'p data qaytarish — keng table da SELECT *, yoki o'rganish paytida LIMIT siz ishlash.
  3. JOIN dagi fan-out — oraliq natija ikkala kirishdan ham ancha katta (5-bob).
  4. Ulkan natijani saralash yoki deduplikatsiya qilish — millionlab row ustida ORDER BY, DISTINCT, UNION.
  5. Filtrlanayotgan column ga o'ralgan function — bu mavjud index ni o'chiradi.

1-raqam eng kattasi, va index — uning davosi.

Index aslida qanday ishlaydi

🧩 Kerak bo'lgan yagona o'xshatish

Index siz table — oxirida alifbo ko'rsatkichi yo'q kitob. "Regression" so'zi uchraydigan hamma joyni topish uchun 600 betning hammasini o'qiysiz. Database index i esa o'sha alifbo ko'rsatkichi: "regression → 42, 87, 310-betlar" deb turuvchi saralangan tuzilma. So'zni bir necha qadamda topib, to'g'ridan-to'g'ri o'sha betlarga sakraysiz.

Va u kitob ko'rsatkichi qancha tursa, shuncha turadi: qo'shimcha betlar (disk joyi) va kitob o'zgarganda uni qaytadan chop etish kerak (yozish sekinlashadi).

Texnik jihatdan ko'p index lar — B-tree, ya'ni saralangan holda saqlanadigan muvozanatli daraxt. Amaliy natijasi esa eng muhimi:

Nega bu kichik farq emasFull table scan : O(n) — 50 000 000 row o'qiladi Index lookup : O(log n) — taxminan 26 qadam 50 million row da bu taxminan 2 000 000× kam ish degani.

Index saralangan bo'lgani uchun u tenglikdan tashqari narsalarni ham tezlashtiradi:

  • WHERE city = 'Tashkent' — to'g'ridan-to'g'ri o'sha yozuvga sakraydi.
  • WHERE grade BETWEEN 80 AND 90 — 80 ga sakrab, oldinga o'qib boradi.
  • ORDER BY enrolled_at — data allaqachon shu tartibda; saralash kerak emas.
  • MAX(grade) — oxirgi yozuvni o'qiydi va to'xtaydi.
  • WHERE name LIKE 'Kar%' — ma'lum boshlanish bu oraliq. '%ova' esa emas — bu 3-bobdagi qoida.

Index yaratish

python13 qator
CREATE INDEX idx_students_city ON students(city);

-- unique index: ikkita row bir xil qiymatga ega bo'lmasligini ham majburlaydi
CREATE UNIQUE INDEX idx_students_email ON students(email);

-- partial index: faqat siz so'raydigan row larni index qiladi (katta tejash)
CREATE INDEX idx_active_enrollments
    ON enrollments(student_id) WHERE status = 'active';

DROP INDEX IF EXISTS idx_students_city;

-- SQLite: mavjudlarini ko'rish
SELECT name, tbl_name, sql FROM sqlite_master WHERE type = 'index';

:::tip 💡 Bepul olinadigan narsalar PRIMARY KEY va UNIQUE constraint larning har biri avtomatik index yaratadi — unique likni tez tekshirish usuli aynan shu. Ya'ni WHERE student_id = 5 siz hech narsa qilmasdan ham tez ishlaydi.

Foreign key lar esa odatda avtomatik index qilinmaydi. Bu odamlarni hayron qoldiradi: agar siz enrollments ni students ga doim join qilsangiz, enrollments.student_id ni o'zingiz index qiling. Bu — real sxemalarda eng ko'p unutiladigan index. :::

Index qo'yishga arziydigan column lar, muhimlik tartibida:

  1. JOIN larda ishlatiladigan foreign key column lari.
  2. Katta table larda WHERE da uchraydigan column lar.
  3. Katta table larda ORDER BY da ishlatiladigan column lar.
  4. Ba'zan GROUP BY dagi column lar.

Composite index va column tartibi

Index bir nechta column ni qamrab olishi mumkin. Column larning tartibi juda muhim, va bu sevimli intervyu savoli.

CREATE INDEX idx_enr_course_status ON enrollments(course_id, status); :::analogy 🧩 Telefon kitobi qoidasi Telefon kitobi (familiya, ism) bo'yicha saralangan. "Karimov, Aziz" ni bir zumda topasiz. Karimov familiyali hammani ham bir zumda topasiz. Lekin ismi Aziz bo'lganlarning hammasini butun kitobni o'qimasdan topa olmaysiz — chunki ism faqat har bir familiya ichida saralangan. :::

(course_id, status) uchun ham aynan shu to'g'ri:

Query Index dan foydalana oladimi?
WHERE course_id = 2 AND status = 'active' ✅ To'liq
WHERE course_id = 2 ✅ Ha — eng chapdagi column
WHERE status = 'active' ❌ Yo'q — boshlovchi column tashlab ketilgan
WHERE course_id = 2 ORDER BY status ✅ Ha, va saralash bepul
📌 Eng chap prefiks qoidasi

(A, B, C) ustidagi composite index A, (A, B) va (A, B, C) bo'yicha query larga xizmat qila oladi — lekin yolg'iz B ga, yolg'iz C ga yoki (B, C) ga yo'q. Eng ko'p filtrlanadigan — va tenglik bilan tekshiriladigan — column ni birinchi qo'ying.

💡 Covering index

Agar index query ga kerak bo'lgan har bir column ni o'zida saqlasa, database javobni faqat index dan oladi va table ga umuman tegmaydi. SELECT status FROM enrollments WHERE course_id = 2 (course_id, status) bilan to'liq qoplangan. Bu SELECT * dan qochishning yana bir sababi — bitta ortiqcha column tez index-only query ni sekin query ga aylantiradi.

Qachon index qo'ymaslik kerak

Index haqida endi o'rgangan talabalar hamma narsani index qilmoqchi bo'ladi. Index lar bepul emas.

Index qo'ymang, agar… Nega
Table kichik bo'lsa (bir necha ming row) To'liq scan allaqachon tez; planner index ni baribir e'tiborsiz qoldirishi mumkin
Column da juda kam har xil qiymat bo'lsa gender ustidagi index table ning yarmiga ishora qiladi — table ni to'g'ridan-to'g'ri o'qish arzonroq
Table ga o'qishdan ko'ra ko'proq yozilsa Har bir INSERT/UPDATE/DELETE har bir index ni ham yangilashi kerak
Column bo'yicha deyarli hech qachon filtrlanmasa Sof xarajat, foydasi yo'q
Mavjud composite index allaqachon shundan boshlansa (A, B) allaqachon A ga xizmat qiladi — alohida A index i ortiqcha
⚠️ Index lar yozishni sekinlashtiradi va disk yeydi

Table da beshta index bo'lsa, bitta INSERT oltita yozuv qiladi: bittasi row uchun, beshtasi index lar uchun. Ko'p yoziladigan table da bu — ulgurish va ortda qolish orasidagi farq. Index lar real disk joyini ham egallaydi — ba'zan table ning o'zidan ko'proq.

Qoida: index ni "har ehtimolga qarshi" emas, aniq bir sekin query talab qilgani uchun qo'shing.

Sargable shartlar

Shart sargable (Search-ARGument-able) deyiladi, agar database uning uchun index dan foydalana olsa. Qoida bitta jumla: solishtirishning chap tomonida column ni toza qoldiring.

❌ Sargable emas ✅ Sargable qayta yozilishi
WHERE strftime('%Y', d) = '2025' WHERE d >= '2025-01-01' AND d < '2026-01-01'
WHERE UPPER(city) = 'TASHKENT' WHERE city = 'Tashkent' (data ni tozalang)
WHERE price * 1.1 > 1000 WHERE price > 1000 / 1.1
WHERE grade + 0 = 90 WHERE grade = 90
WHERE name LIKE '%ova' WHERE name LIKE 'Kar%' — yoki full-text search
WHERE CAST(id AS TEXT) = '5' WHERE id = 5

Agar function haqiqatan kerak bo'lsa — masalan "martda tug'ilganlar" — ko'p database lar ifodaning o'zini index qilishga ruxsat beradi:

CREATE INDEX idx_birth_month ON students(strftime('%m', birth_date)); Bu — functional (expression) index, SQLite 3.9+, PostgreSQL va boshqalar qo'llab-quvvatlaydi.

EXPLAIN QUERY PLAN

Performance haqida hech qachon taxmin qilmang. Database dan nima qilmoqchi ekanini so'rang.

python2 qator
EXPLAIN QUERY PLAN
SELECT * FROM enrollments WHERE course_id = 2;
idparentnotuseddetail
200SCAN enrollments

1 qator

1 ms

Index paydo bo'lishidan oldin:

SCAN enrollments CREATE INDEX idx_enr_course ON enrollments(course_id); dan keyin:

SEARCH enrollments USING INDEX idx_enr_course (course_id=?) :::tip 💡 Talabalarga o'rgatiladigan yagona so'z SCAN = butun table o'qilyapti. SEARCH ... USING INDEX = to'g'ridan-to'g'ri row larga sakralyapti. Kam row qaytarishi kerak bo'lgan query da katta table ustidagi SCAN — ogohlantirish belgisi. (Table ning ko'p qismi haqiqatan kerak bo'lganda — masalan to'liq hisobotda — SCAN mutlaqo normal.) :::

Join rejasi har bir table uchun bitta qator ko'rsatadi va planner tanlagan join tartibini ochib beradi:

python9 qator
EXPLAIN QUERY PLAN
SELECT s.first_name, c.title
FROM enrollments e
JOIN students s ON e.student_id = s.student_id
JOIN courses  c ON e.course_id  = c.course_id;

-- SCAN e
-- SEARCH s USING INTEGER PRIMARY KEY (rowid=?)
-- SEARCH c USING INTEGER PRIMARY KEY (rowid=?)
idparentnotuseddetail
400SCAN e
600SEARCH s USING INTEGER PRIMARY KEY (rowid=?)
900SEARCH c USING INTEGER PRIMARY KEY (rowid=?)

3 qator

0 ms

Bu — yaxshi shakl: katta table bir marta scan qilinadi, mos row lar esa primary key bo'yicha topiladi.

Database Buyruq
SQLite EXPLAIN QUERY PLAN <query>
PostgreSQL EXPLAIN ANALYZE <query> — uni haqiqatan bajaradi va real vaqtlarni ko'rsatadi
MySQL EXPLAIN <query> — type va rows column lariga qarang
SQL Server "Display Estimated Execution Plan"

JOIN larni tezlashtirish

Uchta qoida deyarli har qanday sekin join ni qamrab oladi:

  1. Join shartining ikkala tomonini ham index qiling. Primary key tomoni allaqachon index li; foreign key tomoni esa odatda yo'q. CREATE INDEX idx_enr_student ON enrollments(student_id);
  2. Join dan keyin emas, oldin filtrlang. Table ni join dan oldin 10 milliondan 1000 ta row ga tushirish 10 millionni join qilib keyin filtrlashdan ancha arzon. CTE buni aniq ko'rsatadi: WITH recent AS ( SELECT * FROM enrollments WHERE enrolled_at >= '2026-01-01' ) SELECT s.first_name, r.grade FROM recent r JOIN students s USING (student_id);
  3. Ko'p tomoni katta bo'lganda join dan oldin aggregate qiling — bu shu bilan birga 5-bobdagi fan-out xatosining ham oldini oladi. Bitta yechim, ikkita muammo hal bo'ldi.
⚠️ Type mos kelmasligi index ni indamay o'ldiradi

Agar a.id INTEGER va b.id TEXT bo'lsa, database har bir row uchun bir tomonni o'girishi kerak — va index ishlatib bo'lmaydigan bo'lib qoladi. Join baribir to'g'ri natija qaytaradi, faqat ming barobar sekinroq. Join tushunarsiz sekin ishlaganda ikkala column ning type i bir xilligini tekshiring.

Amaliy tuning ro'yxati

Query sekin bo'lsa, shu ro'yxat bo'yicha tartib bilan yuring:

  1. EXPLAIN QUERY PLAN ni ishga tushiring. Katta table da SCAN bormi?
  2. Filtrlanayotgan column index li mi? Yo'q bo'lsa, index qo'ying va rejani qayta tekshiring.
  3. Filtrlanayotgan column ga function o'ralganmi? Uni oraliq ko'rinishida qayta yozing.
  4. Join column lari bir xil type dami va foreign key tomoni index li mi?
  5. Kerak bo'lmagan column larni tanlayapsizmi? Ularni nomlab yozing; SELECT * covering index ni to'sadi.
  6. JOIN dan keyin COUNT(*) o'sdimi? Bu fan-out — avval aggregate qiling.
  7. Join dagi takroriy row larni yashirayotgan DISTINCT bormi? Buning o'rniga join ni tuzating.
  8. Ertaroq filtrlash mumkinmi — WHERE ni join dan oldin CTE ga tushiring?
  9. Hali ham sekin va query doim ishlaydimi? Jadval bo'yicha yangilanadigan xulosa table ini ko'rib chiqing.
💡 Taxmin qilmang, o'lchang

SQLite da .timer on har bir ifoda qancha vaqt olganini ko'rsatadi. PostgreSQL da EXPLAIN ANALYZE har bir qadam uchun real vaqt beradi. Bitta narsani o'zgartiring, qayta o'lchang, faqat haqiqatan yordam bergan bo'lsa qoldiring. Performance sezgisi ishonchsiz — hatto tajribali muhandislar ham muntazam noto'g'ri taxmin qiladi.

Mashqlar to'plami

python33 qator
-- 1. Index paydo bo'lishidan oldingi rejani ko'ring
EXPLAIN QUERY PLAN SELECT * FROM enrollments WHERE course_id = 2;

-- 2. Index qo'shing va yana qarang
CREATE INDEX idx_enr_course ON enrollments(course_id);
EXPLAIN QUERY PLAN SELECT * FROM enrollments WHERE course_id = 2;

-- 3. Join larda ishlatiladigan foreign key larni index qiling
CREATE INDEX idx_enr_student ON enrollments(student_id);
CREATE INDEX idx_enr_teacher ON enrollments(teacher_id);

-- 4. Composite index, keyin eng chap prefiks qoidasini sinang
CREATE INDEX idx_enr_course_status ON enrollments(course_id, status);
EXPLAIN QUERY PLAN SELECT * FROM enrollments WHERE course_id = 2 AND status = 'active';  -- ishlatadi
EXPLAIN QUERY PLAN SELECT * FROM enrollments WHERE status = 'active';                    -- ishlatmaydi

-- 5. Sargable emas va sargable, javob bir xil
EXPLAIN QUERY PLAN SELECT * FROM enrollments WHERE strftime('%Y', enrolled_at) = '2025';
EXPLAIN QUERY PLAN SELECT * FROM enrollments
    WHERE enrolled_at >= '2025-01-01' AND enrolled_at < '2026-01-01';

-- 6. Ko'p uchraydigan filtr uchun partial index
CREATE INDEX idx_active_only ON enrollments(student_id) WHERE status = 'active';

-- 7. Database dagi barcha index larni ko'ring
SELECT name, tbl_name FROM sqlite_master WHERE type = 'index' ORDER BY tbl_name;

-- 8. Tozalash
DROP INDEX IF EXISTS idx_enr_course;
DROP INDEX IF EXISTS idx_enr_student;
DROP INDEX IF EXISTS idx_enr_teacher;
DROP INDEX IF EXISTS idx_enr_course_status;
DROP INDEX IF EXISTS idx_active_only;

1-natija

idparentnotuseddetail
200SCAN enrollments

1 qator

2-natija

idparentnotuseddetail
300SEARCH enrollments USING INDEX idx_enr_course (course_id=?)

1 qator

3-natija

idparentnotuseddetail
300SEARCH enrollments USING INDEX idx_enr_course_status (course_id=? AND status=?)

1 qator

4-natija

idparentnotuseddetail
200SCAN enrollments

1 qator

5-natija

idparentnotuseddetail
200SCAN enrollments

1 qator

6-natija

idparentnotuseddetail
200SCAN enrollments

1 qator

7-natija

nametbl_name
idx_enr_courseenrollments
idx_enr_studentenrollments
idx_enr_teacherenrollments
idx_enr_course_statusenrollments
idx_active_onlyenrollments

5 qator

4 ms

Intervyu savollari

S: Index nima va u query ni qanday tezlashtiradi?

Qiymatlarni row o'rinlariga bog'laydigan saralangan yordamchi tuzilma (odatda B-tree). U O(n) full scan ni O(log n) lookup ga aylantiradi, hamda saralangan bo'lgani uchun oraliq, saralash va MIN/MAX ga ham xizmat qiladi.

S: Index ning kamchiliklari nima?

Disk joyi va sekinroq yozish — har bir insert, update va delete table dagi har bir index ni yangilashi kerak.

S: Composite index da column tartibi muhimmi?

Ha. Faqat eng chap prefiks lardan foydalanish mumkin: (A, B) index i A ga va (A, B) ga xizmat qiladi, yolg'iz B ga yo'q. Familiya, keyin ism bo'yicha saralangan telefon kitobini tasavvur qiling.

S: Covering index nima?

Query ga kerak bo'lgan har bir column ni o'zida saqlaydigan index; database javobni table ni umuman o'qimasdan index dan oladi.

S: "Sargable" nimani anglatadi?

Shart index dan foydalana olishini. Column ni function yoki arifmetikaga o'rash uni non-sargable qiladi va scan ga majbur qiladi; column yolg'iz turadigan qilib qayta yozing.

S: gender column iga index qo'yasizmi?

Odatda yo'q — juda kam har xil qiymat degani index table ning katta qismiga ishora qiladi va planner baribir ko'pincha scan qiladi. U composite index ning ikkinchi column i sifatida yoki partial index da foydali bo'lishi mumkin.

S: JOIN kutilmaganda sekin. Nimani tekshirasiz?

Join column lari index li mi (ayniqsa foreign key tomoni), ular bir xil data type dami, filtrlarni join dan oldin qo'llash mumkinmi va fan-out oraliq natijani shishirayaptimi.

S: Query nega sekin ekanini qanday aniqlaysiz?

EXPLAIN QUERY PLAN (SQLite) yoki EXPLAIN ANALYZE (PostgreSQL). SEARCH ... USING INDEX kutilgan joyda katta table ustidagi SCAN ni qidiring.

Xulosa

  • Index — bu kitob oxiridagi alifbo ko'rsatkichi: u "hammasini o'qish" ni "qidirib topish" ga aylantiradi. O(n) → O(log n).
  • PRIMARY KEY va UNIQUE avtomatik index yaratadi. Foreign key lar odatda yaratmaydi — bu real sxemalarda eng ko'p unutiladigan index.
  • Composite index lar eng chap prefiks qoidasiga bo'ysunadi. (A, B) A ga xizmat qiladi, B ga yo'q. Eng ko'p filtrlanadigan, tenglik bilan tekshiriladigan column ni birinchi qo'ying.
  • Covering index query ga table ga tegmasdan javob beradi — SELECT * dan qochishning yana bir sababi.
  • Kichik table larga, kam har xil qiymatli column larga va ko'p yoziladigan table larga index qo'ymang. Index ni aniq bir query talab qilgani uchun qo'shing.
  • Sargable = column chapda yolg'iz turadi. Function, arifmetika, cast va boshidagi % — hammasi index ni o'chiradi.
  • EXPLAIN QUERY PLAN: SCAN = butun table, SEARCH ... USING INDEX = aniq nishonga.
  • Tez join lar: foreign key ni index qiling, column type larini moslang, erta filtrlang, join dan oldin aggregate qiling.
  • .timer on yoki EXPLAIN ANALYZE bilan o'lchang. Bir vaqtda bitta narsani o'zgartiring.

Keyingi: 12-bob — SQL ni Python ga ulash va query natijasini pandas DataFrame ga topshirish.

Bu bobni bepul o'qidingiz.

Kitob o'rgatadi, lekin sizning kodingiz nega ishlamaganini ayta olmaydi. Kursda uy vazifangiz tekshiriladi.

Telegramda yozish Narxlar