Mundarija (28)
- Bu darsda
- 1. Nega bu kerak?
- 2. Streaming replication
- 2.1 Primary va replika
- 2.2 Kanon replikani qanday ko'tardi
- 2.3 Replika ekanini qanday bilish
- 3. Replikatsiya lagi
- 3.1 Ta'rif
- 3.2 Kanon o'lchovi: yozib, darhol o'qish
- 3.3 Katta tranzaksiya — katta lag
- 4. Read-your-writes muammosi
- 4.1 Voqea
- 4.2 Uch yechim
- 4.3 Sticky yo'naltirishni modellash
- 5. Ilovada yo'naltirish
- 5.1 Ikki hovuz
- 5.2 Prisma va readReplicas
- 5.3 Replikadagi uzoq so'rovlar
- 6. Mantiqiy replikatsiya
- 7. Ko'p uchraydigan xatolar
- 8. Mashqlar
- 1-mashq (oson): bo'shliqlarni to'ldiring
- 2-mashq (o'rta): LSN farqi JavaScript'da
- 3-mashq (qiyin): LSN bo'yicha yo'naltirish
- 9. Loyiha qadami: kanon tajribasi
- 10. Real ishda
- Xulosa
- Manbalar
Replikatsiya va read replica'lar: PostgreSQL streaming replication va replikatsiya lagi
Qisqacha: Replikatsiya — bazaning jonli nusxasini boshqa serverda ushlab turish. Postgres'da asosiy server (primary) har o'zgarishini WAL ko'rinishida replikaga uzatadi, replika uni qayta o'ynaydi va faqat o'qish uchun ochiq turadi. O'qishni replikaga yuborsak, primary yengillashadi. Lekin replika doim biroz orqada — bu replikatsiya lagi. Kanon o'lchovida yozilgan qator darhol replikadan o'qilganda 300 tadan 298 tasida topilmadi. Yechim — "o'zi yozgan foydalanuvchini bir muddat primary'dan o'qitish" (0/300) yoki sinxron replikatsiya (sekinroq).
Bu darsda
- Primary, replika va streaming replication qanday ishlashini chizib tushuntirasiz.
- Replikatsiya lagi nima ekanini va uni baytlarda qanday o'lchashni bilasiz.
- "Yozdim, lekin ko'rinmayapti" (read-your-writes) muammosini kanon o'lchovida ko'rasiz.
- O'qishni yo'naltirishning uch usulini solishtirasiz va bittasini brauzerda modellab ko'rasiz.
- Fizik va mantiqiy (logical) replikatsiya farqini bilasiz.
Oldin bilishingiz kerak: Postgres arxitekturasi va asosiy sozlamalar, Backup va restore, Postgres versiyasini yangilash, Connection pooling.
Oldingi ikki darsda boshqa bazalarni ko'rdik. Endi Postgres'ga qaytamiz va savolni o'zgartiramiz: bitta server yetmay qolsa yoki yiqilsa nima bo'ladi? Bu dars — birinchi yarmi: nusxa va o'qishni bo'lish. Keyingi darsda — yiqilish va tiklash.
1. Nega bu kerak?
vazifalar-api ni tasavvur qiling, lekin 100 000 foydalanuvchi bilan. Har kim ertalab ilovani ochadi va ro'yxatini ko'radi. Vazifa qo'shish esa kamroq. Odatda bunday ilovalarda o'qish yozishdan ko'p marta ko'p bo'ladi.
Hamma so'rov bitta Postgres serverga boradi. CPU 90 % da. Indekslar qo'yilgan, so'rovlar optimallashtirilgan (EXPLAIN ANALYZE darsi), ulanishlar hovuzda (Connection pooling). Keyingi qadam — o'qishni boshqa serverga bo'lish.
O'xshatish: maktab devoridagi dars jadvali. Uni faqat direktor o'rinbosari o'zgartiradi (primary). Lekin har qavatda nusxasi osilgan (replikalar) — o'quvchilar o'z qavatida o'qiydi, birinchi qavatga tushmaydi. Jadval o'zgarsa, nusxalar biroz keyin yangilanadi. Shu "biroz keyin" — bu darsning asosiy mavzusi.
2. Streaming replication
2.1 Primary va replika
Primary — yozish qabul qiladigan asosiy server. Replika (replica) — primary'ning nusxasi: u o'zgarishlarni primary'dan oladi va o'qish so'rovlariga javob beradi. Postgres hujjati replikani standby deb ham ataydi, faqat o'qishga ochiq replikani esa hot standby.
Nusxa qanday yangilanadi? Postgres arxitekturasi darsidan bilasiz: Postgres har o'zgarishni avval WAL'ga yozadi. Streaming replication — shu WAL yozuvlarini tarmoq orqali replikaga oqim qilib yuborish. Replika ularni qayta o'ynaydi — xuddi PITR da arxivdan o'ynagandek, faqat to'xtovsiz.
flowchart LR
I["Ilova"] -->|"yozish"| P[("Primary")]
I -->|"o'qish"| R[("Replika")]
P --> WS["WAL sender"]
WS -->|"WAL oqimi"| WR["WAL receiver"]
WR --> RP["Qayta o'ynash"]
RP --> RDiagrammani chapdan o'qing. Ilova yozuvni primary'ga, o'qishni replikaga yuboradi. Primary ichidagi WAL sender jarayoni har yangi WAL yozuvini replikadagi WAL receiver ga jo'natadi. Replika uni o'z fayllariga qo'llaydi. Ilova replikadan o'qiganda, u qo'llangan yozuvlarnigina ko'radi.
Bu replikatsiya fizik: bayt darajasida butun klaster ko'chiriladi — hamma bazalar, hamma jadvallar, indekslar ham. Shuning uchun primary va replikaning asosiy versiyasi (major) bir xil bo'lishi shart.
2.2 Kanon replikani qanday ko'tardi
Kanon primary'ning yoniga ikkinchi Postgres 18 konteynerini qo'ydi (faqat lokal port). Asosiy buyruqlar kanon skriptidan (Docker qismi olib tashlandi, qatorlar bo'lingan). Bu buyruqlar o'quvchiga majburiy emas — 33-qismda Docker Compose'ni o'rganganingizdan keyin ularni takrorlash oson bo'ladi:
# primary'da: replikatsiya ulanishiga ruxsat
# (trust — FAQAT lokal tajriba uchun; prodakshnda parol)
echo "host replication all all trust" >> "$PGDATA/pg_hba.conf"
psql -U postgres -c "SELECT pg_reload_conf()"
# replika serverida: primary'dan asosiy nusxa olish
pg_basebackup -h pg-primary -U postgres -D "$PGDATA" \
-R -X stream -C -S r1 -c fast
# replikani ishga tushirish
postgres -c hot_standby=on -c hot_standby_feedback=onpg_basebackup ni Backup va restore darsidan bilasiz. Bu yerda yangi bayroqlar:
| Bayroq | Ma'nosi |
|---|---|
-R |
replika sozlamasini yozadi: standby.signal fayli va primary_conninfo |
-X stream |
nusxa olish paytidagi WAL ham oqim bilan olinadi |
-C -S r1 |
primary'da r1 nomli replikatsiya sloti yaratadi |
-c fast |
darhol checkpoint — nusxa tezroq boshlanadi |
Replikatsiya sloti (replication slot) — primary'dagi "bu replika qayergacha o'qidi" degan belgi. Slot bor ekan, primary replikaga hali kerak bo'lgan WAL fayllarini o'chirmaydi. Bu yaxshi: replika qisqa uzilsa ham, keyin davom etadi. Xavfi ham bor — keyingi bo'limda.
pg-primary — primary serverning tarmoqdagi nomi. Kanon pg_basebackup -R yozgan ulanish satriga application_name=r1 ni ham qo'lda qo'shdi — primary replikani shu nom bilan ko'rsatishi uchun.
2.3 Replika ekanini qanday bilish
Har serverda pg_is_in_recovery() funksiyasi bor. Primary'da u f (false) qaytaradi. Bizning oddiy serverimizda (biz ishga tushirdik, PostgreSQL 17.6):
SELECT pg_is_in_recovery();
SHOW wal_level; pg_is_in_recovery
-------------------
f
(1 row)
wal_level
-----------
replica
(1 row)Replikada esa xuddi shu funksiya t qaytaradi — server hamon "tiklanish" rejimida, WAL'ni o'ynab yotibdi. wal_level = replica — sukut qiymat: WAL'da replikatsiya uchun yetarli ma'lumot bor. Ya'ni zamonaviy Postgres replikaga sukut bo'yicha tayyor.
Primary'da replikalar holati pg_stat_replication ko'rinishida. Kanonning keyingi darsdagi juftligida so'rov shunday javob berdi:
replikatsiya: ha2 streaming asyncUch so'z: replika nomi (ha2), holati (streaming — oqim ishlayapti) va rejimi (async — asinxron, ya'ni primary replikani kutmaydi). Asinxron va sinxron farqi — shu dars oxirida va keyingi darsda.
Tekshirib ko'ring: Replikaga
INSERTyuborsak nima bo'ladi?
Javob
Rad etiladi. Kanon chiqishi: 25006 — cannot execute INSERT in a read-only transaction. Replika — hot standby, faqat o'qish uchun. 25006 — SQLSTATE kodi, "faqat o'qish tranzaksiyasi". Ilovangiz bu xatoni ko'rsa, demak yozish so'rovi adashib replikaga ketgan.
3. Replikatsiya lagi
3.1 Ta'rif
Replikatsiya lagi (replication lag) — replika primary'dan qancha orqada ekani. Uni ikki xil o'lchaydilar: vaqtda (replika qancha millisekund kech ko'rsatadi) va baytlarda (hali qancha WAL qo'llanmagan).
WAL'dagi har joyning manzili bor — LSN (log sequence number), masalan 0/3000000. LSN — WAL boshidan beri necha bayt yozilgani, o'n oltilik sanoqda. Ikki LSN farqi — baytlardagi masofa:
SELECT pg_wal_lsn_diff('0/3000000', '0/2000000') AS bayt; bayt
----------
16777216
(1 row)16 777 216 bayt — aynan 16 MB, ya'ni bitta WAL fayli. Primary'da pg_current_wal_lsn() (hozir qayergacha yozildi) va pg_stat_replication dagi replay_lsn (replika qayergacha qo'lladi) farqi — baytlardagi lag.
3.2 Kanon o'lchovi: yozib, darhol o'qish
Kanon eng oddiy ilova xatti-harakatini o'lchadi: vazifani primary'ga yozish va darhol replikadan o'qish. 300 marta. Skriptning yadrosi (qisqartirilgan — foizlarni hisoblash olib tashlandi):
async function ryw(n, oqish) {
let topilmadi = 0;
const korinish = [];
for (let i = 0; i < n; i++) {
const { rows: [{ id }] } = await P.query(
"INSERT INTO rep_sinov (matn) VALUES ($1) RETURNING id",
[`vazifa ${i}`],
);
const t0 = performance.now();
let birinchi = true;
for (;;) {
const { rowCount } = await oqish(id);
if (rowCount === 1) break;
if (birinchi) { topilmadi++; birinchi = false; }
}
korinish.push(performance.now() - t0);
}
return { topilmadi, korinish };
}P — primary hovuzi. oqish — replikadan SELECT qiladigan funksiya. Ichki sikl qator replikada paydo bo'lguncha qayta so'raydi va kutish vaqtini yozadi. topilmadi — birinchi o'qishda qator yo'q bo'lgan holatlar soni. Natijada ikki narsaga qarang: topilmadi va p50 (o'lchovlarning yarmi shundan tez, yarmi sekin):
1) yozish (primary) → darhol o'qish (replika), 300 marta: { topilmadi: 298, p50: '4.59', p99: '6.99', maks: '14.34' } ms300 tadan 298 tasida birinchi o'qish qatorni topmadi. Qator odatda 4.6 ms dan keyin paydo bo'ldi. Bu bitta kompyuterdagi ikki konteyner — tarmoq deyarli yo'q. Haqiqiy serverlar orasida lag kattaroq bo'ladi.
- p50 (mediana)4,59 ms
- p996,99 ms
- eng uzoq14,34 ms
Manba: IlmHamroh kanon o'lchovi, 2026-11-02: PostgreSQL 18.6, primary va replika — bitta mashinadagi ikki Docker konteyneri, asinxron streaming replication
Grafikdagi uch ustunni shunday o'qing: yarim holatda qator 4.59 ms ichida ko'rindi, 99 % holatda — 7 ms ichida, eng yomon holatda — 14 ms. Millisekundlar oz tuyuladi. Lekin ilova yozgandan keyin 1 ms da o'qiydi — va bo'sh ro'yxatni ko'radi.
3.3 Katta tranzaksiya — katta lag
Ikkinchi o'lchov: primary'da bitta tranzaksiya 200 000 qator yozdi.
3) 200 000 qator: primary 630 ms; commit paytida replika orqada 10923640 bayt; replika yetib oldi: +131 msCommit paytida replika 10 923 640 bayt (~10.9 MB) WAL orqada edi. Replika uni 131 ms da qo'llab, yetib oldi. Katta import, ommaviy UPDATE yoki indeks qurish paytida lag sakraydi — bu paytda replikadan o'qigan foydalanuvchi eski ma'lumotni ko'radi.
Diqqat: Replikatsiya sloti bilan bog'liq xavf: replika uzoq vaqt o'chib qolsa, slot primary'da WAL'ni ushlab turaveradi va disk to'ladi. Postgres'da
max_slot_wal_keep_sizesozlamasi slot ushlay oladigan WAL hajmini cheklaydi. Kerak bo'lmagan slotnipg_drop_replication_slot()bilan o'chiring — kanon ham tajriba oxirida shunday qildi. Slot WAL'ni qanday yig'ishini CDC darsida o'lchaymiz.
4. Read-your-writes muammosi
4.1 Voqea
Aziz telefonda "Non olish" vazifasini qo'shdi. Ilova POST qildi — 201 keldi. Keyin ro'yxatni yangiladi — GET replikaga ketdi. Ro'yxatda "Non olish" yo'q. Aziz yana qo'shadi. Endi ikkita "Non olish" bor.
Bu — read-your-writes (o'z yozganingni o'qish) kafolatining buzilishi. Foydalanuvchi o'zi yozgan narsani darhol ko'rishini kutadi. Boshqa foydalanuvchining yangi vazifasini 5 ms kech ko'rish — muammo emas. O'zinikini ko'rmaslik — muammo.
sequenceDiagram
participant A as Aziz ilovasi
participant P as Primary
participant R as Replika
A->>P: POST "Non olish"
P-->>A: 201 Created
A->>R: GET ro'yxat
R-->>A: eski ro'yxat
P->>R: WAL (5 ms kech)Diagrammada oxirgi strelka muhim: WAL replikaga Aziz o'qigandan keyin yetib keldi. Oxir-oqibat izchillik (eventual consistency) degani shu: baribir yetib keladi, lekin "darhol" emas.
4.2 Uch yechim
| Usul | G'oya | Narxi |
|---|---|---|
| Sticky yo'naltirish | yozgan foydalanuvchi N soniya primary'dan o'qiydi | primary'ga yuk qaytadi; N ni taxmin qilish kerak |
| LSN bo'yicha kutish | yozishdan keyin LSN eslab qolinadi; replika unga yetgandagina undan o'qiladi | murakkabroq kod |
Sinxron replikatsiya (remote_apply) |
primary commit'ni replika qo'llaguncha tasdiqlamaydi | har yozish sekinlashadi |
Kanon birinchi va uchinchisini o'lchadi:
2) sticky yo'naltirish (yozgandan 2 s ichida primary): { topilmadi: 0, p50: '1.67', p99: '3.57', maks: '7.45' }
remote_apply bilan 1-o'lchov qayta: { topilmadi: 0, p50: '2.36', p99: '9.48', maks: '9.48' }Ikkalasida ham topilmadi: 0. Sticky usulda 300 tadan, remote_apply bilan 100 tadan bittasi ham yo'qolmadi. remote_apply ning narxini — har commit qanchaga sekinlashganini — keyingi darsda ko'ramiz.
LSN usulining g'oyasini ham kanon ishlatdi — katta tranzaksiya o'lchovida replika qachon yetib olganini aynan shunday tekshirdi:
SELECT pg_last_wal_replay_lsn() >= $1::pg_lsn AS ok;$1 — primary'da yozishdan keyin olingan pg_current_wal_lsn(). Replikada qo'llangan LSN undan katta yoki teng bo'lsa — replika yozuvni allaqachon ko'radi.
4.3 Sticky yo'naltirishni modellash
Sticky g'oyasi kichik. Uni brauzerda soxta soat bilan modellaymiz: vaqt — oddiy son (millisekund), replika har yozuvni 5 ms kech ko'radi.
const LAG_MS = 5; // replika primary'dan shuncha orqada
const yozuvlar = new Map(); // id → primary'ga yozilgan vaqt
const oxirgiYozish = new Map(); // foydalanuvchi → vaqt
function yoz(kim, id, hozir) {
yozuvlar.set(id, hozir);
oxirgiYozish.set(kim, hozir);
}
function manba(kim, hozir, oyna) {
const t = oxirgiYozish.get(kim);
const yaqinda = t !== undefined && hozir - t < oyna;
return yaqinda ? "primary" : "replika";
}
function oqi(kim, id, hozir, oyna) {
const qayer = manba(kim, hozir, oyna);
const vaqt = yozuvlar.get(id);
const kechikish = qayer === "primary" ? 0 : LAG_MS;
const bor = vaqt !== undefined && hozir >= vaqt + kechikish;
const holat = bor ? "bor" : "YO'Q";
console.log(`${hozir} ms ${kim} → ${qayer}: ${holat}`);
}
console.log("— yo'naltirishsiz (hamma o'qish replikadan)");
yoz("Aziz", 1, 1000);
oqi("Aziz", 1, 1001, 0);
oqi("Aziz", 1, 1006, 0);
console.log("— sticky: yozgandan 2 s ichida primary'dan");
yoz("Aziz", 2, 5000);
oqi("Aziz", 2, 5001, 2000);
oqi("Malika", 2, 5001, 2000);
oqi("Aziz", 2, 7500, 2000);Konsolda:
— yo'naltirishsiz (hamma o'qish replikadan)
1001 ms Aziz → replika: YO'Q
1006 ms Aziz → replika: bor
— sticky: yozgandan 2 s ichida primary'dan
5001 ms Aziz → primary: bor
5001 ms Malika → replika: YO'Q
7500 ms Aziz → replika: borQatorma-qator:
- Yo'naltirishsiz: Aziz 1 ms dan keyin o'qidi — replikada yo'q. 6 ms dan keyin — bor. Kanon o'lchovidagi 298/300 aynan shu.
- Sticky: Aziz yozgandan keyin 2 soniya primary'dan o'qiydi — o'z vazifasini darhol ko'radi.
- Malika replikadan o'qidi va Azizning vazifasini hali ko'rmadi. Bu — normal: u buni kutmagan.
- 2.5 soniyadan keyin Aziz yana replikaga qaytdi — lag allaqachon o'tgan.
Model haqiqiy kodda qayerda soddalashgan: oxirgiYozish bitta jarayonning xotirasida. Server bir nechta nusxada ishlasa, bu ma'lumot umumiy joyda (Redis yoki sessiya cookie'si) turishi kerak. Sticky so'zi sizga Realtime'ni masshtablash darsidagi sticky session'dan tanish — g'oya bir xil: "bu foydalanuvchini ma'lum muddat bir joyga bog'lash".
Tekshirib ko'ring: Sticky oynani 2 soniya o'rniga 10 ms qilsak nima bo'ladi?
Javob
Odatda ishlaydi — kanonda lag p99 7 ms edi. Lekin eng yomon holat 14 ms bo'ldi, katta tranzaksiyada esa 131 ms. Oyna lagdan qisqa bo'lsa, read-your-writes yana buziladi. Oyna lagning eng yomon qiymatidan bir necha baravar katta bo'lishi kerak. Bir-ikki soniya — ishonchli oraliq; primary'ga qo'shimcha yuk esa faqat yaqinda yozganlardan keladi.
5. Ilovada yo'naltirish
5.1 Ikki hovuz
Eng oddiy amalga oshirish — ikkita ulanish hovuzi: biri primary'ga, biri replikaga. Kanon tajriba skriptida aynan shunday qildi:
import pg from "pg";
const P = new pg.Pool({
connectionString: "postgres://postgres@127.0.0.1:5496/postgres",
max: 4,
});
const R = new pg.Pool({
connectionString: "postgres://postgres@127.0.0.1:5497/postgres",
max: 4,
});Qoidalar:
- Yozish va tranzaksiya — doim primary. Tranzaksiya ichidagi o'qish ham primary'dan (aks holda o'qish va yozish boshqa-boshqa holatni ko'radi).
- Ro'yxat, qidiruv, hisobot — replika.
- Yozgandan keyingi o'qish — sticky yoki LSN qoidasi bo'yicha.
- Replika ishlamasa — primary'ga qaytish (sekinroq, lekin ishlaydi).
5.2 Prisma va readReplicas
Prisma uchun rasmiy @prisma/extension-read-replicas kengaytmasi bor (Prisma Client extensions). Hujjatga ko'ra u o'qish so'rovlarini replikaga, yozish va tranzaksiyalarni primary'ga yuboradi; $primary() bilan o'qishni majburan primary'dan qilish mumkin. Kanon uni sinamadi — Prisma 7 va driver adapter bilan mosligi tekshirilmagan. Ishlatishdan oldin joriy hujjatni o'qing va read-your-writes'ni o'zingiz hal qilishingiz kerakligini unutmang: kengaytma sticky qoidani o'zi bilmaydi.
5.3 Replikadagi uzoq so'rovlar
Replikada hisobot so'rovi 10 daqiqa yurayapti, deylik. Shu payt primary'da VACUUM eski qator versiyalarini tozaladi va bu o'zgarish WAL bilan replikaga keldi. Replika qiyin tanlov oldida: WAL'ni qo'llasa — so'rov o'qiyotgan qatorlar yo'qoladi; qo'llamasa — lag o'sadi. Hujjatga ko'ra replika ma'lum vaqt kutadi (max_standby_streaming_delay), keyin so'rovni bekor qiladi: canceling statement due to conflict with recovery.
Kanon replikasi hot_standby_feedback = on bilan ishladi. Bu sozlama replikadagi so'rovlar hali kerak bo'lgan versiyalarni primary'ga xabar qiladi va primary ularni tozalamay turadi. Narxi — primary'da bloat (VACUUM darsi).
6. Mantiqiy replikatsiya
Fizik replikatsiya butun klasterni bayt-bayt ko'chiradi. Mantiqiy replikatsiya (logical replication) esa o'zgarishlarni "qaysi jadvalda qaysi qator qanday o'zgardi" ko'rinishida uzatadi. Primary'da publication (nashr) e'lon qilinadi, qabul qiluvchida subscription (obuna) yaratiladi.
| Fizik | Mantiqiy | |
|---|---|---|
| Nima ko'chadi | butun klaster | tanlangan jadvallar |
| Versiyalar | bir xil asosiy versiya | farqli bo'lishi mumkin |
| Qabul qiluvchi | faqat o'qish | yozish ham mumkin |
| Asosiy ishlatilishi | replika, zaxira server | versiya yangilash, ma'lumot uzatish |
Postgres versiyasini yangilash darsidagi "minimal to'xtash bilan yangilash" usuli aynan mantiqiy replikatsiyaga tayanadi. O'zgarishlarni Postgres'dan boshqa tizimlarga (qidiruv indeksi, analitika) uzatishni CDC va ma'lumot sinxronlash darsida kanon o'lchovi bilan ko'ramiz.
7. Ko'p uchraydigan xatolar
| Belgi | Sabab | Davosi |
|---|---|---|
| Foydalanuvchi o'z yozuvini ko'rmayapti | o'qish replikadan, lag bor | sticky, LSN yoki remote_apply |
cannot execute INSERT in a read-only transaction |
yozish replikaga ketgan | yozish va tranzaksiya — faqat primary |
canceling statement due to conflict with recovery |
replikadagi uzoq so'rov va VACUUM | hot_standby_feedback, hisobot uchun alohida replika |
| Primary diski to'ldi | o'chgan replikaning sloti WAL'ni ushlab turibdi | max_slot_wal_keep_size, keraksiz slotni o'chirish |
| Replika qo'shildi, lekin tezlik oshmadi | tor joy — yozish | replika faqat o'qishni yengillatadi |
Oxirgi qator haqida: replika yozishni tezlashtirmaydi. Har yozuv baribir primary'da bajariladi, ustiga WAL hamma replikalarga yuboriladi. Yozish ko'p bo'lsa, boshqa yo'llar kerak: Partitioning va Sharding darslari.
8. Mashqlar
Mashqlar kurs/mashqlar/31/03-replikatsiya/ da. 2- va 3-mashq yechimlari Node 24.21.0 da tekshirilgan (biz ishga tushirdik).
1-mashq (oson): bo'shliqlarni to'ldiring
- Yozish qabul qiladigan asosiy server —
- Replikada
pg_is_in_recovery()qaytaradigan qiymat — - WAL'dagi joyning manzili (ikki LSN farqi — baytlarda) —
- Kanon o'lchovida 300 ta "yozib darhol o'qish" dan nechtasida qator topilmadi? —
- Replikaga yozishda chiqadigan SQLSTATE kodi —
Yechim
- primary — replikalar undan WAL oladi.
- t — replika hamon WAL'ni o'ynayapti; primary'da
f. - LSN — log sequence number;
pg_wal_lsn_diff()farqni baytda beradi. - 298 — faqat 2 tasida qator darhol bor edi; sticky bilan 0.
- 25006 — "read-only transaction".
2-mashq (o'rta): LSN farqi JavaScript'da
Monitoring paneli pg_stat_replication dan LSN'larni satr ko'rinishida oladi: "16/B374D848". lsnSon(lsn) va lsnFarqi(a, b) funksiyalarini yozing. LSN — ikki o'n oltilik son, / bilan ajratilgan: chap qism — yuqori 32 bit, o'ng qism — quyi 32 bit. Natija 2⁵³ dan oshishi mumkin, shuning uchun BigInt ishlating. Ishora: BigInt("0x" + qism), << 32n.
Yechim
function lsnSon(lsn) {
const [yuqori, quyi] = lsn.split("/");
return (BigInt("0x" + yuqori) << 32n) + BigInt("0x" + quyi);
}
const lsnFarqi = (a, b) => lsnSon(a) - lsnSon(b);
console.log(lsnFarqi("0/3000000", "0/2000000"));
console.log(lsnFarqi("1/0", "0/FFFFFFFF"));
console.log(lsnSon("16/B374D848"));Konsolda:
16777216n
1n
97500059720nBirinchi natija darsdagi pg_wal_lsn_diff('0/3000000', '0/2000000') bilan bir xil — 16 MB. Ikkinchisi chegarani tekshiradi: quyi qism FFFFFFFF dan keyin yuqori qism bittaga oshadi, farq — 1 bayt. Uchinchisini Postgres bilan solishtirdik: pg_wal_lsn_diff('16/B374D848', '0/0') ham 97500059720 qaytardi.
3-mashq (qiyin): LSN bo'yicha yo'naltirish
Sticky usul vaqtni taxmin qiladi. Aniqroq usul — LSN. Model yozing: Klaster — primaryLsn va replikaLsn (BigInt); yoz() primary LSN'ni 100 ga oshirib qaytaradi; replikaYetibOldi(lsn) replikani siljitadi. LsnYonaltiruvchi — har foydalanuvchining oxirgi yozuv LSN'ini eslaydi va qayerdan(kim): replika shu LSN'ga yetgan bo'lsa "replika", aks holda "primary" qaytaradi. Hech narsa yozmagan foydalanuvchi — doim replika. Ishora: klass maydonlari va # xususiy maydon (private # maydonlar), ?? 0n (??).
Yechim
class Klaster {
primaryLsn = 0n;
replikaLsn = 0n;
yoz() {
this.primaryLsn += 100n;
return this.primaryLsn;
}
replikaYetibOldi(lsn) {
this.replikaLsn = lsn;
}
}
class LsnYonaltiruvchi {
#oxirgi = new Map(); // foydalanuvchi → oxirgi yozuv LSN
constructor(klaster) {
this.k = klaster;
}
yoz(kim) {
this.#oxirgi.set(kim, this.k.yoz());
}
qayerdan(kim) {
const kerak = this.#oxirgi.get(kim) ?? 0n;
return this.k.replikaLsn >= kerak ? "replika" : "primary";
}
}
const k = new Klaster();
const y = new LsnYonaltiruvchi(k);
y.yoz("Aziz");
const ikkalasi = () =>
`Aziz: ${y.qayerdan("Aziz")} | Malika: ${y.qayerdan("Malika")}`;
console.log(ikkalasi());
k.replikaYetibOldi(100n);
console.log("Aziz:", y.qayerdan("Aziz"));
y.yoz("Aziz");
y.yoz("Malika");
k.replikaYetibOldi(200n);
console.log(ikkalasi());Konsolda:
Aziz: primary | Malika: replika
Aziz: replika
Aziz: replika | Malika: primaryOxirgi qatorga qarang: Aziz LSN 200 da, Malika LSN 300 da yozdi. Replika 200 ga yetdi — Aziz uchun yetarli, Malika uchun yo'q. Sticky usul ikkalasini ham "2 soniya primary" qilardi. LSN usuli esa replikani imkon qadar ko'p ishlatadi. Haqiqiy kodda LSN yozishdan keyin pg_current_wal_lsn() bilan olinadi va sessiyada saqlanadi, replikaning holati esa pg_last_wal_replay_lsn() dan.
9. Loyiha qadami: kanon tajribasi
Bu darsda vazifalar-api o'zgarmaydi va commit yo'q. Loyiha hozir bitta lokal bazada ishlaydi va o'qish yuki kichik — replika foyda bermaydi, faqat murakkablik qo'shadi. Replika serverlar va ularning ulanish satrlari — infratuzilma ishi (33-qism, deploy). Ilova tomonidagi tayyorgarlik esa shu darsdagi qoidalar: yozish va tranzaksiya — primary, yozgandan keyingi o'qish — sticky yoki LSN.
Kanon tajribasi: primary yonida pg_basebackup -R bilan streaming replika, 300 ta "yozib darhol o'qish", sticky yo'naltirish, 200 000 qatorli tranzaksiya, replikaga yozish urinishi va remote_apply. Darsdagi hamma raqamlar o'sha yugurishdan. Tajriba oxirida replika konteyneri va r1 sloti o'chirildi, synchronous_* sozlamalari qaytarildi.
O'zingiz takrorlaydigan qism Docker'siz. Darsdagi sticky modelida LAG_MS ni 200 ga o'zgartiring — bu katta tranzaksiya holati. Aziz 1.5 va 2.5 soniyadan keyin o'qiganda nima bo'lishini oldindan ayting, keyin ishga tushirib tekshiring. Replikali muhitning o'zini 33-qismda Docker Compose bilan quramiz.
10. Real ishda
- Qayerda uchraydi: deyarli har katta Postgres o'rnatmasida — managed xizmatlar (AWS RDS, Google Cloud SQL, Neon, Supabase) "read replica" tugmasi bilan beradi (Managed va serverless Postgres). Hisobot va analitika uchun alohida replika — eng ko'p uchraydigan naqsh.
- Intervyu savollari: "Replikatsiya lagi nima va uni qanday o'lchaysiz?", "Read-your-writes muammosini qanday hal qilasiz?", "Fizik va mantiqiy replikatsiya farqi?", "Replika yozishni tezlashtiradimi?"
- Monitoring:
pg_stat_replicationdagireplay_lagva baytlardagi lag — eng muhim ko'rsatkichlar (Monitoring va favqulodda vaziyatlar). - Kod review'da: "bu o'qish tranzaksiya ichidami? Unda primary'dan bo'lsin", "yozgandan keyin ro'yxat replikadan o'qilyapti — foydalanuvchi o'zini ko'rmaydi".
Xulosa
- Streaming replication — primary WAL'ni replikaga oqim qilib yuboradi, replika uni qayta o'ynaydi va faqat o'qishga ochiq.
- Replika doim biroz orqada: lag vaqtda va LSN farqida (baytlarda) o'lchanadi; katta tranzaksiyada sakraydi.
- Kanon o'lchovi: yozib darhol replikadan o'qish — 300 tadan 298 tasida topilmadi; sticky yo'naltirish va
remote_applybilan — 0. - Yozish va tranzaksiya — doim primary; yozgandan keyingi o'qish — sticky yoki LSN qoidasi bilan.
- Replikatsiya sloti WAL'ni ushlab turadi — o'chgan replika primary diskini to'ldirishi mumkin.
- Mantiqiy replikatsiya — tanlangan jadvallar, versiyalar aro; versiya yangilash va CDC uchun.
Keyingi dars: Yuqori mavjudlik (HA) va falokatdan tiklash — primary yiqilganda replikani asosiy serverga aylantiramiz, yo'qolgan ma'lumot (RPO) va to'xtash vaqtini (RTO) o'lchaymiz.
Manbalar
- PostgreSQL 18 hujjati — High Availability, Load Balancing, and Replication: https://www.postgresql.org/docs/18/high-availability.html
- PostgreSQL 18 hujjati — Streaming Replication va Replication Slots: https://www.postgresql.org/docs/18/warm-standby.html
- PostgreSQL 18 hujjati — Hot Standby (so'rov ziddiyatlari,
hot_standby_feedback): https://www.postgresql.org/docs/18/hot-standby.html - PostgreSQL 18 hujjati — pg_basebackup: https://www.postgresql.org/docs/18/app-pgbasebackup.html
- PostgreSQL 18 hujjati —
pg_stat_replication, WAL funksiyalari: https://www.postgresql.org/docs/18/monitoring-stats.html - PostgreSQL 18 hujjati — Logical Replication: https://www.postgresql.org/docs/18/logical-replication.html
- Prisma hujjati — Read replicas (
@prisma/extension-read-replicas): https://www.prisma.io/docs/orm/v7/prisma-client/setup-and-configuration/read-replicas (sinalmagan) - Kanon o'lchovi (IlmHamroh, 2026-11-02): PostgreSQL 18.6 (
pgvector/pgvector:pg18), primary va replika — bitta mashinadagi ikki Docker konteyneri, pg 8.23.1, Node 24.21.0. SQL bloklari, sticky modeli va mashq yechimlari — biz ishga tushirdik (2026-11-03, PostgreSQL 17.6, Node 24.21.0).
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!