Mundarija (31)
- Bu darsda
- 1. Nega bu kerak?
- 2. Tabiiy va surrogat kalit
- 2.1 Ikki yondashuv
- 2.2 Tabiiy kalit qayerda sinadi
- 3. serial va IDENTITY
- 3.1 serial — eski uslub
- 3.2 IDENTITY — SQL standarti
- 3.3 ID'dagi "teshiklar" — normal
- 4. UUID: v4 va v7
- 4.1 UUID nima uchun
- 4.2 v4 — tasodifiy
- 4.3 v7 — vaqt bo'yicha tartiblangan (PostgreSQL 18)
- 5. O'lchov: bigint, v4, v7
- 6. ULID va nanoid
- 7. Hujumchi nigohi: ketma-ket ID oshkor qiladi
- 8. SQL qadami: kanon qarori — bigint IDENTITY
- 9. Ko'p uchraydigan xatolar
- 9.1 serial va qo'lda ID
- 9.2 int ID
- 9.3 Teshiklarni "tuzatish"
- 9.4 UUID'ni text da saqlash
- 9.5 "UUID — xavfsizlik"
- 10. Mashqlar
- 1-mashq (oson): to'ldiring
- 2-mashq (o'rta): ID'larni oldindan ayting
- 3-mashq (qiyin): ichki va tashqi ID
- 4-mashq: SQL qadami
- 11. Real ishda
- Xulosa
- Manbalar
Primary key tanlovi: serial, IDENTITY, UUID v4 va uuidv7
Qisqacha: Birlamchi kalit (primary key) uchun odatda ma'nosiz surrogat kalit olinadi, email yoki telefon kabi tabiiy kalit emas. PostgreSQL'da standart tanlov —
bigint GENERATED ALWAYS AS IDENTITY: tez, ixcham (8 bayt), qo'lda yozib buzib bo'lmaydi. Eskiserialo'rniga aynan shu ishlatiladi. UUID kerak bo'lsa — tasodifiy v4 emas, vaqt bo'yicha tartiblangan v7 (uuidv7(), PostgreSQL 18). Kanon o'lchovida 1 mln qator yozish v4 da 6.7 s, v7 da 3.7 s,bigintda 2.6 s.
Bu darsda
- Tabiiy va surrogat kalit farqini va nega surrogat ko'proq tanlanishini tushuntirasiz.
serialvaGENERATED ALWAYS AS IDENTITYfarqini xato misolida ko'rsatasiz.- ID'lardagi "teshiklar" nega normal ekanini bilasiz.
- UUID v4, v7, ULID va nanoid'ni solishtirasiz va o'lchov natijalarini o'qiysiz.
- Ketma-ket ID'ni tashqariga ko'rsatish xavfini va undan himoyani aytasiz.
Oldin bilishingiz kerak: CREATE TABLE va cheklovlar, PostgreSQL ma'lumot turlari.
1. Nega bu kerak?
CREATE TABLE va cheklovlar darsida id bigint GENERATED ALWAYS AS IDENTITY yozdik va "nega aynan shunday — keyingi darsda" dedik. Endi shu savol.
ID — jadvaldagi eng "abadiy" ustun. U boshqa jadvallarda tashqi kalit sifatida takrorlanadi, URL'larda (/vazifalar/42), loglarda, mobil ilova keshida, boshqa xizmatlarda yashaydi. Ma'lumot ko'payganda uni almashtirish — butun tizimni qayta yozish degani. Shuning uchun ID turini boshida o'ylab tanlash kerak.
Uchta savol tanlovni belgilaydi:
- Nima kalit bo'ladi — ma'noli qiymat (email) yoki ma'nosiz raqam?
- Kim yaratadi — baza yoki ilova?
- Tashqariga ko'rinadimi — URL'da, API javobida?
Hayotiy o'xshatish: kutubxona. Kitobni nomi bo'yicha qidirish qulay, lekin nom takrorlanadi va qayta nashrda o'zgaradi. Shuning uchun har kitobga inventar raqami yopishtiriladi — kitob haqida hech narsa demaydi, lekin hech qachon o'zgarmaydi va takrorlanmaydi.
2. Tabiiy va surrogat kalit
2.1 Ikki yondashuv
Tabiiy kalit (natural key) — narsaning o'zida bor, noyob bo'lishi kerak bo'lgan xususiyat: email, telefon, pasport raqami, mahsulot shtrix-kodi. Surrogat kalit (surrogate key) — baza bergan, ma'nosiz raqam yoki identifikator: 1, 2, 3 yoki UUID.
| Tabiiy (email) | Surrogat (id) |
|
|---|---|---|
| Ma'nosi | bor | yo'q |
| O'zgaradimi | ha (email almashadi) | hech qachon |
| Hajmi | o'zgaruvchan matn | 8 yoki 16 bayt |
| FK'lar soni | har jadvalda email takrorlanadi | faqat raqam |
2.2 Tabiiy kalit qayerda sinadi
foydalanuvchilar ning PK si email bo'lsin deylik. Unda vazifalar.egasi_email ham email bo'ladi. Aziz emailini almashtirsa, uning hamma vazifalari, izohlari, tarix yozuvlari ham yangilanishi kerak. Kanon izohida aynan shu qayd etilgan: tabiiy kalit o'zgarsa — hamma FK yangilanadi.
Boshqa misollar:
- Telefon raqami — odam raqamini almashtiradi, eski raqam boshqaga beriladi.
- Pasport raqami — pasport almashtiriladi; chet el fuqarosida boshqa formatda; shaxsiy ma'lumot, URL'da ko'rinmasligi kerak.
- "Noyob" deb o'ylangan narsa — ism + tug'ilgan sana ham takrorlanadi.
Qoida: tabiiy noyoblikni UNIQUE bilan saqlang (email noyob bo'lsin), lekin PK — surrogat. Kanonda aynan shunday: id — PK, email — foydalanuvchilar_email_key noyob indeksi.
Tabiiy kalit o'rinli joylar ham bor: hech qachon o'zgarmaydigan kod va ma'lumotnomalar (valyuta kodi USD, mamlakat kodi UZ) yoki bog'lanish jadvalidagi juftlik (vazifa + teg). Ikkinchisini UPSERT va MERGE darsida ko'ramiz.
3. serial va IDENTITY
3.1 serial — eski uslub
Ko'p eski qo'llanmalarda id serial PRIMARY KEY uchraydi. serial — haqiqiy tur emas, qisqartma: Postgres int ustun, alohida ketma-ketlik (sequence) obyekti va DEFAULT nextval(…) yaratadi. Sequence — har chaqirilganda keyingi raqamni beradigan hisoblagich.
Muammo: serial qo'lda qiymat yozishni taqiqlamaydi. Kanon tajribasi:
CREATE TABLE t13_serial (id serial PRIMARY KEY, nom text);
INSERT INTO t13_serial (id, nom) VALUES (1, 'qo''lda');
INSERT INTO t13_serial (nom) VALUES ('avtomatik');Natija:
CREATE TABLE
INSERT 0 1
ERROR: duplicate key value violates unique constraint "t13_serial_pkey"
DETAIL: Key (id)=(1) already exists.Birinchi qator id = 1 ni qo'lda oldi. Sequence esa buni bilmaydi va ikkinchi qatorga yana 1 ni berdi — to'qnashuv. Haqiqiy loyihada bu ko'pincha ma'lumot ko'chirilgandan keyin bo'ladi: eski qatorlar o'z ID'lari bilan import qilindi, sequence esa 1 da qoldi. Keyingi kun yangi foydalanuvchi ro'yxatdan o'ta olmaydi.
3.2 IDENTITY — SQL standarti
PostgreSQL 10 dan beri standart usul bor:
CREATE TABLE t13_identity (
id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
nom text
);
INSERT INTO t13_identity (id, nom) VALUES (1, 'qo''lda');Natija:
CREATE TABLE
ERROR: cannot insert a non-DEFAULT value into column "id"
DETAIL: Column "id" is an identity column defined as GENERATED ALWAYS.
HINT: Use OVERRIDING SYSTEM VALUE to override.Qo'lda yozish rad etildi — sequence bilan to'qnashuv bo'lmaydi. Ikki varianti bor:
| Variant | Qo'lda qiymat |
|---|---|
GENERATED ALWAYS AS IDENTITY |
taqiqlangan (faqat OVERRIDING SYSTEM VALUE bilan) |
GENERATED BY DEFAULT AS IDENTITY |
ruxsat — serial kabi xavf |
Kanon ALWAYS ni tanlagan. Boshqa afzalliklari: sequence ustunga "tegishli" (jadval o'chsa, u ham o'chadi), sintaksis boshqa bazalarda ham bir xil (standart). Yangi kodda serial ishlatmang — eski loyihalarda uchratganingizda nima ekanini bilish yetadi.
3.3 ID'dagi "teshiklar" — normal
Kanon ikkita qator qo'shdi, keyin uchinchisini tranzaksiyada qo'shib, bekor qildi, so'ng yana bittasini qo'shdi:
INSERT INTO t13_identity (nom) VALUES ('a'), ('b') RETURNING id;
BEGIN; INSERT INTO t13_identity (nom) VALUES ('bekor'); ROLLBACK;
INSERT INTO t13_identity (nom) VALUES ('c') RETURNING id;Natija:
id
----
1
2
(2 rows)
INSERT 0 2
BEGIN
INSERT 0 1
ROLLBACK
id
----
4
(1 row)
INSERT 0 13 yo'q. Bekor qilingan tranzaksiya raqamni "yeb" qo'ydi va u qaytmaydi. Nega sequence ROLLBACK ga bo'ysunmaydi? Agar bo'ysunsa, bir vaqtda ishlayotgan ikki tranzaksiya bir-birini kutishga majbur bo'lardi: "u 3 ni oldi, bekor qiladimi, yo'qmi?". Bu minglab foydalanuvchida bazani sekinlashtirardi. Postgres tezlikni tanlagan — raqamlar noyob va o'sib boradi, lekin uzluksiz emas.
Xulosa: ID'dan "hisob" sifatida foydalanmang (id = 1000 → "1000-mijoz" emas) va teshiklarni "tuzatishga" urinmang. Chek raqami kabi uzluksiz raqam kerak bo'lsa — bu alohida talab, uni boshqa yo'l bilan qilinadi.
Tekshirib ko'ring:
CHECKcheklovi tufayli rad etilganINSERTham ID'ni "yeydimi"?
Javob
Ha. DEFAULT qiymat (sequence'dan keyingi raqam) qator tekshirilishidan oldin hisoblanadi. Qator keyin CHECK dan o'tmasa, raqam allaqachon olingan. CREATE TABLE va cheklovlar darsidagi 2-mashqda ikkinchi vazifaning id si shu sabab 3 bo'lgan edi. 2-mashqda buni o'zingiz hisoblaysiz.
4. UUID: v4 va v7
4.1 UUID nima uchun
UUID (Universally Unique Identifier) — 128 bitli identifikator, 8-4-4-4-12 ko'rinishidagi 36 belgi. Uning kuchi: uni hech kim bilan kelishmasdan yaratish mumkin, to'qnashuv ehtimoli amalda nolga teng. Bu quyidagi holatlarda kerak:
- Ilova ID'ni bazaga yozishdan oldin bilishi kerak (oflayn mobil ilova, bir nechta bog'liq yozuvni birdan yaratish).
- Bir nechta baza yoki xizmat ID yaratadi va keyin ma'lumot birlashtiriladi.
- ID tashqariga (URL, API) chiqadi va undan hech narsa taxmin qilinmasligi kerak.
4.2 v4 — tasodifiy
gen_random_uuid() (PostgreSQL 13 dan beri o'rnatilgan) — v4, deyarli hammasi tasodifiy bitlar:
SELECT gen_random_uuid() AS v4 FROM generate_series(1, 3);Kanon chiqishi (har safar boshqa):
v4
--------------------------------------
dfe8ca9d-080a-4918-93ee-463adfa4113f
1d34459f-4a84-432f-bb68-0f22e54fdaf3
106a9b3f-f005-46db-b6ec-43cff9f18e87
(3 rows)Uchta ketma-ket UUID orasida hech qanday tartib yo'q. Bu xavfsizlik uchun yaxshi, indeks uchun esa yomon — pastda o'lchovni ko'ramiz.
4.3 v7 — vaqt bo'yicha tartiblangan (PostgreSQL 18)
UUID v7 2024-yilda RFC 9562 standartida paydo bo'ldi. Uning birinchi 48 biti — millisekundlardagi vaqt, qolgani tasodifiy. PostgreSQL 18 da uuidv7() funksiyasi qo'shildi:
SELECT uuidv7() AS v7 FROM generate_series(1, 3);Kanon chiqishi:
v7
--------------------------------------
01a16c43-797b-734d-9d2d-9a84b11a092b
01a16c43-797b-73c0-a6a9-cc1845b00853
01a16c43-797b-73cd-9301-ef84f15eac89
(3 rows)Boshi bir xil (01a16c43-797b- — bitta millisekund), keyingi qismi o'sib boradi: 734d < 73c0 < 73cd. Hujjatga ko'ra (2026-10 holatiga), uuidv7() vaqtni millisekund va undan kichik bo'lak bilan yozadi — shuning uchun bitta millisekund ichidagilar ham bu yerda tartib bilan chiqdi. Ya'ni v7 lar yaratilish tartibida tartiblanadi — deyarli bigint kabi. Ichidagi vaqtni o'qish ham mumkin:
SELECT uuid_extract_timestamp(uuidv7()) AS v7_ichidagi_vaqt,
uuidv7(interval '-1 day') AS kecha_vaqtli_v7;Kanon chiqishi:
v7_ichidagi_vaqt | kecha_vaqtli_v7
----------------------------+--------------------------------------
2026-10-24 04:55:23.772+00 | 01a1671d-1d7c-7395-b0d5-1f6677999f30
(1 row)uuidv7(interval '-1 day') — kecha vaqti bilan UUID (masalan, eski ma'lumotni import qilganda). PostgreSQL 17 va undan eskida uuidv7() yo'q: biz o'z nusxamizda 17.6-bob sinaganda function uuidv7() does not exist xatosini oldik. Eski versiyalarda v7 ni ilova (JS kutubxonasi) yaratadi.
Diqqat: v7 ichidagi vaqtni istalgan odam o'qiy oladi. URL'dagi v7 dan "bu buyurtma qachon yaratilgan" bilinadi. Ko'p holatda bu zararsiz, lekin yaratilish vaqti sir bo'lsa — v4.
Tekshirib ko'ring: Nega
ORDER BY idUUID v4 ustunida "yaratilish tartibi" ni bermaydi, v7 da esa beradi?
Javob
v4 ning hamma bitlari tasodifiy — kattaroq UUID kechroq yaratilgan degani emas. v7 ning boshida vaqt turadi, shuning uchun solishtirish avval vaqt bo'yicha bo'ladi. Bitta millisekund ichida yaratilganlar ham kanon chiqishida tartib bilan chiqdi (734d < 73c0 < 73cd) — millisekunddan kichik vaqt bo'lagi tufayli. Hujjat uni "vaqt bo'yicha tartiblanadigan" deydi, lekin har qanday holatda qat'iy o'sishni va'da qilmaydi. Turli serverlarda esa turli serverlarda yaratilgan v7 lar soatlar farqi tufayli aniq tartibda bo'lmasligi mumkin.
5. O'lchov: bigint, v4, v7
Kanon har tur bilan 1 000 000 qatorli jadvalni uch marta to'ldirdi va medianani oldi (PostgreSQL 18.6, Docker Desktop, Windows 11, i5-12500H, pg 8.23.1):
- bigint IDENTITYPK indeks 21 MB2 579 ms
- uuidv7()PK indeks 30 MB3 701 ms
- gen_random_uuid() (v4)PK indeks 38 MB6 686 ms
Manba: IlmHamroh kanon o'lchovi, 2026-10-24: PostgreSQL 18.6 (Docker, WSL2), i5-12500H, t13-kalit.mjs
Xom natija. Har qatorda uch narsaga qarang: mediana (vaqt), PK indeks (hajm) va kamaygan id (tartibsizlik — diskda ketma-ket 100 000 qatordan nechtasida ID oldingisidan kichik):
bigint INSERT 1 mln: mediana 2579 ms (2579, 2462, 3131); jadval 50 MB, PK indeks 21 MB; diskdagi tartibda kamaygan id: 0/100000
uuid_v4 INSERT 1 mln: mediana 6686 ms (6686, 6565, 6808); jadval 57 MB, PK indeks 38 MB; diskdagi tartibda kamaygan id: 50182/100000
uuid_v7 INSERT 1 mln: mediana 3701 ms (3828, 3701, 3344); jadval 57 MB, PK indeks 30 MB; diskdagi tartibda kamaygan id: 0/100000Uchta xulosa:
- Hajm. UUID 16 bayt,
bigint— 8. Jadval 50 → 57 MB, PK indeks 21 → 30–38 MB. Har tashqi kalit ustuni ham shuncha kattalashadi. - v4 sekin. Diskda ketma-ket 100 000 qatorning 50 182 tasida ID oldingisidan kichik — butunlay tartibsiz. Har yangi qiymat indeksning tasodifiy joyiga tushadi va B-tree sahifalari bo'linadi, keshdan chiqib ketadi. Natija —
bigintdan 2.6 baravar sekin. - v7 o'rtada. Tartibsiz qator 0 — har yangi qiymat indeks oxiriga tushadi, xuddi
bigintkabi. v4 dan deyarli ikki baravar tez, lekin hajm UUID'niki.
Bu bitta mashinadagi o'lchov: raqamlar sizda boshqacha chiqadi, nisbat esa odatda saqlanadi. B-tree ichini Indeks nima: B-tree ichidan darsida ochamiz.
6. ULID va nanoid
Kanon ularni sinamagan — bu bo'lim kutubxonalar hujjatiga tayanadi (2026-10).
- ULID — UUID v7 ga o'xshash g'oya (boshida vaqt, keyin tasodif), lekin 26 belgili Crockford base32 matn:
01ARZ3NDEKTSV4RRFFQ69G5FAV. UUID v7 standartlashguncha keng ishlatilgan. Postgres'da o'z turi yo'q —textda saqlanadi yokiuuidga aylantiriladi. - nanoid — qisqa (sukut 21 belgi), URL uchun xavfsiz tasodifiy satr:
V1StGXR8_Z5jdHi6B-myT. Tartib yo'q (v4 kabi). Qisqa havolalar, taklif kodlari uchun qulay.
Matn sifatida ular ko'proq joy oladi. Biz o'z nusxamizda (PostgreSQL 17.6) pg_column_size bilan o'lchadik: ULID matni — 30 bayt, nanoid — 25, uuid — 16, bigint — 8. Postgres'da v7 kerak bo'lsa, uuid turidagi v7 ULID'dan ixchamroq va standart.
7. Hujumchi nigohi: ketma-ket ID oshkor qiladi
Ketma-ket bigint ID tashqariga chiqsa, u ma'lumot beradi:
- Hajm.
/buyurtmalar/1042— raqobatchi bir hafta ichida ikkita buyurtma berib, ID farqidan kunlik buyurtmalar sonini hisoblaydi. - Sanab chiqish (enumeration).
/vazifalar/41,/vazifalar/42,/vazifalar/43… — skript hamma raqamlarni ketma-ket sinaydi.
Ikkinchisi xavfli faqat bitta holatda: server "bu vazifa so'rovchiga tegishlimi?" deb tekshirmasa. Bu xato IDOR (Insecure Direct Object Reference) deb ataladi va real hayotda eng ko'p topiladigan zaifliklardan. Diqqat: UUID uni tuzatmaydi, faqat taxmin qilishni qiyinlashtiradi. UUID havolasi xatda, logda yoki skrinshotda oshkor bo'lishi mumkin — tekshiruv yo'q bo'lsa, u ham ochiladi.
Himoya:
- Ruxsat tekshiruvi har so'rovda — "egasi_id = joriy foydalanuvchi". Bu asosiy himoya, uni 28-qismda (auth) quramiz.
- Tashqi ID alohida — kerak bo'lsa ichkarida
bigintPK (tez JOIN, ixcham FK), tashqariga esauuidustun (noyob indeks bilan). 3-mashqda shu naqshni yasaysiz.
vazifalar ilovasida hamma so'rov foydalanuvchining o'z vazifalari bilan cheklanadi — shuning uchun kanon ichki bigint ni tanlagan va uni API'da ko'rsatishga ruxsat beradi.
8. SQL qadami: kanon qarori — bigint IDENTITY
vazifalar bazasidagi hamma jadval uchun kanon qarori:
| Savol | Javob |
|---|---|
| Tabiiy yoki surrogat | surrogat (id), email — alohida noyob indeks |
| Tur | bigint (int tugab qolmasin) |
| Kim yaratadi | baza, GENERATED ALWAYS AS IDENTITY |
| UUID | kerak emas: ID'ni oldindan yaratish yo'q, bitta baza |
O'lchov qarorni tasdiqladi: eng tez yozish va eng kichik indeks. Bitta tuzoq oldinda: Node.js'dagi pg kutubxonasi bigint ni JS satri sifatida qaytaradi (PostgreSQL ma'lumot turlari darsidagi maslahat) — buni node-postgres (pg) asoslari darsida hal qilamiz. Bu darsda sxema fayli o'zgarmaydi va commit yo'q — qaror 001-jadvallar.sql da allaqachon bor.
9. Ko'p uchraydigan xatolar
9.1 serial va qo'lda ID
Import yoki test ma'lumotida ID qo'lda yozildi — keyin duplicate key value violates unique constraint "…_pkey". Yechim: GENERATED ALWAYS AS IDENTITY; eski serial jadvalda esa sequence'ni eng katta ID'ga moslash (setval).
9.2 int ID
Bir necha yildan keyin integer out of range — yangi qator yozilmaydi. ID — bigint.
9.3 Teshiklarni "tuzatish"
"ID 3 yo'q, demak qator o'chirilgan" — noto'g'ri xulosa: bekor qilingan tranzaksiya yoki rad etilgan INSERT ham raqam yeydi. ID'dan sanoq sifatida foydalanmang.
9.4 UUID'ni text da saqlash
'dfe8ca9d-…' matn sifatida — 36+ bayt, uuid turida — 16. Solishtirish ham sekinroq. UUID — uuid turida (3-mashqda o'lchaysiz).
9.5 "UUID — xavfsizlik"
Taxmin qilib bo'lmaydigan ID ruxsat tekshiruvini almashtirmaydi. Har so'rovda "bu yozuv shu foydalanuvchinikimi?" deb so'rang.
10. Mashqlar
Mashqlar kurs/mashqlar/26/13-primary-key/ da.
1-mashq (oson): to'ldiring
- Ma'nosiz, baza bergan kalit — kalit.
- Qo'lda yozishni taqiqlaydigan identity —
GENERATED AS IDENTITY. - Ketma-ket raqam beradigan Postgres obyekti — .
- Vaqt bo'yicha tartiblangan UUID versiyasi — v.
- Ruxsat tekshirilmagani uchun boshqaning yozuvini ochish zaifligi — .
Yechim
Surrogat kalit; GENERATED ALWAYS AS IDENTITY (BY DEFAULT qo'lda yozishga ruxsat beradi); sequence; UUID v7 (uuidv7(), PostgreSQL 18); IDOR — Insecure Direct Object Reference.
2-mashq (o'rta): ID'larni oldindan ayting
Bo'sh buyurtmalar jadvali: id bigint GENERATED ALWAYS AS IDENTITY, taom text NOT NULL CHECK (taom <> ''). Ketma-ket:
oshvamantibittaINSERTbilan.BEGIN,lag'monqo'shish,ROLLBACK.- Bo'sh
''qo'shish (xato beradi). somsaqo'shish,RETURNING id.
somsa ning id si qancha? Jadvalda qaysi id lar bo'ladi?
Yechim
Biz ishga tushirgan natija (PostgreSQL 17.6, psql -q — muvaffaqiyatli buyruqlar jim; xatolar qisqa shaklda; oxirida SELECT id, taom FROM buyurtmalar ORDER BY id;):
ERROR: new row for relation "buyurtmalar" violates check constraint "buyurtmalar_taom_check"
id
----
5
(1 row)
id | taom
----+-------
1 | osh
2 | manti
5 | somsa
(3 rows)osh — 1, manti — 2. lag'mon 3 ni oldi, lekin ROLLBACK — raqam qaytmadi. Bo'sh matn 4 ni oldi, lekin CHECK rad etdi — raqam yana kuydi. somsa — 5. Jadvalda 1, 2, 5 — ikki teshik, ikkalasi ham normal.
3-mashq (qiyin): ichki va tashqi ID
«Bahor» onlayn buyurtmalari jadvalini yarating: ichki id (bigint identity, PK) va tashqariga ko'rsatiladigan ochiq_id (uuid, DEFAULT gen_random_uuid(), noyob). 1 000 ta buyurtma qo'shing (generate_series bilan — DB turlari xaritasi va SQL vs NoSQL darsida ko'rgansiz). Tekshiring:
- Hamma
ochiq_idnoyobmi va matn ko'rinishi necha belgi? ochiq_idbo'yicha (500-buyurtmaniki) qidirish to'g'ri qatorni topadimi?id,ochiq_idvaochiq_id::textnecha bayt?
PostgreSQL 18 da uuidv7() yaxshiroq tanlov bo'lardi; yechim 17 da ham ishlashi uchun v4 bilan yozilgan.
Yechim
CREATE TABLE buyurtmalar (
id bigint GENERATED ALWAYS AS IDENTITY,
ochiq_id uuid NOT NULL DEFAULT gen_random_uuid(),
taom text NOT NULL,
CONSTRAINT buyurtmalar_pkey PRIMARY KEY (id),
CONSTRAINT buyurtmalar_ochiq_id_key UNIQUE (ochiq_id)
);
INSERT INTO buyurtmalar (taom)
SELECT 'taom ' || g FROM generate_series(1, 1000) AS g;
SELECT count(*) AS jami, count(DISTINCT ochiq_id) AS noyob,
min(length(ochiq_id::text)) AS uzunlik
FROM buyurtmalar;
SELECT id, taom FROM buyurtmalar
WHERE ochiq_id = (SELECT ochiq_id FROM buyurtmalar WHERE id = 500);
SELECT pg_column_size(id) AS id_bayt,
pg_column_size(ochiq_id) AS uuid_bayt,
pg_column_size(ochiq_id::text) AS matn_bayt
FROM buyurtmalar WHERE id = 1;Natija (PostgreSQL 17.6):
CREATE TABLE
INSERT 0 1000
jami | noyob | uzunlik
------+-------+---------
1000 | 1000 | 36
(1 row)
id | taom
-----+----------
500 | taom 500
(1 row)
id_bayt | uuid_bayt | matn_bayt
---------+-----------+-----------
8 | 16 | 40
(1 row)ochiq_id hammasi noyob, matn ko'rinishi 36 belgi. Qidiruv to'g'ri qatorni topdi — API /buyurtmalar/<ochiq_id> shu so'rov bilan ishlaydi (va baribir egasini tekshiradi!). Bitta UUID uuid turida 16 bayt, matn qilib saqlansa — 40 bayt: ikki yarim baravar ko'p. Ichki FK'lar esa 8 baytlik id ni ishlatadi.
4-mashq: SQL qadami
DB turlari xaritasi va SQL vs NoSQL darsidagi loyihangiz (masalan portfolio) jadvallari uchun kalit tanlang: har jadvalda PK nima, qaysi tabiiy noyoblik UNIQUE bo'lib qoladi, qaysi ID URL'da ko'rinadi. Bitta qatorda UUID tanlang va sababini yozing.
Yechim
Namuna (portfolio): loyihalar — PK id bigint, slug (vazifalar-ilovasi) — UNIQUE, URL'da slug ko'rinadi (ID emas). postlar — xuddi shunday, slug bilan. xabarlar (aloqa formasi) — PK id bigint, tashqariga chiqmaydi. yuklamalar (fayllar) — PK id bigint + ochiq_id uuid: fayl havolasi xatlarda yuboriladi, ketma-ket raqam bilan boshqa fayllarni sanab chiqish mumkin bo'lmasin. Slug — ham tabiiy kalit, lekin PK emas: post nomi o'zgarsa slug ham o'zgarishi mumkin.
11. Real ishda
- Ko'p uchraydigan sxemalar: ko'p SaaS loyihalarda ichki
bigint+ tashqiuuid; mikroxizmatlarda va mobil oflayn ilovalarda — UUID (endi ko'pincha v7). ORM'lar (Prisma, Drizzle) ikkalasini ham qo'llaydi. - Migratsiya hikoyalari:
int→bigintga o'tish, v4 → v7 ga o'tish — katta jadvallarda soatlab ish. Boshida to'g'ri tanlash arzon. - Xavfsizlik auditida: "ID'ni o'zgartirib boshqaning ma'lumotini ochish mumkinmi?" — penetratsion testlarning birinchi savollaridan.
- Intervyu savollari: "
serialvaIDENTITYfarqi?", "UUID v4 nega indeksni sekinlashtiradi?", "Ketma-ket ID'ni tashqariga ko'rsatish xavflimi?". Kuchli javobda IDOR va ruxsat tekshiruvi tilga olinadi.
Xulosa
- Surrogat kalit (ma'nosiz
id) — PK; tabiiy noyoblik (email) — alohidaUNIQUE. serialqo'lda ID'ga yo'l qo'yadi va to'qnashadi;bigint GENERATED ALWAYS AS IDENTITY— standart va xavfsiz.- Sequence
ROLLBACKga bo'ysunmaydi — ID'dagi teshiklar normal (bekor tranzaksiya, rad etilganINSERT). - 1 mln qator:
bigint2.6 s,uuidv7()3.7 s, v4 6.7 s; PK indeks 21 / 30 / 38 MB — v4 tartibsizligi indeksni sekinlashtiradi. uuidv7()— PostgreSQL 18; ichida yaratilish vaqti ochiq turadi.- Ketma-ket ID hajmni oshkor qiladi; IDOR'dan himoya — UUID emas, har so'rovdagi ruxsat tekshiruvi.
Keyingi dars: Foreign key va referential actions — vazifalar.egasi_id ga tashqi kalit qo'yamiz va foydalanuvchi o'chirilganda nima bo'lishini tanlaymiz.
Manbalar
- PostgreSQL 18 hujjati — Identity Columns: https://www.postgresql.org/docs/18/ddl-identity-columns.html
- PostgreSQL 18 hujjati — UUID Functions (
uuidv7,uuid_extract_timestamp): https://www.postgresql.org/docs/18/functions-uuid.html - PostgreSQL 18 hujjati — Sequence Manipulation Functions: https://www.postgresql.org/docs/18/functions-sequence.html
- RFC 9562 — Universally Unique IDentifiers (UUIDs), 2024: https://www.rfc-editor.org/rfc/rfc9562
- OWASP — Insecure Direct Object Reference Prevention: https://cheatsheetseries.owasp.org/cheatsheets/Insecure_Direct_Object_Reference_Prevention_Cheat_Sheet.html
- ULID spetsifikatsiyasi: https://github.com/ulid/spec
- nanoid: https://github.com/ai/nanoid
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!