14-bo‘lim

Indekslar

B-tree va GIN, indeks qachon ishlatilmaydi, qisman va ifoda indekslari, ko'p ustunli indeksda tartib va indeksning narxi.

🕑 11 daqiqa o‘qish 📄 746 so‘z 👁 0 marta ko‘rilgan
Ushbu bo‘lim mundarijasi
  1. Indeks turlari
  2. Indeks har doim ishlatilmaydi
  3. Qisman indeks
  4. Ifoda ustidan indeks
  5. Ko'p ustunli indeks
  6. Indeksning narxi
  7. Xulosa

Indeks - so'rovni tezlashtirishning asosiy vositasi. Lekin u sehrli tayoqcha emas: noto'g'ri qo'yilgan indeks foyda bermaydi, ortiqchasi esa zarar keltiradi.

SQL
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:

TurQachon ishlatiladi
B-treeTenglik va oraliq - standart, 90% holat
GINMassiv, JSONB, to'liq matn qidiruv
GiSTGeometriya, oraliqlar, qo'shnilik
BRINJuda katta, tartiblangan jadval (vaqt qatorlari)
HashFaqat tenglik - kamdan-kam kerak

Nomi ko'rsatilmasa, PostgreSQL B-tree yaratadi:

SQL
CREATE INDEX buyurtma_mijoz_idx ON buyurtma (mijoz);

SELECT indexname FROM pg_indexes
WHERE tablename = 'buyurtma' ORDER BY indexname;
Natija
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:

Natija
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)
Natija
-- '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.

Nima uchun baza indeksdan voz kechadi 1% qator mos keladi Indeksdan 200 ta manzil olib, faqat o'shalarni o'qish arzon INDEKS ISHLATILADI 99% qator mos keladi Indeksdan manzil olib, keyin deyarli hamma sahifani o'qish - QIMMAT TO'G'RIDAN-TO'G'RI O'QISH TEZROQ Indeks ikki bosqichli ish 1. Indeksdan qator manzillarini topish 2. Har manzil bo'yicha jadvaldan haqiqiy qatorni o'qish Ikkinchi qadam tasodifiy o'qish - u ketma-ket o'qishdan sekinroq Ko'p qator kerak bo'lsa, hammasini ketma-ket o'qigan arzonroq
Shuning uchun "indeks qo'ysam tezlashadi" degan taxmin har doim ham to'g'ri emas
boolean va kam qiymatli ustunlarga indeks qo'ymang

holat, 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:

SQL
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;
Natija
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:

Natija
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:

SQL
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';
Natija
CREATE INDEX
                                    indeks
------------------------------------------------------------------------------
 CREATE INDEX buyurtma_mijoz_lower_idx ON buyurtma USING btree (lower(mijoz))
(1 row)
Bu registrga sezgir bo'lmagan qidiruvning to'g'ri yo'li

Ko'p loyihalarda email yoki login registrga bog'liq bo'lmasligi kerak.

Uch xil yondashuv bor:

UsulBaho
WHERE lower(email) = lower($1) + ifoda indeksTo'g'ri
WHERE email ILIKE $1Indeks ishlamaydi, sekin
Saqlashda kichik harfga o'tkazishTez, 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:

SQL
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';
Natija
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 shartiIndeks 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:

SQL
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;
Natija
    indeks     |  hajm
---------------+--------
 buyurtma_pkey | 456 kB
(1 row)
Har indeks yozishni sekinlashtiradi

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:

VaziyatTavsiya
Ko'p o'qiladigan jadvalIndeks qo'yish foydali
Ko'p yoziladigan jurnal jadvaliMinimal indeks
Ishlatilmayotgan indeksO'chiring

Ishlatilmayotgan indekslarni topish oson:

SQL
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.

Amaliy topshiriq
  1. mijoz ustuniga indeks qo'ying va ro'yxatni ko'ring.
  2. Birlamchi kalit indeksi qayerdan paydo bo'lganini yozing.
  3. holat = 'bekor' va holat = 'yakunlangan' rejalarini solishtiring.
  4. Nima uchun bir xil indeks ikki xil ishlaganini tushuntiring.
  5. Qisman indeks yarating va hajmini to'liq indeks bilan solishtiring.
  6. lower(mijoz) bo'yicha qidiruv rejasini ko'ring.
  7. Ifoda ustidan indeks qo'ying va rejani qaytadan tekshiring.
  8. (holat, sana) indeksi sana bo'yicha qidiruvga yaraydimi?
  9. Ko'p ustunli indeksda tartib nima uchun muhimligini yozing.
  10. pg_stat_user_indexes bilan 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 = 0 bilan topib o'chiring.

Keyingi bo'limda rejalarni o'qishni - EXPLAIN ni batafsil o'rganamiz.

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.