14-bo‘lim
Indekslar
B-tree va GIN, indeks qachon ishlatilmaydi, qisman va ifoda indekslari, ko'p ustunli indeksda tartib va indeksning narxi.
Ushbu bo‘lim mundarijasi
Indeks - so'rovni tezlashtirishning asosiy vositasi. Lekin u sehrli tayoqcha emas: noto'g'ri qo'yilgan indeks foyda bermaydi, ortiqchasi esa zarar keltiradi.
CREATE TABLE buyurtma (
id int GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
mijoz text NOT NULL,
holat text NOT NULL,
summa numeric(10,2) NOT NULL,
sana date NOT NULL
);
INSERT INTO buyurtma (mijoz, holat, summa, sana)
SELECT 'mijoz_' || (i % 500),
CASE WHEN i % 100 = 0 THEN 'bekor' ELSE 'yakunlangan' END,
(i % 900 + 100)::numeric,
DATE '2026-01-01' + (i % 365)
FROM generate_series(1, 20000) AS i;
Indeks turlari #
PostgreSQL da bir nechta indeks turi bor va har biri boshqa vazifa uchun:
| Tur | Qachon ishlatiladi |
|---|---|
| B-tree | Tenglik va oraliq - standart, 90% holat |
| GIN | Massiv, JSONB, to'liq matn qidiruv |
| GiST | Geometriya, oraliqlar, qo'shnilik |
| BRIN | Juda katta, tartiblangan jadval (vaqt qatorlari) |
| Hash | Faqat tenglik - kamdan-kam kerak |
Nomi ko'rsatilmasa, PostgreSQL B-tree yaratadi:
CREATE INDEX buyurtma_mijoz_idx ON buyurtma (mijoz);
SELECT indexname FROM pg_indexes
WHERE tablename = 'buyurtma' ORDER BY indexname;
CREATE INDEX
indexname
--------------------
buyurtma_mijoz_idx
buyurtma_pkey
(2 rows)
Birlamchi kalit uchun indeks avtomatik yaratilgan -
buyurtma_pkey.
Indeks har doim ishlatilmaydi #
Bu eng muhim tushuncha. Indeks bor bo'lishi - u ishlatiladi degani emas.
holat ustuniga indeks qo'yamiz va bir xil indeksni ikki
xil qiymat bilan sinaymiz:
CREATE INDEX buyurtma_holat_idx ON buyurtma (holat);
-- 'yakunlangan' - qatorlarning 99 foizi
EXPLAIN (COSTS OFF) SELECT * FROM buyurtma WHERE holat = 'yakunlangan';
QUERY PLAN
-----------------------------------------
Seq Scan on buyurtma
Filter: (holat = 'yakunlangan'::text)
-- 'bekor' - qatorlarning 1 foizi
EXPLAIN (COSTS OFF) SELECT * FROM buyurtma WHERE holat = 'bekor';
QUERY PLAN
-------------------------------------------------
Index Scan using buyurtma_holat_idx on buyurtma
Index Cond: (holat = 'bekor'::text)
Bir xil ustun, bir xil indeks - lekin ikki xil reja.
boolean va kam qiymatli ustunlarga indeks qo'ymangholat, faol, jins kabi ustunlarda atigi ikki-uch xil
qiymat bo'ladi. Oddiy indeks ularda deyarli foydasiz - har
qiymat qatorlarning yarmiga to'g'ri keladi.
Istisno: qiymatlardan biri juda kam uchrasa. Yuqoridagi
bekor holati aynan shunday - u 1 foiz, shuning uchun indeks
ishladi.
Bunday holatda eng yaxshi yechim - qisman indeks (quyida).
Qisman indeks #
Faqat kerakli qatorlarni indekslash mumkin:
CREATE INDEX buyurtma_sana_idx ON buyurtma (sana);
CREATE INDEX buyurtma_bekor_idx ON buyurtma (sana) WHERE holat = 'bekor';
SELECT pg_size_pretty(pg_relation_size('buyurtma_bekor_idx')) AS qisman,
pg_size_pretty(pg_relation_size('buyurtma_sana_idx')) AS toliq;
CREATE INDEX
CREATE INDEX
qisman | toliq
--------+--------
16 kB | 160 kB
(1 row)
Farqqa qarang: qisman indeks ancha kichik, chunki u qatorlarning atigi bir foizini saqlaydi.
U kichik bo'lgani uchun xotiraga sig'adi, tez o'qiladi va yozishda kam yuk beradi.
Ifoda ustidan indeks #
Ustunga funksiya qo'llanilsa, oddiy indeks ishlamaydi:
EXPLAIN (COSTS OFF) SELECT * FROM buyurtma WHERE lower(mijoz) = 'mijoz_42';
QUERY PLAN
---------------------------------------------------
Gather
Workers Planned: 1
-> Parallel Seq Scan on buyurtma
Filter: (lower(mijoz) = 'mijoz_42'::text)
Indeks mijoz ni saqlaydi, so'rov esa lower(mijoz) ni
so'rayapti - baza ularni bog'lay olmaydi.
Yechim - aynan shu ifodani indekslash:
CREATE INDEX buyurtma_mijoz_lower_idx ON buyurtma (lower(mijoz));
SELECT replace(indexdef, 'public.', '') AS indeks
FROM pg_indexes
WHERE indexname = 'buyurtma_mijoz_lower_idx';
CREATE INDEX
indeks
------------------------------------------------------------------------------
CREATE INDEX buyurtma_mijoz_lower_idx ON buyurtma USING btree (lower(mijoz))
(1 row)
Ko'p loyihalarda email yoki login registrga bog'liq bo'lmasligi kerak.
Uch xil yondashuv bor:
| Usul | Baho |
|---|---|
WHERE lower(email) = lower($1) + ifoda indeks | To'g'ri |
WHERE email ILIKE $1 | Indeks ishlamaydi, sekin |
| Saqlashda kichik harfga o'tkazish | Tez, lekin asl yozuv yo'qoladi |
Birinchisi eng moslashuvchan: asl yozuv saqlanadi va qidiruv ham tez.
Ko'p ustunli indeks #
Bir nechta ustunni birga indekslash mumkin, lekin tartib muhim:
CREATE INDEX buyurtma_holat_sana_idx ON buyurtma (holat, sana);
SELECT replace(indexdef, 'public.', '') AS indeks
FROM pg_indexes
WHERE indexname = 'buyurtma_holat_sana_idx';
CREATE INDEX
indeks
----------------------------------------------------------------------------
CREATE INDEX buyurtma_holat_sana_idx ON buyurtma USING btree (holat, sana)
(1 row)
Bu indeks quyidagi so'rovlarga yaraydi:
| So'rov sharti | Indeks yaraydimi |
|---|---|
holat = ? | Ha |
holat = ? AND sana > ? | Ha - eng yaxshi holat |
sana > ? | Yo'q - birinchi ustun yo'q |
Qoida: ko'p ustunli indeks chapdan boshlab ishlaydi. Uni telefon kitobiga o'xshating: familiya bo'yicha topish oson, faqat ism bo'yicha esa - yo'q.
Shuning uchun tenglik sharti qo'yiladigan ustunni oldinga, oraliq shartlisini keyinga qo'ying.
Indeksning narxi #
Indeks bepul emas:
SELECT indexrelname AS indeks,
pg_size_pretty(pg_relation_size(indexrelid)) AS hajm
FROM pg_stat_user_indexes
WHERE relname = 'buyurtma'
ORDER BY pg_relation_size(indexrelid) DESC;
indeks | hajm
---------------+--------
buyurtma_pkey | 456 kB
(1 row)
INSERT, UPDATE va DELETE da baza har bir indeksni
yangilashi kerak.
Beshta indeksli jadvalga qator qo'shish - bu olti marta yozish (jadval + beshta indeks).
Shuning uchun:
| Vaziyat | Tavsiya |
|---|---|
| Ko'p o'qiladigan jadval | Indeks qo'yish foydali |
| Ko'p yoziladigan jurnal jadvali | Minimal indeks |
| Ishlatilmayotgan indeks | O'chiring |
Ishlatilmayotgan indekslarni topish oson:
SELECT indexrelname, idx_scan FROM pg_stat_user_indexes
WHERE idx_scan = 0;
idx_scan = 0 - bu indeks statistika yig'ilgandan beri
hech qachon ishlatilmagan degani. Bunday indeks faqat joy
egallaydi va yozishni sekinlashtiradi.
mijozustuniga indeks qo'ying va ro'yxatni ko'ring.- Birlamchi kalit indeksi qayerdan paydo bo'lganini yozing.
holat = 'bekor'vaholat = 'yakunlangan'rejalarini solishtiring.- Nima uchun bir xil indeks ikki xil ishlaganini tushuntiring.
- Qisman indeks yarating va hajmini to'liq indeks bilan solishtiring.
lower(mijoz)bo'yicha qidiruv rejasini ko'ring.- Ifoda ustidan indeks qo'ying va rejani qaytadan tekshiring.
(holat, sana)indeksisanabo'yicha qidiruvga yaraydimi?- Ko'p ustunli indeksda tartib nima uchun muhimligini yozing.
pg_stat_user_indexesbilan ishlatilmagan indekslarni toping.
Xulosa #
- B-tree - standart indeks; tenglik va oraliq shartlari uchun.
- GIN massiv, JSONB va matn qidiruv uchun; BRIN juda katta tartiblangan jadval uchun.
- Indeks bor bo'lishi - u ishlatiladi degani emas.
- Ko'p qator mos kelsa, baza ketma-ket o'qishni afzal ko'radi - bu tezroq.
- Kam qiymatli ustunlarga (
boolean,holat) oddiy indeks deyarli foydasiz. - Qisman indeks faqat kerakli qatorlarni saqlaydi - kichik va tez.
- Ustunga funksiya qo'llanilsa, oddiy indeks ishlamaydi - ifoda indeksi kerak.
- Registrga sezgir bo'lmagan qidiruv uchun
lower()ustidan indeks - to'g'ri yechim. - Ko'p ustunli indeks chapdan boshlab ishlaydi; tenglik ustuni oldinga.
- Har indeks yozishni sekinlashtiradi - ishlatilmayotganini
idx_scan = 0bilan topib o'chiring.
Keyingi bo'limda rejalarni o'qishni - EXPLAIN ni batafsil 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.