Table Partitioning — katta jadvallarni bo'lish

~20 daq · MySQL · Index va Performance

Muammodan boshlaylik

kirish_loglari jadvali — har kuni 2 million yangi qator. 3 yildan keyin — 2 milliard. Muammolar:

  • 1 yildan eski loglarni o'chirish kerak: DELETE FROM kirish_loglari WHERE vaqt < ... — soatlab ishlaydi, jadvalni qulflaydi, replica lar orqada qoladi
  • "Oxirgi 7 kun" so'rovi indeks bilan ham sekinlashib bormoqda — indeksning o'zi ulkan
  • Backup va ALTER TABLE — kunlar

Jadvalni bir nechta mustaqil bo'lakka (partition) bo'lish — bu muammolarning ko'pini hal qiladi.


Partitioning nima?

Tashqaridan — bitta jadval. So'rovlar, INSERT lar o'zgarmaydi. Ichkarida esa MySQL ma'lumotni bir nechta alohida qismga bo'lib saqlaydi — har biri o'z fayli va o'z indekslari bilan.

kirish_loglari (bitta jadval sifatida ko'rinadi)
├── p2025_11   ← 2025-noyabr qatorlari
├── p2025_12
├── p2026_01
├── ...
└── p2026_11   ← joriy oy

Hayotiy o'xshatma: arxivdagi papkalar. Barcha hujjatlarni bitta ulkan qutiga tashlash o'rniga — har oy uchun alohida papka. "Oktabr hujjatlari" kerak bo'lsa — faqat bitta papkani ochasiz. Eski yilni arxivdan chiqarish — papkani olib tashlash, hujjatlarni birma-bir ajratish emas.


RANGE partitioning — sana bo'yicha

Eng ko'p ishlatiladigan tur:

CREATE TABLE kirish_loglari (
    id         BIGINT UNSIGNED AUTO_INCREMENT,
    foydalanuvchi_id INT UNSIGNED NOT NULL,
    ip         VARCHAR(45) NOT NULL,
    vaqt       DATETIME NOT NULL,
    PRIMARY KEY (id, vaqt),                -- ← partition ustuni PK da bo'lishi SHART
    INDEX idx_foyd_vaqt (foydalanuvchi_id, vaqt)
)
PARTITION BY RANGE COLUMNS (vaqt) (
    PARTITION p2026_09 VALUES LESS THAN ('2026-10-01'),
    PARTITION p2026_10 VALUES LESS THAN ('2026-11-01'),
    PARTITION p2026_11 VALUES LESS THAN ('2026-12-01'),
    PARTITION p_kelajak VALUES LESS THAN (MAXVALUE)
);

Har bir qator vaqt ga qarab mos partition ga tushadi. p_kelajak — xavfsizlik uchun: kutilmagan kelajak sanasi xato bermasin.

Asosiy cheklov

[!WARNING] Partition ustuni jadvaldagi har bir PRIMARY KEY va UNIQUE kalitning tarkibida bo'lishi shart. Shuning uchun PRIMARY KEY (id) emas, PRIMARY KEY (id, vaqt). Natijada UNIQUE (email) kabi global noyoblikni partitioned jadvalda ta'minlab bo'lmaydi.

Sababi: har bir partition — alohida indeks. MySQL "bu email boshqa partition da bormi?" ni tekshirish uchun hamma partition larni ko'rib chiqishi kerak bo'lardi.

Yana bir cheklov: partitioned jadvallarda foreign key ishlatib bo'lmaydi (InnoDB).


Partition pruning — asosiy foyda

EXPLAIN SELECT COUNT(*) FROM kirish_loglari
WHERE vaqt >= '2026-11-01' AND vaqt < '2026-11-08';
partitions: p2026_11

MySQL faqat bitta partition ni o'qiydi — qolganlarini umuman ochmaydi. Bu pruning (kesib tashlash). 2 milliard qatorli jadval o'rniga — 60 million qatorli bo'lak.

Pruning faqat so'rovda partition ustuni bo'yicha shart bo'lganda ishlaydi:

SELECT * FROM kirish_loglari WHERE foydalanuvchi_id = 42;
-- partitions: p2026_09,p2026_10,p2026_11,p_kelajak — HAMMASI o'qiladi

Shuning uchun partition kalitini eng ko'p filtrlanadigan ustundan tanlang.


Eski ma'lumotni o'chirish — bir zumda

Partitioning ning eng katta amaliy foydasi:

-- ❌ Soatlab, qulflar, replication kechikishi
DELETE FROM kirish_loglari WHERE vaqt < '2026-10-01';

-- ✅ Bir zumda — fayl shunchaki o'chiriladi
ALTER TABLE kirish_loglari DROP PARTITION p2026_09;

60 million qatorni o'chirish — millisekundlar.

Yangi oy uchun partition qo'shish — p_kelajak ni bo'lish:

ALTER TABLE kirish_loglari REORGANIZE PARTITION p_kelajak INTO (
    PARTITION p2026_12 VALUES LESS THAN ('2027-01-01'),
    PARTITION p_kelajak VALUES LESS THAN (MAXVALUE)
);

Bu ikki amal odatda avtomatlashtiriladi — har oy Event Scheduler (44-dars) yoki cron orqali: yangi partition qo'shish, eng eskisini o'chirish.

Partitionni o'chirmasdan arxivga ko'chirish:

CREATE TABLE loglar_arxiv_2026_09 LIKE kirish_loglari;
ALTER TABLE loglar_arxiv_2026_09 REMOVE PARTITIONING;
ALTER TABLE kirish_loglari EXCHANGE PARTITION p2026_09 WITH TABLE loglar_arxiv_2026_09;

Boshqa turlar

LIST — aniq qiymatlar bo'yicha

PARTITION BY LIST COLUMNS (viloyat) (
    PARTITION p_toshkent  VALUES IN ('Toshkent', 'Toshkent viloyati'),
    PARTITION p_vodiy     VALUES IN ('Andijon', 'Farg''ona', 'Namangan'),
    PARTITION p_boshqa    VALUES IN ('Samarqand', 'Buxoro', 'Xorazm', ...)
);

HASH / KEY — teng taqsimlash

PARTITION BY HASH (foydalanuvchi_id) PARTITIONS 8;

Qatorlar 8 ta bo'lakka teng taqsimlanadi. Pruning faqat foydalanuvchi_id = ? aniq qiymatida ishlaydi. Eski ma'lumotni o'chirish afzalligi yo'q.


Partitioning'ni ko'rish

SELECT PARTITION_NAME, TABLE_ROWS, PARTITION_DESCRIPTION
FROM information_schema.PARTITIONS
WHERE TABLE_SCHEMA = 'dokon' AND TABLE_NAME = 'kirish_loglari';

Qachon kerak — va qachon kerak emas

✅ Kerak:

  • Juda katta (yuzlab million+ qator), vaqt bo'yicha o'sadigan jadvallar: loglar, hodisalar, tranzaksiyalar tarixi, sensor ma'lumotlari
  • Eski ma'lumot muntazam o'chiriladi yoki arxivlanadi (retention)
  • So'rovlarning katta qismi partition kaliti bo'yicha filtrlanadi ("oxirgi 30 kun")

❌ Kerak emas:

  • Bir necha million qator — oddiy yaxshi indeks yetarli
  • So'rovlar partition kaliti bo'yicha filtrlanmaydi — pruning ishlamaydi, aksincha sekinlashadi (har bir partition ni alohida ko'rish)
  • Global UNIQUE yoki foreign key lar kerak

[!INFO] Partitioning — tezlashtirish vositasi emas, boshqaruv vositasi. U indeks o'rnini bosmaydi. Uning asosiy foydasi — ulkan jadvallarni boshqarish: eski ma'lumotni bir zumda o'chirish, kichikroq indekslar, qismlab texnik xizmat. Tezlik ko'pincha qo'shimcha natija.


Nima uchun shunday?

Nima uchun DROP PARTITION bir zumda, DELETE esa soatlab ishlaydi?

DELETE har bir qatorni alohida o'chiradi: uni "o'chirilgan" deb belgilaydi, har bir indeksdan olib tashlaydi, undo log ga yozadi (rollback uchun), binary log ga yozadi (replication uchun), qulflarni boshqaradi. 60 million qator × bularning hammasi. DROP PARTITION esa butun bir faylni o'chiradi — xuddi DROP TABLE kabi. Qatorlarni birma-bir ko'rib chiqish yo'q. Farq — "kitobdan har bir sahifani yirtib olish" va "butun kitobni javondan olish".


Xulosa

Eslab qoling:

  • Partitioning — bitta jadval, ichkarida alohida bo'laklar
  • RANGE COLUMNS (sana) — eng ko'p ishlatiladi: oylar yoki kunlar bo'yicha
  • Partition ustuni har bir PRIMARY KEY / UNIQUE da bo'lishi shart; foreign key yo'q
  • Pruning — so'rovda partition kaliti bo'yicha shart bo'lsa, faqat kerakli bo'lak o'qiladi
  • DROP PARTITION — eski ma'lumotni bir zumda o'chirish
  • REORGANIZE PARTITION — yangi davr qo'shish; odatda avtomatlashtiriladi
  • Faqat ulkan, vaqt bo'yicha o'sadigan jadvallar uchun

Keyingi dars

36-dars: Transaction — ACID, BEGIN, COMMIT, ROLLBACK