JSONB — GIN index, so'rovlarni tezlashtirish
Muammodan boshlaylik
Oldingi darsda JSONB ga ma'lumot qo'shib, @> operatori bilan qidirdik. Kichik jadvalda tez ishladi. Lekin jadvalda 1 million mahsulot bo'lsa nima bo'ladi?
PostgreSQL har bir qatorning xususiyatlar ustunini ochib, ichida "ranglar": ["qora"] bormi deb tekshiradi. 1 million qator — 1 million tekshiruv. Bu — Seq Scan (to'liq jadval skanerlash). Sekin.
Index qo'yilsa — PostgreSQL oldindan tayyorlangan "qidirish xaritasi" orqali bir zumda topadi. JSONB uchun bu xarita — GIN index.
GIN index nima?
GIN (Generalized Inverted Index) — teskari indeks. Har bir kalit va qiymat qaysi qatorlarda borligini alohida saqlaydi.
Hayotiy o'xshatma: kitob oxiridagi mavzular ko'rsatkichi. "PostgreSQL" so'zi kitobning 12, 45, 78-betlarida bor — bu ko'rsatkichda yozilgan. Kitobni sahifa-sahifa o'qimasdan, ko'rsatkichdan darhol topasiz. GIN index — shu ko'rsatkich.
Oddiy qidiruv (Seq Scan): GIN index bilan:
────────────────────────── ──────────────────────────
1-qator → JSONB ochib tekshir "qora" → [1, 2, 3] ←
2-qator → JSONB ochib tekshir darhol topildi!
3-qator → JSONB ochib tekshir
...
1 000 000-qator → ...
GIN index yaratish
-- Butun JSONB ustuniga GIN index
CREATE INDEX idx_mahsulotlar_xususiyatlar
ON mahsulotlar USING GIN (xususiyatlar);
Bu bitta index — xususiyatlar ustunidagi barcha kalit va qiymatlarni qamrab oladi.
Index yaratilgandan keyin quyidagi operatorlar avtomatik tezlashadi:
@> ichida tuzilma bormi
? kalit bormi
?| kalitlardan biri bormi
?& kalitlarning hammasi bormi
Index qancha tezlashtiradi?
-- Index yo'q — Seq Scan
EXPLAIN ANALYZE
SELECT * FROM mahsulotlar
WHERE xususiyatlar @> '{"brend": "Dell"}';
-- Seq Scan on mahsulotlar (cost=0.00..2540.00 rows=100)
-- Execution time: 1240 ms
-- GIN index bor — Bitmap Index Scan
EXPLAIN ANALYZE
SELECT * FROM mahsulotlar
WHERE xususiyatlar @> '{"brend": "Dell"}';
-- Bitmap Index Scan on idx_mahsulotlar_xususiyatlar (cost=0.00..12.50 rows=100)
-- Execution time: 3 ms
1240 ms → 3 ms. 400 barobar tez.
jsonb_path_ops — yengilroq index
Standart GIN index kalitlar, qiymatlar va ularning kombinatsiyasini saqlaydi. jsonb_path_ops esa faqat @> uchun optimallashtirilgan — kichikroq, tezroq:
CREATE INDEX idx_mahsulotlar_path
ON mahsulotlar USING GIN (xususiyatlar jsonb_path_ops);
Standart GIN: jsonb_path_ops:
────────────── ──────────────────
@> ✅ @> ✅
? ✅ ? ❌ (ishlamaydi)
?| ✅ ?| ❌
?& ✅ ?& ❌
Faqat @> ishlatilsa — jsonb_path_ops tanlang: hajmi kichik, tezroq. ? operatorlari ham kerak bo'lsa — standart GIN.
Muayyan kalit uchun index
Ba'zan butun JSONB emas, faqat bitta kalit bo'yicha qidirish kerak. Masalan, faqat brend bo'yicha. Ifodalangan indeks — expression index:
CREATE INDEX idx_mahsulotlar_brend
ON mahsulotlar ((xususiyatlar ->> 'brend'));
Bu B-tree index — oddiy ustun indeksiga o'xshash. Faqat brend kalit bo'yicha qidirishni tezlashtiradi:
-- Bu so'rov endi B-tree index ishlatadi
SELECT * FROM mahsulotlar
WHERE xususiyatlar ->> 'brend' = 'Dell';
GIN dan qachon afzal? GIN butun JSONB uchun katta index. Agar faqat bitta-ikkita kalit bo'yicha ko'p qidirilsa — expression index kichikroq va ko'pincha tezroq.
Partial index — faqat kerakli qatorlar uchun
Agar mahsulotlarning faqat bir qismi qidirilsa — masalan, faqat aktiv mahsulotlar — partial index qo'yish mumkin:
CREATE INDEX idx_aktiv_mahsulotlar
ON mahsulotlar USING GIN (xususiyatlar)
WHERE aktiv = true; -- ← faqat aktiv qatorlar
Bu index hajmi kichik — chunki faqat aktiv qatorlar kiritilgan.
Amaliy misol — qidirish so'rovlari va ularning indexlari
Vaziyat: elektron do'kon qidiruvi
-- 1. Brend bo'yicha qidirish — expression index yaxshi
CREATE INDEX idx_brend ON mahsulotlar ((xususiyatlar ->> 'brend'));
SELECT * FROM mahsulotlar
WHERE xususiyatlar ->> 'brend' = 'Dell';
-- 2. Xususiyat mavjudligini tekshirish — GIN kerak
CREATE INDEX idx_gin ON mahsulotlar USING GIN (xususiyatlar);
SELECT * FROM mahsulotlar
WHERE xususiyatlar ? 'noise_cancelling';
-- 3. Rang bo'yicha qidirish — GIN @> bilan
SELECT * FROM mahsulotlar
WHERE xususiyatlar @> '{"ranglar": ["qora"]}';
-- 4. Narx oralig'i + JSONB kombinatsiyasi
SELECT * FROM mahsulotlar
WHERE narx BETWEEN 500000 AND 2000000
AND xususiyatlar @> '{"brend": "Sony"}';
-- PostgreSQL ikki indexni birlashtiradi: narx (B-tree) + JSONB (GIN)
Index ishlatilayaptimi? — EXPLAIN bilan tekshirish
EXPLAIN (ANALYZE, BUFFERS)
SELECT * FROM mahsulotlar
WHERE xususiyatlar @> '{"brend": "Dell"}';
Natijada qidirish:
Bitmap Index Scan on idx_gin ← index ishlatildi ✅
Index Cond: (xususiyatlar @> '{"brend": "Dell"}')
Seq Scan on mahsulotlar ← index ishlatilmadi ❌
Filter: (xususiyatlar @> '{"brend": "Dell"}')
Seq Scan ko'rsak — index yo'q yoki ishlatilmayapti. Index Scan yoki Bitmap Index Scan — index ishlayapti.
Tez-tez uchraydigan xatolar
->> bilan qidirishda GIN index ishlamasligini bilmaslik:
-- GIN index bor, lekin bu so'rovda ISHLAMAYDI
SELECT * FROM mahsulotlar
WHERE xususiyatlar ->> 'brend' = 'Dell';
-- Seq Scan!
-- GIN index ISHLAYDI
SELECT * FROM mahsulotlar
WHERE xususiyatlar @> '{"brend": "Dell"}';
-- Bitmap Index Scan ✅
GIN index faqat @>, ?, ?|, ?& operatorlari bilan ishlaydi. ->> bilan qidirish uchun expression index kerak.
Katta jadvalga index qo'shishni kechiktirish:
-- Katta jadvalga index qo'shish vaqt oladi va jadval lock bo'ladi
CREATE INDEX ON mahsulotlar USING GIN (xususiyatlar);
-- Production da bu bir necha daqiqa davom etishi mumkin
-- ✅ CONCURRENTLY — jadval lock bo'lmaydi, sekinroq lekin xavfsiz
CREATE INDEX CONCURRENTLY ON mahsulotlar USING GIN (xususiyatlar);
Production jadvalda index qo'shishda har doim CONCURRENTLY — jadval bloklashmasdan index quriladi.
Xulosa
GIN index — JSONB qidiruvini Seq Scan dan Index Scan ga o'tkazadi. Katta jadvalda yuzlab barobar tezlashtiradi.
Standart GIN → @>, ?, ?|, ?& barchasi uchun
jsonb_path_ops GIN → faqat @>, kichikroq va tezroq
Expression index → bitta kalit bo'yicha qidirish uchun
Partial index → faqat ma'lum qatorlar uchun, hajmi kichik
Qoida: @> bilan JSONB qidiruvingiz bor va jadval katta bo'lsa — GIN index shart. EXPLAIN ANALYZE bilan tekshiring — Seq Scan ko'rsangiz, index qo'shing.
Keyingi dars
24-dars: ARRAY — yaratish, operatorlar, ANY, ALL, unnest