Mundarija (30)
- Bu darsda
- 1. Nega bu kerak?
- 2. Ko'rinadigan qilish: query log
- 2.1 log sozlamasi
- 2.2 Bazaning o'zi ham ko'radi
- 3. Relation'lar: alohida so'rovlar yoki JOIN
- 3.1 Sukut: query strategiyasi
- 3.2 join strategiyasi
- 4. So'rovlar soni: N+1 va batching
- 4.1 N+1 siklda
- 4.2 findUnique batching
- 4.3 Kerakli ustunlar
- 5. Ulanishlar: hovuz, serverless, pooler
- 5.1 Hovuz adapter orqali
- 5.2 Serverless
- 5.3 Accelerate va Optimize
- 6. Vositalar: Studio va seed
- 7. O'rnatish hajmi va ishga tushish
- 7.1 330 MB dan 89 MB gacha
- 7.2 Modul yuklash vaqti
- 7.3 So'rov tezligi
- 8. Ko'p uchraydigan xatolar
- 9. Mashqlar
- 1-mashq (oson): to'ldiring
- 2-mashq (o'rta): N+1 detektori
- 3-mashq (qiyin): include bilan N+1 ni tuzatish
- 10. Loyiha qadami: kanon tajribasi, loyihaga kirmaydi
- 11. Real ishda
- Xulosa
- Manbalar
Prisma prodakshnda: query log, relationLoadStrategy, N+1, hovuz va o'rnatish hajmi
Qisqacha: Prisma'ni prodakshnda ishlatishdan oldin uch narsani ko'rinadigan qiling: SQL (
loghodisasi — so'rov, parametrlar, ms), so'rovlar soni (N+1 — siklda so'rov; biz o'lchadik: 9 SQL o'rniga_countbilan 1) va ulanishlar (adapter hovuzi).relationLoadStrategy: "join"(previewrelationJoins) relation'larni bitta LATERAL JOIN bilan oladi. Bir tick'dagifindUniquelar avtomatik bittaIN (…)so'roviga birlashadi. Kanon o'lchovi: Prisma bilan prodnode_modules330 MB,--omit=optionalbilan 89 MB; 26-qismdagipgvarianti — 15 MB.
Bu darsda
- Prisma yuborgan har SQL'ni, parametrlarini va davomiyligini log'ga chiqarasiz.
relationLoadStrategyning ikki rejimini SQL darajasida solishtirasiz.- N+1 ni topasiz va tuzatasiz;
findUniquebatching'idan foydalanasiz. - Hovuzni adapter orqali sozlaysiz; serverless'dagi ulanish muammosini tushuntirasiz.
- Prisma Studio,
prisma db seed, Accelerate/Optimize nima ekanini bilasiz. - Prod o'rnatish hajmini 330 MB dan 89 MB ga tushirasiz.
Oldin bilishingiz kerak: Prisma Client extensions, N+1 muammosi, Connection pooling, PgBouncer va tashqi poolerlar, Sekin so'rovlarni topish.
1. Nega bu kerak?
Lokal kompyuterda hamma narsa tez: 48 ta vazifa, bitta foydalanuvchi. Prodakshnda esa 500 000 vazifa, yuzlab parallel so'rov va bir nechta server nusxasi. ORM qulayligi shu yerda ko'rinmas narxga aylanadi: bir qator kod o'nlab SQL yuboradi, har nusxa o'z hovuzini ochadi, Docker image yuzlab megabayt o'sadi.
N+1 muammosi darsida bu tuzoqni pg bilan ko'rgan edik. Prisma'da u yashirinroq: include va sikl ichidagi await bir xil ko'rinadi, lekin SQL soni butunlay boshqa.
O'xshatish: mashina sotib olganda faqat tezligiga emas, yoqilg'i sarfiga ham qaraysiz. Prisma — qulay mashina; bu dars uning "yoqilg'i hisoblagichi"ni o'rnatadi.
Chiqishlarni biz ishga tushirdik: PostgreSQL 17.6, holat 20 namunasi, Prisma 7.10.1 (relationJoins yoqilgan tajriba nusxasi), Node 24.21.0. Hajm va tezlik o'lchovlari — kanondan (PostgreSQL 18.6, katta bazasi, 500 000 vazifa).
2. Ko'rinadigan qilish: query log
2.1 log sozlamasi
const db = new PrismaClient({
adapter,
log: [
{ emit: "event", level: "query" },
{ emit: "stdout", level: "warn" },
{ emit: "stdout", level: "error" },
],
});
db.$on("query", (e) => {
logger.debug({ sql: e.query, params: e.params, ms: e.duration });
});Daraja to'rtta: query, info, warn, error. emit: "stdout" — Prisma o'zi konsolga yozadi; emit: "event" — siz $on bilan ushlab, o'z logger'ingizga (25-qismdagi Pino) yuborasiz. $on("query") ning turi faqat log da query hodisasi e'lon qilinganda to'g'ri chiqadi.
Bitta findFirst hodisasi:
sql, params, ms | duration turi: number
SQL: SELECT "t0"."id" FROM "vazifalar" AS "t0" WHERE ("t0"."egasi_id" = $1 AND "t0"."bajarildi" = $2) LIMIT $3
params: ["3",false,"1"]params — JSON satri, ichida haqiqiy qiymatlar. Prodakshnda bu ma'lumot oqishi: parol hash'i, email, token log'ga tushadi. Qoida — query darajasi dev'da yoqiladi; prodda faqat sekin so'rovlar (e.duration > 200) va parametrlarsiz.
Diqqat: SQL'dagi
"t0"taxalluslariga qarang — oldingi darslarda"vazifalar"."id"edi. Bu nusxadarelationJoinspreview yoqilgani uchun Prisma oddiy so'rovlarni ham boshqacha yasadi. Preview imkoniyat faqat bitta joyga emas, hamma so'rovga ta'sir qilishi mumkin — yoqishdan oldin to'liq test.
2.2 Bazaning o'zi ham ko'radi
Prisma log'i "ilova nima yubordi"ni ko'rsatadi. "Baza qancha vaqt sarfladi" — pg_stat_statements (Sekin so'rovlarni topish). Prisma SQL'ni parametrli yuborgani uchun u yerda bir xil so'rovlar bitta qatorga yig'iladi — "qaysi so'rov eng ko'p vaqt yeydi" darhol ko'rinadi. Hovuzni o'zimiz yaratganimiz uchun application_name: "vazifalar-api" ham beriladi (Prisma Client CRUD, «Loyiha qadami») — pg_stat_activity da API ulanishlari ajralib turadi.
3. Relation'lar: alohida so'rovlar yoki JOIN
3.1 Sukut: query strategiyasi
Prisma: relations darsida ko'rgandik: include har relation uchun alohida SQL yuboradi. Prisma hujjatida bu relationLoadStrategy: "query". Kanon Prisma 7.10 da "join" ni sinab ko'rganda:
" Unknown argument `relationLoadStrategy`. Available options are marked with ?."Ya'ni bu parametr faqat previewFeatures = ["relationJoins"] yoqilganda bor.
3.2 join strategiyasi
Biz tajriba nusxasida relationJoins ni yoqdik va bir xil so'rovni ikki rejimda yubordik — Sardor va uning 2 ta vazifasi:
const tanlov = {
where: { id: 3n },
select: {
ism: true,
vazifalar: {
take: 2,
orderBy: { id: "asc" as const },
select: { matn: true },
},
},
};
await db.foydalanuvchi.findUnique({
...tanlov, relationLoadStrategy: "query",
});
await db.foydalanuvchi.findUnique({
...tanlov, relationLoadStrategy: "join",
});Chiqishda ikki narsaga qarang: har rejim nechta SQL yubordi va join qatorida LEFT JOIN LATERAL qayerda turibdi (uzun qatorlar telefonda yonga suriladi).
query: 2 ta SQL
SQL: SELECT "foydalanuvchilar"."id", "foydalanuvchilar"."ism" FROM "foydalanuvchilar" WHERE (…) LIMIT $2 OFFSET $3
SQL: SELECT "vazifalar"."id", "vazifalar"."matn", "vazifalar"."egasi_id" FROM "vazifalar" WHERE "vazifalar"."egasi_id" = $1 ORDER BY "vazifalar"."id" ASC LIMIT $2 OFFSET $3
join: 1 ta SQL
SQL: SELECT "t0"."id", "t0"."ism", "Foydalanuvchi_vazifalar"."__prisma_data__" AS "vazifalar" FROM "foydalanuvchilar" AS "t0" LEFT JOIN LATERAL (SELECT COALESCE(JSONB_AGG("__prisma_data__"), '[]') AS "__prisma_data__" FROM (… JSONB_BUILD_OBJECT('matn', "t2"."matn") … ORDER BY "t3"."id" ASC LIMIT $1 …) AS "t4") AS "Foydalanuvchi_vazifalar" ON true WHERE ("t0"."id" = $2 AND 1=1) LIMIT $3
{"ism":"Sardor Tursunov","vazifalar":[{"matn":"Mashinani texnik ko'rikka olib borish #mashina"},{"matn":"Moy almashtirish #mashina"}]}Nima bo'ldi? query rejimi ikki so'rov yubordi: avval foydalanuvchi, keyin uning vazifalari — ularni Prisma JavaScript'da birlashtirdi. join — bitta so'rov: LEFT JOIN LATERAL (Subquery va LATERAL) va natijani Postgres'ning o'zida JSON'ga yig'ish (JSONB_AGG). Oxirgi qatordagi natija ikkala rejimda bir xil.
flowchart LR
subgraph Q["query (sukut)"]
Q1["SELECT foydalanuvchi"] --> Q2["SELECT vazifalar IN (…)"]
Q2 --> Q3["JS'da birlashtirish"]
end
subgraph J["join (relationJoins)"]
J1["SELECT … LEFT JOIN LATERAL<br/>JSONB_AGG"] --> J2["tayyor JSON"]
endDiagrammaga qarang: chapda ikki tarmoq aylanishi (round trip) va JavaScript'da yig'ish, o'ngda bitta aylanish va Postgres'da yig'ish.
Qaysi biri tez? Bu holatga bog'liq: baza uzoqda bo'lsa (har aylanish 20–50 ms, masalan boshqa regiondagi managed baza), join sezilarli yutadi. Baza yonida bo'lsa va relation katta bo'lsa, JSON yig'ish narxi farqni yeyishi mumkin. Biz bu ikki rejim tezligini o'lchamadik — preview imkoniyatni loyihaga kiritmadik. Qaror — EXPLAIN va o'z o'lchovingiz bilan.
4. So'rovlar soni: N+1 va batching
4.1 N+1 siklda
Har foydalanuvchining vazifalar sonini sanash — ikki usul:
// ❌ siklda so'rov
const egalar = await db.foydalanuvchi.findMany({
select: { id: true },
});
for (const e of egalar) {
await db.vazifa.count({ where: { egasiId: e.id } });
}
// ✅ bitta so'rov
await db.foydalanuvchi.findMany({
select: { id: true, _count: { select: { vazifalar: true } } },
});sikl (count har biriga): 9 ta SQL
_count bilan: 1 ta SQL8 foydalanuvchi — 1 + 8 = 9 SQL. 1 000 foydalanuvchida — 1 001. Har biri 1 ms bo'lsa ham, tarmoq aylanishlari bilan sekundga yetadi. Bunday kodni log'dan topish oson: bir xil SQL shablon ketma-ket ko'p marta (2-mashq).
4.2 findUnique batching
Tick — event loop ning bitta aylanishi: sinxron kod va shu paytgacha yig'ilgan microtask'lar bajariladigan oraliq. Prisma'ning ichida kichik "dataloader" (so'rovlarni yig'ib, bittaga birlashtiruvchi) bor: bir tick'da kelgan bir xil shakldagi findUnique lar bitta so'rovga birlashadi.
await Promise.all([1n, 2n, 3n, 4n, 5n, 6n, 7n, 8n].map((id) =>
db.foydalanuvchi.findUnique({
where: { id }, select: { ism: true },
})));
await Promise.all([1n, 2n, 3n, 4n].map((id) =>
db.foydalanuvchi.findFirst({
where: { id }, select: { ism: true },
})));Promise.all findUnique x8: 1 ta SQL
SQL: SELECT "t0"."id", "t0"."ism" FROM "foydalanuvchilar" AS "t0" WHERE "t0"."id" IN ($1,$2,$3,$4,$5,$6,$7,$8)
params: ["1","2","3","4","5","6","7","8"]
Promise.all findFirst x4: 4 ta SQL8 ta findUnique — bitta IN (…). findFirst — batching'siz, 4 ta SQL. Qadamma-qadam:
| Qadam | Kod | Prisma | SQL |
|---|---|---|---|
| 1 | Promise.all 8 ta findUnique ni boshlaydi |
so'rovlarni navbatga yig'adi | hali yo'q |
| 2 | tick tugadi | 8 ta id ni bitta IN ga birlashtiradi |
1 ta |
| 3 | natija keldi | har qatorni o'z chaqiruviga qaytaradi | — |
| 4 | for … await siklida |
har chaqiruv alohida tick'da | har biriga 1 ta |
Bu GraphQL'da (har maydon alohida resolver) N+1'ni avtomatik yumshatadi. Lekin faqat Promise.all kabi parallel chaqiruvda: for … await siklida har so'rov alohida tick'da — batching yo'q. |
Tekshirib ko'ring: Nega 8 ta
findUniquebitta so'rov bo'ldi-yu, 4 tafindFirstbo'lmadi?
Javob
findUnique noyob kalit bo'yicha — har natija aniq bitta yozuvga mos keladi, Prisma IN natijasini qaysi chaqiruvga qaytarishni biladi. findFirst istalgan shart bilan, orderBy va boshqa argumentlar bilan bo'lishi mumkin — ularni xavfsiz birlashtirib bo'lmaydi.
4.3 Kerakli ustunlar
Azizning 13 vazifasi — hamma ustun va faqat id, matn:
hamma ustun: 4216 bayt JSON | select: 717 bayt JSON6 baravar. API javobi, tarmoq va xotira — hammasi shunga mutanosib. select (yoki global omit) — prodakshn odati (Prisma Client CRUD).
5. Ulanishlar: hovuz, serverless, pooler
5.1 Hovuz adapter orqali
Prisma 7 da ulanishlar — pg.Pool ning ishi. new PrismaPg({ connectionString, max: 10 }) yoki tayyor pg.Pool (Prisma Client CRUD, «Loyiha qadami»). pg sukutlari (node-postgres hujjati, 2026-10-25): max 10, connectionTimeoutMillis 0 — ya'ni ulanish bo'shashini cheksiz kutadi. Shuning uchun kanon 5 000 ms beradi: hovuz to'lsa, so'rov osilib qolmasdan xato beradi (Connection pooling).
Hovuz hajmining ta'siri — 10 ta parallel pg_sleep(0.1):
10 x pg_sleep(0.1), max 2: 600 ms atrofida
10 x pg_sleep(0.1), max 10: 400 ms atrofidamax: 2 — so'rovlar navbatda: 5 ta "to'lqin" × 100 ms + ulanish ochish. max: 10 — hammasi bir vaqtda, lekin 10 ta yangi ulanishni ochish vaqti qo'shildi (birinchi ishga tushirishda). Bitta o'lchov, yuklangan mashina — faqat tendensiyani ko'rsatadi.
5.2 Serverless
Serverless (AWS Lambda, Vercel Functions) — har so'rov alohida "nusxa"da ishlashi mumkin, nusxalar soni yuzlab. Har nusxada hovuz max: 10 bo'lsa — 100 nusxa × 10 = 1 000 ulanish, Postgres esa sukut 100 tani ko'taradi (Postgres arxitekturasi). Yechimlar (Prisma hujjati, sinalmagan): nusxa boshiga max: 1, oldida tashqi pooler — PgBouncer transaction rejimi (PgBouncer va tashqi poolerlar) yoki provayder pooleri (Neon, Supabase), yoki HTTP orqali ulanadigan adapterlar (@prisma/adapter-neon kabi). Transaction pooler bilan sessiya darajasidagi narsalar (SET va prepared statement) ishlamasligi mumkin — 26-qismda ko'rgan cheklovlar. Managed va serverless Postgres — alohida dars.
5.3 Accelerate va Optimize
Prisma kompaniyasining bulut xizmatlari (prisma.io, 2026-10): Accelerate — global ulanish hovuzi va so'rov keshi. Optimize — so'rovlarni tahlil qilib tavsiya beradigan vosita edi; Prisma blogiga ko'ra u 2026-yil mart oyida yopildi (sunset) va o'rnini Prisma Postgres ichidagi Query Insights egalladi. Eski loyihada @prisma/extension-optimize va withOptimize() ni ko'rsangiz — bu o'sha yopilgan xizmat, uni olib tashlash mumkin. Bu xizmatlar hisob ochishni talab qiladi — kanon ham, biz ham sinamadik. Ular ixtiyoriy: shu darsdagi log, pg_stat_statements va o'z hovuzingiz bilan ko'p muammolar bepul hal bo'ladi.
6. Vositalar: Studio va seed
- Prisma Studio —
npx prisma studiobrauzerda jadvallarni ko'rish va tahrirlash interfeysini ochadi (lokal). Dev'da qulay; prod bazaga ulamang — u to'g'ridan yozadi, auditsiz. Biz uni bu darsda ochmadik (GUI); hujjat bo'yicha. prisma db seed—prisma.config.tsdagimigrations.seedbuyrug'ini bajaradi. Deterministik, idempotent seed va production'da ishlamaslik himoyasi — Seeding va test ma'lumotlari (holat 39).
7. O'rnatish hajmi va ishga tushish
7.1 330 MB dan 89 MB gacha
Kanon prod o'rnatishni (npm ci --omit=dev) o'lchadi. Chiqishda node_modules hajmiga va "prisma CLI: bor" kabi qatorlarga qarang — prodda ular bo'lmasligi kerak edi:
=== 26 kanoni (pg, Prisma'siz)
added 97 packages in 5s
node_modules: 15M; @prisma: ; prisma CLI: yo'q; typescript: yo'q; react-dom: yo'q
=== 27 holat 20 (Prisma 7)
added 238 packages in 27s
node_modules: 330M; @prisma: 146M; prisma CLI: bor; typescript: bor; react-dom: bor
@prisma/client peerDependencies: {"prisma":"*","typescript":">=5.4.0"}
node_modules/prisma {"devOptional":true}
node_modules/typescript {"devOptional":true}
node_modules/react-dom {"peer":true,"devOptional":true}--omit=dev bilan ham prisma CLI, TypeScript va hatto react-dom o'rnatildi! Sababi: @prisma/client ularni ixtiyoriy peer sifatida e'lon qilgan, package-lock.json da devOptional: true — npm ularni "dev yoki optional" deb biladi va --omit=dev bilan chiqarmaydi. --omit=peer ham yordam bermadi (322 MB). Yechim:
$ npm ci --omit=dev --omit=optional (/tmp/kanon27/prod-sinov)
added 105 packages in 5s
node_modules: 89M; @prisma: 72M; prisma CLI: yo'q; typescript: yo'q; react-dom: yo'q89 MB — shundan 72 MB @prisma/client ning o'zi (hamma provayderlar uchun runtime). Docker image o'lchami 33-qismda yana ko'riladi; hozir qoida: prod o'rnatish — npm ci --omit=dev --omit=optional, va undan keyin ilova haqiqatan ishga tushishini tekshiring (ba'zi paketlar ixtiyoriy bog'liqlikka tayanadi).
7.2 Modul yuklash vaqti
Kanon src/ishga-tushir.ts ni import qilish vaqtini o'lchadi (prod node_modules, Windows):
import #1: 598 ms
import #2: 620 ms
import #3: 576 ms
faqat pg import #1: 183 ms
faqat pg import #2: 178 msPrisma bilan ~0.6 s, pg bilan ~0.18 s. Doimiy server uchun bu ahamiyatsiz — bir marta. Serverless'da esa har "sovuq start" ga qo'shiladi. (Kanon server ishga tushishi va o'chishini ham o'lchamoqchi bo'lgan, lekin Windows'da jarayon osilib qolgan — o'lchov ishonchsiz deb olib tashlangan.)
7.3 So'rov tezligi
Bir xil VazifaOmbori — PgOmbori va PrismaOmbori (kanon, katta, egasi 77, mediana 50):
- ro'yxat 50 — pg3,65 ms
- ro'yxat 50 — prisma4,43 ms
- teg filtri — pg7,33 ms
- teg filtri — prisma8,36 ms
- matn saralash — pg5,42 ms
- matn saralash — prisma12,07 ms
Manba: Kanon o'lchovi: PostgreSQL 18.6, 500 000 vazifa, egasi 77, mediana 50 (2026-10-25)
Oddiy so'rovlarda farq 1 ms atrofida. Eng katta farq — matn bo'yicha saralash (12.07 ms): u yerda Prisma ikki so'rov qiladi (id'lar + raw SQL, raw SQL darsi). Yozuvlarda: 200 ta yangila — pg 2 842 ms, prisma 4 051 ms (har yozuvga uch so'rov). Xulosa: Prisma tezlikni o'zi "yemaydi" — qo'shimcha so'rovlar yeydi. Ularni log'da ko'rib, kerak joyda raw SQL ga o'tish — prodakshn ishi.
8. Ko'p uchraydigan xatolar
| Belgi | Sabab | Davosi |
|---|---|---|
| sahifa sekin, log'da bir xil SQL 100 marta | N+1 siklda | include/_count, IN, batching |
too many clients serverless'da |
har nusxa o'z hovuzi | max: 1 + pooler |
| so'rov osilib qoldi | connectionTimeoutMillis 0 |
aniq timeout |
| log'da email/token | query + params prodda |
prodda faqat sekin so'rovlar, parametrsiz |
| image 330 MB | ixtiyoriy peer'lar (devOptional) |
--omit=dev --omit=optional |
Unknown argument relationLoadStrategy |
preview yoqilmagan | previewFeatures = ["relationJoins"] |
Failed to deserialize column of type 'void' |
$queryRaw da pg_sleep |
SELECT 1 FROM pg_sleep(…) |
9. Mashqlar
Mashqlar kurs/mashqlar/27/28-prisma-prod/ da. 2-mashq yechimi Node 24 da tekshirilgan (bazasiz).
1-mashq (oson): to'ldiring
- Parametrlarni ham ko'rsatadigan log hodisasi —
$on("") - Bitta tick'dagi qaysi metod chaqiruvlari bitta
INga birlashadi — - Relation'larni bitta LATERAL JOIN bilan olish —
relationLoadStrategy: "" - Prisma'li prod o'rnatishni 89 MB ga tushiradigan bayroq —
--omit=
Yechim
- query —
e.query,e.params,e.duration. - findUnique —
findFirstbirlashmaydi. - join —
relationJoinspreview bilan. - optional —
--omit=devbilan birga.
2-mashq (o'rta): N+1 detektori
Query log'dan yig'ilgan SQL satrlari massivini oladigan n1Top(sorovlar, chegara) funksiyasini yozing. U bir xil shablondagi so'rovlarni sanasin (shablon — $1, $2 kabi parametr raqamlari va raqamli literallar ? bilan almashtirilgan matn) va chegara dan ko'p takrorlanganlarini { shablon, soni } ko'rinishida, kamayish tartibida qaytarsin.
Yechim
function shablon(sql) {
return sql.replace(/\$\d+/g, "?").replace(/\b\d+\b/g, "?");
}
function n1Top(sorovlar, chegara) {
const hisob = new Map();
for (const s of sorovlar) {
const k = shablon(s);
hisob.set(k, (hisob.get(k) ?? 0) + 1);
}
return [...hisob]
.filter(([, n]) => n > chegara)
.map(([k, n]) => ({ shablon: k, soni: n }))
.sort((a, b) => b.soni - a.soni);
}
const log = [
'SELECT "id" FROM "foydalanuvchilar" OFFSET $1',
...Array.from({ length: 8 }, () =>
'SELECT COUNT(*) FROM "vazifalar" ' +
'WHERE "egasi_id" = $1 OFFSET $2'),
'SELECT 1',
];
console.log(n1Top(log, 3));Konsolda:
[
{
shablon: 'SELECT COUNT(*) FROM "vazifalar" WHERE "egasi_id" = ? OFFSET ?',
soni: 8
}
]Bu — «N+1 siklda» bo'limidagi 9 ta SQL'ning modeli: 8 marta takrorlangan COUNT shabloni ushlandi. Haqiqiy ilovada n1Top ni har HTTP so'rov oxirida (log'ni AsyncLocalStorage bilan har so'rov uchun yig'ib) dev rejimida chaqirib, ogohlantirish chiqarish mumkin.
3-mashq (qiyin): include bilan N+1 ni tuzatish
Quyidagi kod har vazifa uchun egasining ismini alohida so'raydi. Uni bitta findMany + select bilan qayta yozing va log'da SQL sonini solishtiring (o'z bazangizda).
const vazifalar = await db.vazifa.findMany({
where: { egasiId: 1n }, select: { id: true, egasiId: true },
});
for (const v of vazifalar) {
const e = await db.foydalanuvchi.findFirst({
where: { id: v.egasiId }, select: { ism: true },
});
console.log(v.id, e?.ism);
}Yechim
const vazifalar = await db.vazifa.findMany({
where: { egasiId: 1n },
select: { id: true, egasi: { select: { ism: true } } },
});
for (const v of vazifalar) console.log(v.id, v.egasi.ism);Biz ishga tushirdik: asl kod — 14 SQL (1 + 13, findFirst batching'siz). Yangisi — 2 SQL (query strategiyasida — vazifalar va IN (…) bilan egalar) yoki relationJoins bilan 1. findFirst o'rniga findUnique qo'yib ham sinadik: for … await siklida baribir 13 ta alohida SQL (har chaqiruv alohida tick'da), Promise.all bilan esa 1 ta. Eng to'g'risi — relation'ni so'rovning o'zida olish.
10. Loyiha qadami: kanon tajribasi, loyihaga kirmaydi
Bu darsda vazifalar-api kodi o'zgarmaydi. Kanon o'lchovlari va qarorlari:
- Hovuz — holat 20 da allaqachon prodakshn shaklida: o'z
pg.Poolimiz (application_name,idleTimeoutMillis30 s,connectionTimeoutMillis5 s,statement_timeout,error→ Pino)PrismaPgga beriladi. - Query log API'ga qo'shilmadi: API'da allaqachon Pino so'rov log'i (
pino-http, 25-qism) bor; SQL darajasida —pg_stat_statements(Sekin so'rovlarni topish). Dev'da SQL'ni ko'rish uchun shu darsdagi$on("query")ni vaqtincha yoqish yetarli. relationJoins— preview; u oddiy so'rovlarning SQL'ini ham o'zgartirdi (taxalluslar). Ombor relation'larni deyarli ishlatmaydi (ro'yxat bitta jadvaldan) — foyda kam, xavf bor. Kiritilmadi.- O'rnatish — prod uchun
npm ci --omit=dev --omit=optional(89 MB). Docker image va deploy — 33-qismda. - Accelerate — hisob kerak, sinalmagan; Optimize 2026-03 da yopilgan.
11. Real ishda
- Prodakshndagi Prisma muammolarining katta qismi — N+1 va keraksiz ustunlar; ular kod review'da emas, log va APM (Sentry, Datadog) da topiladi.
- Serverless'ga ko'chishda birinchi savol: "ulanishlar soni qancha bo'ladi?" — hovuz
max, pooler, adapter. - Intervyuda: "Prisma ilovasi sekin — nimadan boshlaysiz?", "Serverless'da Postgres ulanishlari muammosi?", "
includeqanday SQL beradi?"
Xulosa
log: [{ emit: "event", level: "query" }]+$on— SQL, parametrlar, ms; prodda parametrlarni log'lamang.includesukut — alohida so'rovlar;relationJoinspreview bilanjoin— bitta LATERAL + JSONB_AGG.- N+1: siklda 9 SQL,
_countbilan 1; bir tick'dagifindUniquelar bittaINga birlashadi,findFirst— yo'q. - Hovuz — adapter orqali, aniq timeout bilan; serverless'da
max: 1+ pooler. - Prod o'rnatish:
--omit=dev --omit=optional— 330 → 89 MB; Prisma importi ~0.6 s.
Keyingi dars: Drizzle ORM 1: sxema va so'rovlar — sxema TypeScript'da, so'rovlar SQL'ga yaqin, natija turi avtomatik.
Manbalar
- Prisma hujjati — Logging: https://www.prisma.io/docs/orm/v7/prisma-client/observability-and-logging/logging (2026-10-25)
- Prisma hujjati — Relation load strategies (
relationJoins): https://www.prisma.io/docs/orm/v7/prisma-client/queries/relation-queries - Prisma hujjati — Query optimization, N+1 va dataloader: https://www.prisma.io/docs/orm/v7/prisma-client/queries/advanced/query-optimization-performance
- Prisma hujjati — Serverless va connection pool: https://www.prisma.io/docs/orm/v7/prisma-client/setup-and-configuration/databases-connections
- node-postgres — Pool sozlamalari: https://node-postgres.com/apis/pool
- Prisma blogi — Sunsetting Prisma Optimize (2026-03-23): https://www.prisma.io/blog/sunsetting-prisma-optimize
- Chiqishlar — biz ishga tushirdik: PostgreSQL 17.6, Prisma 7.10.1, Node 24.21.0 (2026-10-25); hajm, import vaqti va ombor tezligi — kanon (PostgreSQL 18.6, npm 11.19.0).
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!