Table Partitioning — RANGE, LIST, HASH

~25 daq · PostgreSQL · Index va Performance

buyurtmalar jadvalida 500 million qator. So'rovlar sekinlashgan, index ham yordam bermayapti. Sabab — index juda katta, to'liq skanlanmoqda.

Yechim — jadvalni fizik bo'laklarga bo'lish: Table Partitioning. Har yilgi buyurtmalar alohida bo'lakda — 2024 yil so'rovida faqat 2024 bo'lagi skanlanadi.


Partitioning nima?

Katta jadvalni kichik bo'laklarga (partition) bo'lish. Logik jihatdan bitta jadval, fizik jihatdan ko'p bo'lak.

buyurtmalar (ota jadval)
├── buyurtmalar_2022 (2022 yil qatorlari)
├── buyurtmalar_2023 (2023 yil qatorlari)
└── buyurtmalar_2024 (2024 yil qatorlari)

2024 yil so'rovida faqat buyurtmalar_2024 ko'rib chiqiladi — 10 barobar tez.


RANGE Partitioning — oraliq bo'yicha

Sana, son oraliqlari uchun:

CREATE TABLE buyurtmalar (
  id      BIGSERIAL,
  sana    DATE NOT NULL,
  summa   NUMERIC,
  PRIMARY KEY (id, sana)
) PARTITION BY RANGE (sana);

-- Yillik bo'laklar
CREATE TABLE buyurtmalar_2022
  PARTITION OF buyurtmalar
  FOR VALUES FROM ('2022-01-01') TO ('2023-01-01');

CREATE TABLE buyurtmalar_2023
  PARTITION OF buyurtmalar
  FOR VALUES FROM ('2023-01-01') TO ('2024-01-01');

CREATE TABLE buyurtmalar_2024
  PARTITION OF buyurtmalar
  FOR VALUES FROM ('2024-01-01') TO ('2025-01-01');

So'rov avvalgidek — ota jadvalga:

SELECT * FROM buyurtmalar WHERE sana >= '2024-01-01';
-- Faqat buyurtmalar_2024 skanlanadi — Partition Pruning

LIST Partitioning — qiymat ro'yxati bo'yicha

Mamlakat, mintaqa, kategoriya kabi aniq qiymatlar uchun:

CREATE TABLE buyurtmalar (
  id      BIGSERIAL,
  mamlakat TEXT NOT NULL,
  summa   NUMERIC
) PARTITION BY LIST (mamlakat);

CREATE TABLE buyurtmalar_uz
  PARTITION OF buyurtmalar FOR VALUES IN ('UZ', 'Uzbekistan');

CREATE TABLE buyurtmalar_kz
  PARTITION OF buyurtmalar FOR VALUES IN ('KZ', 'Kazakhstan');

CREATE TABLE buyurtmalar_boshqa
  PARTITION OF buyurtmalar DEFAULT;  -- qolganlar uchun

HASH Partitioning — teng taqsimlash

Ma'lum partitsiya belgisi bo'lmasa — HASH bilan teng taqsimlash:

CREATE TABLE foydalanuvchilar (
  id   BIGSERIAL PRIMARY KEY,
  ism  TEXT
) PARTITION BY HASH (id);

CREATE TABLE foydalanuvchilar_0 PARTITION OF foydalanuvchilar
  FOR VALUES WITH (MODULUS 4, REMAINDER 0);
CREATE TABLE foydalanuvchilar_1 PARTITION OF foydalanuvchilar
  FOR VALUES WITH (MODULUS 4, REMAINDER 1);
CREATE TABLE foydalanuvchilar_2 PARTITION OF foydalanuvchilar
  FOR VALUES WITH (MODULUS 4, REMAINDER 2);
CREATE TABLE foydalanuvchilar_3 PARTITION OF foydalanuvchilar
  FOR VALUES WITH (MODULUS 4, REMAINDER 3);

4 ta bo'lak — ma'lumot teng taqsimlanadi.


Partition Pruning

PostgreSQL WHERE shartiga qarab kerakli bo'laklarni tanlab, boshqalarini o'tkazib yuboradi:

EXPLAIN SELECT * FROM buyurtmalar WHERE sana = '2024-06-15';
Append
  -> Seq Scan on buyurtmalar_2024
       Filter: (sana = '2024-06-15')
-- buyurtmalar_2022 va buyurtmalar_2023 umuman ko'rilmadi

Tez-tez uchraydigan xatolar

PRIMARY KEY partitsiya ustunini o'z ichiga olishi kerak:

-- ❌ — sana PRIMARY KEY da yo'q
CREATE TABLE buyurtmalar (
  id   BIGSERIAL PRIMARY KEY,
  sana DATE
) PARTITION BY RANGE (sana);

-- ✅
PRIMARY KEY (id, sana)  -- sana ham kiradi

Eski jadvalga partitioning qo'shish qiyin:

Mavjud katta jadvalni partitsiyalash — ma'lumotni qayta yozishni talab qiladi. Loyiha boshidan rejalashtirish kerak.


Xulosa

RANGE  — sana, son oraliqlari (yillik, oylik bo'laklar)
LIST   — aniq qiymatlar (mamlakat, kategoriya)
HASH   — teng taqsimlash (aniq belgi yo'q)

Afzalliklar:
  - Partition Pruning — faqat kerakli bo'lak skanlanadi
  - Parallel query    — bo'laklar parallel ishlanadi
  - Eski ma'lumot     — eski bo'lakni tez o'chirish (DROP PARTITION)

Keyingi dars

44-dars: Transaction — ACID, BEGIN, COMMIT, ROLLBACK, SAVEPOINT