Index va performance
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:
- Full table scan — database har bir row ni o'qiydi, chunki siz so'ragan row larni topishning tezroq yo'li yo'q.
- Juda ko'p data qaytarish — keng table da
SELECT *, yoki o'rganish paytidaLIMITsiz ishlash. - JOIN dagi fan-out — oraliq natija ikkala kirishdan ham ancha katta (5-bob).
- Ulkan natijani saralash yoki deduplikatsiya qilish — millionlab row ustida
ORDER BY,DISTINCT,UNION. - Filtrlanayotgan column ga o'ralgan function — bu mavjud index ni o'chiradi.
1-raqam eng kattasi, va index — uning davosi.
Index aslida qanday ishlaydi
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:
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
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:
- JOIN larda ishlatiladigan foreign key column lari.
- Katta table larda
WHEREda uchraydigan column lar. - Katta table larda
ORDER BYda ishlatiladigan column lar. - Ba'zan
GROUP BYdagi 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 |
(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.
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 |
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.
EXPLAIN QUERY PLAN SELECT * FROM enrollments WHERE course_id = 2;
| id | parent | notused | detail |
|---|---|---|---|
| 2 | 0 | 0 | SCAN 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:
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=?)
| id | parent | notused | detail |
|---|---|---|---|
| 4 | 0 | 0 | SCAN e |
| 6 | 0 | 0 | SEARCH s USING INTEGER PRIMARY KEY (rowid=?) |
| 9 | 0 | 0 | SEARCH 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:
- 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); - 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); - 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.
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:
EXPLAIN QUERY PLANni ishga tushiring. Katta table daSCANbormi?- Filtrlanayotgan column index li mi? Yo'q bo'lsa, index qo'ying va rejani qayta tekshiring.
- Filtrlanayotgan column ga function o'ralganmi? Uni oraliq ko'rinishida qayta yozing.
- Join column lari bir xil type dami va foreign key tomoni index li mi?
- Kerak bo'lmagan column larni tanlayapsizmi? Ularni nomlab yozing;
SELECT *covering index ni to'sadi. - JOIN dan keyin
COUNT(*)o'sdimi? Bu fan-out — avval aggregate qiling. - Join dagi takroriy row larni yashirayotgan
DISTINCTbormi? Buning o'rniga join ni tuzating. - Ertaroq filtrlash mumkinmi —
WHEREni join dan oldin CTE ga tushiring? - Hali ham sekin va query doim ishlaydimi? Jadval bo'yicha yangilanadigan xulosa table ini ko'rib chiqing.
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
-- 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
| id | parent | notused | detail |
|---|---|---|---|
| 2 | 0 | 0 | SCAN enrollments |
1 qator
2-natija
| id | parent | notused | detail |
|---|---|---|---|
| 3 | 0 | 0 | SEARCH enrollments USING INDEX idx_enr_course (course_id=?) |
1 qator
3-natija
| id | parent | notused | detail |
|---|---|---|---|
| 3 | 0 | 0 | SEARCH enrollments USING INDEX idx_enr_course_status (course_id=? AND status=?) |
1 qator
4-natija
| id | parent | notused | detail |
|---|---|---|---|
| 2 | 0 | 0 | SCAN enrollments |
1 qator
5-natija
| id | parent | notused | detail |
|---|---|---|---|
| 2 | 0 | 0 | SCAN enrollments |
1 qator
6-natija
| id | parent | notused | detail |
|---|---|---|---|
| 2 | 0 | 0 | SCAN enrollments |
1 qator
7-natija
| name | tbl_name |
|---|---|
| idx_enr_course | enrollments |
| idx_enr_student | enrollments |
| idx_enr_teacher | enrollments |
| idx_enr_course_status | enrollments |
| idx_active_only | enrollments |
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 KEYvaUNIQUEavtomatik 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)Aga xizmat qiladi,Bga 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 onyokiEXPLAIN ANALYZEbilan 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.