Baza sxemasini to‘xtovsiz o‘zgartirish: expand and contract patterni
Chuqur4 daqInnovialabs tahririyati
Qisqacha
Ishlayotgan sxemani bitta buzuvchi qadamda o‘zgartirmang: avval kengaytiring (eskisi yonida yangi tuzilmalar qo‘shing), ma’lumot va kodni asta-sekin ko‘chiring, keyin qisqartiring — eski qismni hech kim ishlatmay qolganda o‘chiring.
Qisqa javob
Sxemani o‘zgartirish ikki sababga ko‘ra to‘xtab qolishga olib keladi: o‘zgarish paytida so‘rovlarni to‘xtatadigan blokirovkalar va yangi sxemaning hali ishlab turgan ilova kodi bilan mos kelmasligi. Expand and contract patterni ikkalasini ham hal qiladi:
- Expand (kengaytirish): hech narsani o‘chirmasdan yangi ustun, jadval yoki indeks qo‘shasiz. Eski va yangi kod ham ishlaydi.
- Ko‘chirish: ikkala tuzilmaga yozadigan kodni deploy qilasiz, eski ma’lumotlarni qismlab to‘ldirasiz, o‘qishni almashtirasiz.
- Contract (qisqartirish): eski tuzilmani hech bir kod ishlatmay qolganda uni o‘chirasiz.
Har bir qadam — alohida, kichik va orqaga qaytariladigan deploy.
Nolinchi qoida: blokirovka navbatidan himoya
PostgreSQL’da ko‘pchilik ALTER TABLE buyruqlari ACCESS EXCLUSIVE blokirovkasini talab qiladi. O‘zgarishning o‘zi bir zumda bo‘lsa ham, u joriy tranzaksiyalar tugashini kutadi, barcha yangi so‘rovlar esa uning ortidan navbatga turadi. Bitta uzun hisobot jadvalni hamma uchun muzlatib qo‘yishi mumkin.
Migratsiya sessiyasida doim qisqa blokirovka taymautini belgilang va muvaffaqiyatsizlikda qayta urining:
SET lock_timeout = '3s';
SET statement_timeout = '15min';
ALTER TABLE orders ADD COLUMN note text;
Blokirovka o‘z vaqtida olinmasa, migratsiya servisni yiqitmasdan, toza xato bilan tugaydi.
Ustun nomini o‘zgartirish
ALTER TABLE ... RENAME COLUMN bir zumda bajariladi, lekin eski kodning barcha ishlayotgan nusxalarini buzadi. Buni bosqichma-bosqich qiling. Misol: name ustuni full_name ga aylanadi.
- Expand:
ALTER TABLE users ADD COLUMN full_name text; - Ikki tomonlama yozish: ikkala ustunga yozadigan kodni (yoki
namenifull_namega ko‘chiradigan trigger) deploy qiling. - Backfillni uzun tranzaksiya va replika kechikishidan qochish uchun kichik qismlarda bajaring:
UPDATE users SET full_name = name
WHERE id IN (
SELECT id FROM users
WHERE full_name IS NULL
LIMIT 5000
);
-- 0 qator yangilanmaguncha takrorlang
- O‘qishni
full_namega o‘tkazing, keyinnamega yozishni to‘xtating. - Contract:
ALTER TABLE users DROP COLUMN name;— keyingi relizlardan birida.
NOT NULL ustun qo‘shish
Zamonaviy PostgreSQL versiyalarida nullable ustun yoki o‘zgarmas default qiymatli ustun qo‘shish jadvalni qayta yozmaydi — faqat metama’lumot o‘zgaradi. Xavfli qismi — mavjud ustunda NOT NULL ni yoqish: oddiy SET NOT NULL butun jadvalni eksklyuziv blokirovka ostida skanerlaydi.
Xavfsiz ketma-ketlik:
-- 1. Cheklovsiz qo‘shamiz
ALTER TABLE orders ADD COLUMN currency text;
-- 2. Kod maydonni doim to‘ldiradi, eski qatorlarni qismlab to‘ldiramiz
-- 3. Tekshirilmagan CHECK qo‘shamiz (tez)
ALTER TABLE orders ADD CONSTRAINT orders_currency_nn
CHECK (currency IS NOT NULL) NOT VALID;
-- 4. Yozishni bloklamasdan tekshiramiz
ALTER TABLE orders VALIDATE CONSTRAINT orders_currency_nn;
-- 5. Ixtiyoriy: haqiqiy NOT NULL ga aylantiramiz (yangi PostgreSQL
-- versiyalari tekshirilgan CHECK’dan foydalanib, skanerlamaydi)
ALTER TABLE orders ALTER COLUMN currency SET NOT NULL;
ALTER TABLE orders DROP CONSTRAINT orders_currency_nn;
Xuddi shu NOT VALID, keyin VALIDATE usuli foreign key’lar uchun ham ishlaydi.
Indeksni bloklamasdan qurish
Oddiy CREATE INDEX tugaguncha jadvalga yozishni bloklaydi. Katta jadvalda bu uzoq davom etishi mumkin. Buning o‘rniga:
CREATE INDEX CONCURRENTLY idx_orders_customer
ON orders (customer_id);
Bilish kerak bo‘lgan narsalar:
- Buyruq tranzaksiya ichida ishlamaydi, shuning uchun ko‘p migratsiya vositalariga tranzaksiya o‘ramini o‘chiradigan maxsus bayroq kerak.
- U sekinroq va jadvaldan ikki marta o‘tadi.
- Xato bo‘lsa, INVALID indeks qoladi. Uni
DROP INDEX CONCURRENTLYbilan o‘chirib, qayta urining. - O‘chirish va qayta qurish uchun
DROP INDEX CONCURRENTLYvaREINDEX CONCURRENTLYbor.
MySQL InnoDB’da ko‘p amallar online DDL’ni qo‘llab-quvvatlaydi (ALGORITHM=INPLACE, LOCK=NONE), gh-ost yoki pt-online-schema-change kabi vositalar esa jadvalni fonda qayta quradi.
Migratsiyadan oldin tekshiruv ro‘yxati
- O‘zgarish hozir productionda turgan kod bilan orqaga mosmi?
- Qaysi buyruqlar eksklyuziv blokirovka oladi yoki jadvalni qayta yozadi? Aynan shu
ALTERshakli uchun hujjatni tekshiring. lock_timeoutbelgilanganmi va qayta urinish bormi?- Backfill qismlab, pauzalar bilan va replika kechikishini kuzatib bajariladimi?
- Har bir qadamni alohida orqaga qaytarish mumkinmi?
- Migratsiyani production hajmidagi ma’lumot nusxasida sinab ko‘rdingizmi?
Ko‘p uchraydigan xatolar
- Ustun turini joyida o‘zgartirish (
ALTER COLUMN TYPE) — ko‘pincha bu butun jadvalni qayta yozadi. Yangi ustun va backfill yaxshiroq. - Eski nusxalar hali ishlayotgan paytda, kod ustunni ishlatishni to‘xtatgan relizning o‘zida uni o‘chirish.
- Backfill uchun bitta ulkan
UPDATE: jadval shishadi, replikalar orqada qoladi. - ORM ustunlar ro‘yxatini keshlab, o‘chirishdan keyin xato berishi mumkinligini unutish.