14-bo‘lim
Indekslar qanday ishlaydi
B-daraxt tuzilmasi, klasterlangan va ikkilamchi indeks, kompozit indeks tartibi, qamrovchi indeks va indeksning narxi.
Ushbu bo‘lim mundarijasi
Indeks - kitobning mundarijasi. Usiz har savolga javob topish uchun butun kitobni varaqlash kerak.
Indekssiz qidiruv #
CREATE TABLE sonlar (
n INT PRIMARY KEY
);
INSERT INTO sonlar (n)
WITH RECURSIVE ketma_ket AS (
SELECT 1 AS n
UNION ALL
SELECT n + 1 FROM ketma_ket WHERE n < 20000
)
SELECT n FROM ketma_ket;
CREATE TABLE xodimlar (
id INT PRIMARY KEY,
ism VARCHAR(50) NOT NULL,
familiya VARCHAR(50) NOT NULL,
bolim VARCHAR(30) NOT NULL,
maosh INT NOT NULL,
shahar VARCHAR(30) NOT NULL
);
INSERT INTO xodimlar (id, ism, familiya, bolim, maosh, shahar)
SELECT
n,
CONCAT('Ism', n),
CONCAT('Familiya', n),
ELT(1 + (n % 4), 'IT', 'Hisobot', 'Marketing', 'Kadrlar'),
3000000 + (n % 50) * 100000,
ELT(1 + (n % 5), 'Namangan', 'Toshkent', 'Andijon', 'Farg''ona', 'Buxoro')
FROM sonlar;
SELECT COUNT(*) AS qatorlar FROM xodimlar;
+----------+
| qatorlar |
+----------+
| 20000 |
+----------+
-- Indekssiz ustun bo'yicha qidiruv - butun jadval o'qiladi
FLUSH STATUS;
SELECT COUNT(*) FROM xodimlar WHERE familiya = 'Familiya500';
SHOW SESSION STATUS LIKE 'Handler_read_rnd_next';
+----------+
| COUNT(*) |
+----------+
| 1 |
+----------+
+-----------------------+-------+
| Variable_name | Value |
+-----------------------+-------+
| Handler_read_rnd_next | 20001 |
+-----------------------+-------+
Handler_read_rnd_next - to'liq skanerlash o'lchagichiBu hisoblagich ketma-ket o'qilgan qatorlar sonini ko'rsatadi - ya'ni indekssiz varaqlangan qatorlar.
20001 - butun jadval (20 000 qator) plus oxirgi tekshiruv.
Ma'lumotlar bazasi bitta qatorni topish uchun hammasini
o'qidi.
Foydali hisoblagichlar:
| Hisoblagich | Nima anglatadi |
|---|---|
Handler_read_rnd_next | To'liq skanerlash - kam bo'lgani yaxshi |
Handler_read_key | Indeks bo'yicha qidiruv - ko'p bo'lgani yaxshi |
Handler_read_next | Indeks diapazoni bo'yicha o'qish |
FLUSH STATUS ularni nolga qaytaradi, shuning uchun bitta
so'rovni aniq o'lchash mumkin.
Bu EXPLAIN dan aniqroq: EXPLAIN rejani ko'rsatadi,
hisoblagichlar esa haqiqatan nima bo'lganini.
Indeks bilan #
CREATE INDEX idx_familiya ON xodimlar (familiya);
FLUSH STATUS;
SELECT COUNT(*) FROM xodimlar WHERE familiya = 'Familiya500';
SHOW SESSION STATUS LIKE 'Handler_read_rnd_next';
+----------+
| COUNT(*) |
+----------+
| 1 |
+----------+
+-----------------------+-------+
| Variable_name | Value |
+-----------------------+-------+
| Handler_read_rnd_next | 0 |
+-----------------------+-------+
Nol qator ketma-ket o'qildi - indeks to'g'ridan-to'g'ri kerakli joyga olib bordi.
B-daraxt tuzilmasi #
B+ daraxt balanslangan - har bargga yo'l bir xil uzunlikda.
Har tugunda yuzlab kalit bo'ladi (bitta 16 KB sahifaga sig'adigancha), shuning uchun daraxt juda past bo'ladi:
| Qatorlar | Daraxt darajasi | Disk o'qishlari |
|---|---|---|
| 1 000 | 2 | 2 |
| 1 000 000 | 3 | 3 |
| 1 000 000 000 | 4-5 | 4-5 |
Milliard qatorda ham atigi 5 ta o'qish. To'liq skanerlashda esa million marta ko'p.
Yana bir muhim xossa: barglar tartiblangan va bir-biriga bog'langan. Shuning uchun indeks quyidagilarni ham tezlashtiradi:
WHERE ustun BETWEEN a AND b- diapazon;ORDER BY ustun- saralash bepul;MIN(ustun),MAX(ustun)- chekka barg;WHERE ustun > x LIMIT 10- dastlabki qatorlar.
Klasterlangan indeks #
SELECT INDEX_NAME AS indeks, SEQ_IN_INDEX AS tartib,
COLUMN_NAME AS ustun, NON_UNIQUE AS takrorlanadi
FROM information_schema.STATISTICS
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'xodimlar'
ORDER BY BINARY INDEX_NAME, SEQ_IN_INDEX;
+--------------+--------+----------+--------------+
| indeks | tartib | ustun | takrorlanadi |
+--------------+--------+----------+--------------+
| PRIMARY | 1 | id | 0 |
| idx_familiya | 1 | familiya | 1 |
+--------------+--------+----------+--------------+
InnoDB da jadval birlamchi kalit bo'yicha tartiblangan B-daraxt sifatida saqlanadi. Ya'ni:
PRIMARY indeks barglari = QATORNING O'ZI
Bu klasterlangan indeks deb ataladi va muhim oqibatlari bor:
1. Birlamchi kalit bo'yicha qidiruv eng tez - qo'shimcha o'qish kerak emas.
2. Ikkilamchi indeks birlamchi kalitni saqlaydi:
idx_familiya barglari: ('Familiya500' -> id=500)
Keyin id bo'yicha asosiy daraxtga borish kerak. Bu
ikki bosqichli qidiruv (bookmark lookup).
3. Uzun birlamchi kalit hamma indeksni kattalashtiradi.
Shuning uchun 3-bo'limda CHAR(36) UUID emas, BINARY(16)
tavsiya qilingan edi.
4. Tasodifiy birlamchi kalit yozishni sekinlashtiradi - yangi qator daraxtning o'rtasiga tushadi va sahifa bo'linadi.
Shuning uchun AUTO_INCREMENT yaxshi: yangi qator har doim
oxiriga qo'shiladi.
Kompozit indeks va ustunlar tartibi #
CREATE INDEX idx_bolim_maosh ON xodimlar (bolim, maosh);
-- Birinchi ustun bo'yicha - indeks ISHLAYDI
FLUSH STATUS;
SELECT COUNT(*) FROM xodimlar WHERE bolim = 'IT';
SHOW SESSION STATUS LIKE 'Handler_read_rnd_next';
+----------+
| COUNT(*) |
+----------+
| 5000 |
+----------+
+-----------------------+-------+
| Variable_name | Value |
+-----------------------+-------+
| Handler_read_rnd_next | 0 |
+-----------------------+-------+
-- Ikkala ustun bo'yicha - indeks TO'LIQ ishlaydi
FLUSH STATUS;
SELECT COUNT(*) FROM xodimlar WHERE bolim = 'IT' AND maosh = 3000000;
SHOW SESSION STATUS LIKE 'Handler_read_rnd_next';
+----------+
| COUNT(*) |
+----------+
| 200 |
+----------+
+-----------------------+-------+
| Variable_name | Value |
+-----------------------+-------+
| Handler_read_rnd_next | 0 |
+-----------------------+-------+
-- FAQAT ikkinchi ustun bo'yicha - indeks YARAMAYDI
FLUSH STATUS;
SELECT COUNT(*) FROM xodimlar WHERE maosh = 3000000;
SHOW SESSION STATUS LIKE 'Handler_read_rnd_next';
+----------+
| COUNT(*) |
+----------+
| 400 |
+----------+
+-----------------------+-------+
| Variable_name | Value |
+-----------------------+-------+
| Handler_read_rnd_next | 0 |
+-----------------------+-------+
Kompozit indeks (a, b, c) quyidagi so'rovlarga yordam beradi:
| So'rov | Indeks ishlaydimi |
|---|---|
WHERE a = ? | Ha |
WHERE a = ? AND b = ? | Ha |
WHERE a = ? AND b = ? AND c = ? | Ha |
WHERE b = ? | Yo'q |
WHERE c = ? | Yo'q |
WHERE b = ? AND c = ? | Yo'q |
WHERE a = ? AND c = ? | Faqat a qismi |
Buni telefon kitobi bilan tasavvur qiling: u
(familiya, ism) bo'yicha tartiblangan.
- "Qodirov" ni topish - oson;
- "Qodirov Husanboy" ni topish - oson;
- Faqat "Husanboy" ni topish - butun kitobni varaqlash.
Yuqoridagi uchinchi misolda Handler_read_rnd_next nol
chiqdi, chunki MariaDB indeksni to'liq skanerlash (index
scan) usulini tanladi - u jadvalni skanerlashdan arzonroq,
lekin haqiqiy qidiruvdan ancha sekin.
Shuning uchun maosh bo'yicha tez-tez qidirsangiz, unga
alohida indeks kerak.
Tartib qanday tanlanadi: eng ko'p tenglik bilan
ishlatiladigan ustun birinchi bo'lsin. Diapazon shartlari
(>, BETWEEN) esa oxirida - ulardan keyingi ustunlar
indeksda ishlamaydi.
Qamrovchi indeks #
CREATE INDEX idx_shahar_ism ON xodimlar (shahar, ism);
-- Kerakli ustunlar indeksda BOR - jadvalga borish shart emas
EXPLAIN SELECT shahar, ism FROM xodimlar WHERE shahar = 'Andijon' LIMIT 5;
+------+-------------+----------+------+----------------+----------------+---------+-------+------+--------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+------+-------------+----------+------+----------------+----------------+---------+-------+------+--------------------------+
| 1 | SIMPLE | xodimlar | ref | idx_shahar_ism | idx_shahar_ism | 122 | const | 4000 | Using where; Using index |
+------+-------------+----------+------+----------------+----------------+---------+-------+------+--------------------------+
Using index - eng yaxshi belgiExtra ustunidagi Using index "qamrovchi indeks"
(covering index) degani: so'rovga kerakli barcha ustun
indeksning o'zida bor, shuning uchun asosiy jadvalga
umuman borilmadi.
Bu ikki bosqichli qidiruvni bir bosqichga aylantiradi va so'rovni sezilarli tezlashtiradi.
Chalkashtirmang:
Extra qiymati | Ma'nosi |
|---|---|
Using index | Qamrovchi indeks - juda yaxshi |
Using where | Qo'shimcha filtr qo'llandi - normal |
Using index condition | Filtr indeks darajasida - yaxshi |
Using filesort | Saralash uchun qo'shimcha ish - yomon |
Using temporary | Vaqtinchalik jadval - yomon |
Qamrovchi indeks yaratish uchun SELECT dagi ustunlarni ham
indeksga qo'shing:
-- WHERE shahar = ? bo'yicha ism va maosh kerak bo'lsa
CREATE INDEX idx_qamrovchi ON xodimlar (shahar, ism, maosh);
Lekin haddan oshmang - har qo'shimcha ustun indeksni kattalashtiradi va yozishni sekinlashtiradi.
Indeksning narxi #
SELECT
COUNT(DISTINCT INDEX_NAME) AS indekslar_soni
FROM information_schema.STATISTICS
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'xodimlar';
+----------------+
| indekslar_soni |
+----------------+
| 4 |
+----------------+
Indeks bepul emas:
| Amal | Ta'siri |
|---|---|
INSERT | Har indeksga yangi yozuv qo'shiladi |
UPDATE | O'zgargan ustunning indekslari yangilanadi |
DELETE | Har indeksdan yozuv olib tashlanadi |
| Disk | Indeks joy egallaydi (ba'zan jadvaldan ko'p) |
| Xotira | Kesh indekslar bilan bo'lishiladi |
Beshta indeksli jadvalda har INSERT oltita yozuv amalini
bajaradi (jadval + 5 indeks).
Shuning uchun:
- Kerak bo'lmagan indeksni o'chiring;
- Ishlatilmayotgan indekslarni toping;
- Ko'p yoziladigan jadvalda indeksni minimal tuting.
Ishlatilmayotgan indeksni topish (MariaDB da
performance_schema yoqilgan bo'lsa):
SELECT object_name, index_name, count_star
FROM performance_schema.table_io_waits_summary_by_index_usage
WHERE index_name IS NOT NULL AND count_star = 0;
Takroriy indekslarga ham e'tibor bering:
INDEX (a)
INDEX (a, b) -- birinchisi ORTIQCHA, chapdan prefiks qoidasi
(a, b) indeksi (a) ning ishini ham bajaradi.
Qaysi ustunga indeks kerak #
| Qo'ying | Sabab |
|---|---|
WHERE da tez-tez ishlatiladigan ustun | Asosiy maqsad |
| Tashqi kalit | JOIN va cheklov tekshiruvi |
ORDER BY / GROUP BY ustuni | Saralash bepul bo'ladi |
UNIQUE bo'lishi kerak ustun | Cheklov + indeks |
| Qo'ymang | Sabab |
|---|---|
| Kam selektiv ustun | jins, faolmi - foydasi kam |
| Juda kichik jadval | To'liq skanerlash tezroq |
| Ko'p yoziladigan, kam o'qiladigan ustun | Narxi foydasidan ko'p |
| Allaqachon prefiks bilan qamralgan | Takrorlanish |
Selektivlik - noyob qiymatlar ulushi:
SELECT COUNT(DISTINCT bolim) / COUNT(*) AS selektivlik FROM xodimlar;
Natija 1 ga yaqin bo'lsa - indeks juda foydali. 0 ga yaqin
bo'lsa (masalan 0.0002) - indeks deyarli yordam bermaydi,
chunki har qiymatda minglab qator bor.
Odatiy chegara: selektivlik 0.01 dan past bo'lsa, indeks ko'pincha ishlatilmaydi.
Selektivlikni o'lchash #
SELECT
ROUND(COUNT(DISTINCT bolim) / COUNT(*), 6) AS bolim,
ROUND(COUNT(DISTINCT shahar) / COUNT(*), 6) AS shahar,
ROUND(COUNT(DISTINCT maosh) / COUNT(*), 6) AS maosh,
ROUND(COUNT(DISTINCT familiya)/ COUNT(*), 6) AS familiya
FROM xodimlar;
+----------+----------+----------+----------+
| bolim | shahar | maosh | familiya |
+----------+----------+----------+----------+
| 0.000200 | 0.000250 | 0.002500 | 1.000000 |
+----------+----------+----------+----------+
familiya selektivligi 1.0 - har qiymat noyob, indeks juda
samarali. bolim esa 0.0002 - atigi 4 xil qiymat, indeksdan
foyda kam.
- 20 000 qatorli jadval yarating.
- Indekssiz qidiruvda
Handler_read_rnd_nextni o'lchang. - Indeks qo'shib, xuddi shu o'lchovni takrorlang.
- B+ daraxt nima uchun tez ekanini tushuntiring.
- Klasterlangan indeks nima ekanini ayting.
- Kompozit indeks yaratib, uch xil so'rovni sinang.
- Chapdan prefiks qoidasini o'z so'zingiz bilan tushuntiring.
Using indexchiqadigan so'rov yozing.- Indeksning to'rtta narxini sanang.
- Ustunlar selektivligini hisoblab, qaysiga indeks kerakligini ayting.
Xulosa #
- Indeks - B+ daraxt, milliard qatorda ham 5 ta o'qish.
Handler_read_rnd_nextto'liq skanerlashni o'lchaydi.- InnoDB da birlamchi kalit - klasterlangan, qatorning o'zi.
- Ikkilamchi indeks birlamchi kalitni saqlaydi.
- Uzun birlamchi kalit barcha indeksni kattalashtiradi.
- Kompozit indeksda chapdan prefiks qoidasi ishlaydi.
(a, b)indeksiWHERE b = ?ga yordam bermaydi.Using index- qamrovchi indeks, eng yaxshi belgi.Using filesortvaUsing temporary- yomon belgilar.- Har indeks yozishni sekinlashtiradi va joy egallaydi.
- Selektivlik 0.01 dan past bo'lsa, indeks kam foyda beradi.
(a, b)bor bo'lsa,(a)indeksi ortiqcha.
Keyingi bo'limda EXPLAIN bilan so'rovlarni tahlil qilishni
o'rganamiz.
O‘qish tarixini saqlamoqchimisiz?
Tizimga kirsangiz, tugatgan bo‘limlaringiz saqlanadi va qoldirgan joyingizdan davom etasiz.
Xatolik topdingizmi?
Imlo xatosi, ishlamaydigan kod yoki noto‘g‘ri ma‘lumotni ko‘rsangiz - bizga xabar bering. Har bir xabar administrator tomonidan ko‘rib chiqiladi.