Lakehouse: ko'l va ombor birlashgani
Ikki bino va ular orasidagi yuk mashinasi
Kompaniyada ikkita bino bor.
Birinchi bino — ombor (data warehouse). Ichkarida hammasi tartibli: javonlar raqamlangan, har quti yorliqlangan, ro'yxat bor. Buxgalter kirsa, kerakli hujjatni bir daqiqada topadi.
Lekin omborga hamma narsani qo'ya olmaysiz. Qabulxonada qoida bor: "faqat rasmiylashtirilgan hujjatlar". Video kassetami? Qabul qilinmaydi. Tartibsiz qog'ozlar to'plamimi? Avval rasmiylashtiring.
Ikkinchi bino — ko'l (data lake). Bu ulkan angar. U yerga hamma narsani tashlash mumkin: videolar, rasmlar, log fayllar, tartibsiz qog'ozlar. Arzon va cheksiz.
Lekin u yerdan biror narsa topish — alohida kasb. Ro'yxat yo'q, tartib yo'q.
Natijada kompaniyada ikki nusxa ma'lumot paydo bo'ladi va ular orasida doimiy yuk mashinasi qatnaydi: ko'ldan ombor uchun ma'lumot tayyorlanadi, tozalanadi, ko'chiriladi.
diagramma( { "Manbalar": (0, 1.8, 1.9, 1.0, "manba"), "KO'L\n(arzon, tartibsiz)": (2.7, 3.2, 2.4, 1.1, "saqlash"), "OMBOR\n(qimmat, tartibli)": (2.7, 0.4, 2.4, 1.1, "saqlash"), "ETL\n(yuk mashinasi)": (6.0, 1.8, 2.1, 1.0, "qayta"), "ML / Data Science": (9.2, 3.2, 2.4, 1.0, "xizmat"), "Hisobot / BI": (9.2, 0.4, 2.4, 1.0, "xizmat"), }, [ ("Manbalar", "KO'L\n(arzon, tartibsiz)"), ("KO'L\n(arzon, tartibsiz)", "ETL\n(yuk mashinasi)"), ("ETL\n(yuk mashinasi)", "OMBOR\n(qimmat, tartibli)"), ("KO'L\n(arzon, tartibsiz)", "ML / Data Science"), ("OMBOR\n(qimmat, tartibli)", "Hisobot / BI"), ], nom="kol_ombor.png", kenglik=13, izoh="Eski tuzilish: ikki nusxa, ular orasida doimiy ko'chirish.", )

Eski tuzilish: ikki nusxa, ular orasida doimiy ko'chirish.
Ikki nusxaning narxi
Bu tuzilish o'n yil ishlatilgan va u ishlaydi. Lekin uch narxi bor.
Pul. Bir xil ma'lumotni ikki joyda saqlaysiz. Ombor qimmat — u tez disk va litsenziya talab qiladi.
Vaqt. Ko'ldagi ma'lumot ombordan doim orqada. ETL har kecha ishlaydi, demak hisobot kechagi holatni ko'rsatadi.
Ishonch. Eng yomoni shu. Ma'lumot muhandisi ko'ldan hisoblaydi, tahlilchi ombordan hisoblaydi — ikki xil raqam chiqadi. Yig'ilishda ikki kishi bir-biriga raqam ko'rsatadi va hech kim qaysi biri to'g'riligini bilmaydi.
Bu holatni ko'rgan har bir jamoa xuddi shu savolni beradi: nega ikkita nusxa kerak?
Lakehouse g'oyasi
Javob: 15-bobdan keyin — kerak emas.
Va sababini tushunish uchun savolni boshqacha qo'yish kerak. Nega ombor qimmat va tartibli, ko'l esa arzon va tartibsiz edi?
Ko'pchilik "chunki ombor kuchliroq dastur" deb o'ylaydi. Noto'g'ri.
Haqiqiy sabab: omborda ro'yxat bor edi, ko'lda esa yo'q. Ombor har qutini qayd qiladi, har o'zgarishni yozadi, shuning uchun u savollarga javob bera oladi. Ko'l esa shunchaki fayllar uyumi — javob beradigan hech narsa yo'q.
Ya'ni farq saqlashda emas, hisobotda edi.
Endi 15-bobni eslang. Biz nima qildik? Fayllar uyumiga ro'yxat qo'shdik. Fayllarni o'zgartirmadik — ichida baribir Parquet. Faqat ustiga metama'lumot qatlami qo'ydik.
Va shu zahoti ko'l ombor xususiyatlarini oldi: sxema, tranzaksiya, tarix, atomik o'zgarish.
Demak ikkita bino kerak emas edi. Bitta bino yetardi — unga shunchaki ro'yxat qo'yish kerak edi.
Bu — lakehouse: ko'l (lake) narxi + ombor (house) tartibi.
import pandas as pd show(pd.DataFrame([ {"": "Narxi", "Ko'l": "Arzon", "Ombor": "Qimmat", "Lakehouse": "Arzon"}, {"": "Sxema", "Ko'l": "Yo'q", "Ombor": "Bor", "Lakehouse": "Bor"}, {"": "Tranzaksiya", "Ko'l": "Yo'q", "Ombor": "Bor", "Lakehouse": "Bor"}, {"": "Tarix", "Ko'l": "Yo'q", "Ombor": "Cheklangan", "Lakehouse": "Time travel"}, {"": "Har xil ma'lumot", "Ko'l": "Ha", "Ombor": "Yo'q", "Lakehouse": "Ha"}, {"": "Nusxalar soni", "Ko'l": "1", "Ombor": "1", "Lakehouse": "1 (jami)"}, ]))
| Ko'l | Ombor | Lakehouse | |
|---|---|---|---|
| Narxi | Arzon | Qimmat | Arzon |
| Sxema | Yo'q | Bor | Bor |
| Tranzaksiya | Yo'q | Bor | Bor |
| Tarix | Yo'q | Cheklangan | Time travel |
| Har xil ma'lumot | Ha | Yo'q | Ha |
| Nusxalar soni | 1 | 1 | 1 (jami) |
Bitta ma'lumot, ikki xil iste'molchi
Eng muhim xususiyatni o'lchaymiz: bir xil jadvalni turli vositalar o'qiy oladimi?
15-bobda rostuy.elon jadvalini yaratgandik. Avval uni tayyorlaymiz.
python3 -c " import sys, shutil; sys.path.insert(0, 'src') import pandas as pd, pyarrow as pa from quvur.muz import katalog USTUN = ['elon_id','tuman','narx_mln','xonalar','tavsif_uzunligi','broker'] d = pd.read_csv('../datasets/ijara.csv').drop_duplicates('elon_id') shutil.rmtree('ombor', ignore_errors=True) c = katalog() c.create_namespace_if_not_exists('rostuy') ar = pa.Table.from_pandas(d[USTUN], preserve_index=False) t = c.create_table('rostuy.elon', schema=ar.schema) t.append(ar) print(f'lakehouse jadvali: {len(t.scan().to_arrow()):,} qator') print(f'fayllar : {len(list(t.scan().plan_files()))}') "
lakehouse jadvali: 11,906 qator fayllar : 1
Endi uch xil vosita bilan o'qiymiz — hech qanday ko'chirishsiz:
python3 -c " import sys, time; sys.path.insert(0, 'src') from quvur.muz import katalog c = katalog() t = c.load_table('rostuy.elon') # 1) pyiceberg - to'g'ridan-to'g'ri t0 = time.perf_counter() ark = t.scan().to_arrow() a = (time.perf_counter()-t0)*1000 print(f'1. pyiceberg : {len(ark):>6,} qator {a:>7.1f} ms') # 2) pandas - tahlilchi uslubi t0 = time.perf_counter() df = t.scan().to_pandas() ort = df.groupby('tuman')['narx_mln'].mean().sort_values(ascending=False) b = (time.perf_counter()-t0)*1000 print(f'2. pandas : {len(df):>6,} qator {b:>7.1f} ms') # 3) DuckDB - SQL uslubi (Iceberg qaytargan arrow ustida) import duckdb con = duckdb.connect() t0 = time.perf_counter() r = con.execute('SELECT tuman, avg(narx_mln) m FROM ark GROUP BY tuman ORDER BY m DESC').fetchall() d_ms = (time.perf_counter()-t0)*1000 print(f'3. DuckDB SQL : {len(r):>6,} guruh {d_ms:>7.1f} ms') print() print('Eng qimmat tuman:') print(f' pandas : {ort.index[0]:14s} {ort.iloc[0]:.3f}') print(f' DuckDB : {r[0][0]:14s} {r[0][1]:.3f}') print() print(f'natijalar bir xilmi: {abs(ort.iloc[0] - r[0][1]) < 1e-9}') con.close() "
1. pyiceberg : 11,906 qator 10.4 ms 2. pandas : 11,906 qator 11.3 ms 3. DuckDB SQL : 10 guruh 6.9 ms Eng qimmat tuman: pandas : Yakkasaroy 7.816 DuckDB : Yakkasaroy 7.816 natijalar bir xilmi: True
Uchala vosita bir xil fayllarni o'qidi va bir xil javob berdi. Hech narsa ko'chirilmadi, ikkinchi nusxa yaratilmadi.
Mana lakehouse ning asosiy va'dasi: tahlilchi SQL yozadi, ma'lumot olimi pandas ishlatadi, quvur Spark bilan yozadi — manba bitta.
"Bitta nusxa" hamma muammoni hal qilmaydi
Lakehouse jismoniy nusxani yo'q qiladi. Lekin mantiqiy nusxa qolaveradi.
Misol: tahlilchi "faol e'lonlar" ni holat='faol' deb hisoblaydi, ma'lumot olimi esa
"oxirgi 30 kunda yangilangan" deb hisoblaydi. Bitta jadval, ikki xil ta'rif, yana ikki xil
raqam.
Buni hal qiladigan narsa format emas — kelishilgan ta'rif. Odatda bu dbt kabi vosita
bilan umumiy modelga yoziladi: "faol" nima ekani bir joyda ta'riflanadi va hamma o'shani
ishlatadi.
Qatlamlar: bronza, kumush, oltin
Lakehouse da hamma narsa bitta joyda, lekin bitta qozonda emas. Amalda uch qatlam ishlatiladi.
Restoran oshxonasini tasavvur qiling:
- Bronza — bozordan kelgan mahsulot. Yuvilmagan, to'g'ralmagan, qanday kelgan bo'lsa shunday.
- Kumush — yuvilgan, tozalangan, to'g'ralgan. Ishlatishga tayyor.
- Oltin — tayyor taom. Mijozga chiqariladi.
Endi eng muhim savol: nega bronzani saqlash kerak? U axlat-ku — yuvilmagan sabzi.
Sabab shunda: bir kuni siz tozalash qoidangizda xato topasiz.
Masalan, olti oy davomida narx_mln > 200 bo'lgan e'lonlarni "xato" deb tashlab
kelgansiz. Keyin ma'lum bo'ladiki, bular xato emas — bu lyuks kvartiralar, va ular
mijozlarning eng daromadli qismi edi.
Endi ularni qaytarib bo'ladimi?
- Bronza saqlangan bo'lsa — ha. Qoidani tuzatasiz, kumushni qaytadan quriladi, hammasi joyida.
- Bronza saqlanmagan bo'lsa — yo'q. O'sha e'lonlar butunlay yo'qolgan. Olti oylik ma'lumot qaytmaydi.
Mana qoida: bronza — sizning "bekor qilish" tugmangiz.
Shuning uchun bronzaga hech qachon tegmaydilar: tozalamaydilar, tuzatmaydilar, ustiga yozmaydilar. U faqat qo'shiladi.
import pandas as pd show(pd.DataFrame([ {"Qatlam": "Bronza", "Nima": "Xom manba, o'zgartirilmagan", "Kim ishlatadi": "Muhandis", "Nega kerak": "Qayta hisoblash imkoni"}, {"Qatlam": "Kumush", "Nima": "Tozalangan, sxema qo'llangan", "Kim ishlatadi": "Ma'lumot olimi", "Nega kerak": "Ishonchli asos"}, {"Qatlam": "Oltin", "Nima": "Yig'ilgan, biznes ta'rifi", "Kim ishlatadi": "Tahlilchi, direktor", "Nega kerak": "Tez hisobot"}, ]))
| Qatlam | Nima | Kim ishlatadi | Nega kerak |
|---|---|---|---|
| Bronza | Xom manba, o'zgartirilmagan | Muhandis | Qayta hisoblash imkoni |
| Kumush | Tozalangan, sxema qo'llangan | Ma'lumot olimi | Ishonchli asos |
| Oltin | Yig'ilgan, biznes ta'rifi | Tahlilchi, direktor | Tez hisobot |
Uchala qatlamni quramiz va hajmini solishtiramiz:
python3 -c " import sys, pathlib; sys.path.insert(0, 'src') import pandas as pd, pyarrow as pa from quvur.muz import katalog c = katalog() c.create_namespace_if_not_exists('bronza') c.create_namespace_if_not_exists('kumush') c.create_namespace_if_not_exists('oltin') xom = pd.read_csv('../datasets/ijara.csv') # BRONZA: qanday kelgan bo'lsa shunday b = pa.Table.from_pandas(xom, preserve_index=False) try: c.drop_table('bronza.elon') except Exception: pass tb = c.create_table('bronza.elon', schema=b.schema); tb.append(b) # KUMUSH: takrorni tashlaymiz, qoidaga mos bo'lmaganini olib tashlaymiz k = xom.drop_duplicates('elon_id') k = k[(k['narx_mln'] > 0) & (k['maydon'] >= 10) & (k['xonalar'].between(1, 10))] kt = pa.Table.from_pandas(k, preserve_index=False) try: c.drop_table('kumush.elon') except Exception: pass tk = c.create_table('kumush.elon', schema=kt.schema); tk.append(kt) # OLTIN: biznes savoliga tayyor javob o = (k.groupby('tuman') .agg(elonlar=('elon_id','count'), ort_narx=('narx_mln','mean'), broker_ulushi=('broker','mean')) .reset_index()) ot = pa.Table.from_pandas(o, preserve_index=False) try: c.drop_table('oltin.tuman_xulosa') except Exception: pass to = c.create_table('oltin.tuman_xulosa', schema=ot.schema); to.append(ot) def hajm(t): return sum(pathlib.Path(f.file.file_path.replace('file://','')).stat().st_size for f in t.scan().plan_files()) print(f'{\"qatlam\":10s} {\"qatorlar\":>10s} {\"hajm KB\":>10s}') print('-' * 34) for nom, t in (('bronza', tb), ('kumush', tk), ('oltin', to)): n = len(t.scan().to_arrow()) print(f'{nom:10s} {n:>10,} {hajm(t)/1024:>10.1f}') "
qatlam qatorlar hajm KB ---------------------------------- bronza 12,000 166.1 kumush 11,906 164.4 oltin 10 2.0
Raqamlarni diqqat bilan o'qing — ular kutilganidek emas.
Bronza va kumush deyarli bir xil hajmda: tozalash atigi 94 qatorni olib tashladi, chunki bizning ma'lumot allaqachon toza edi. Haqiqiy manbalarda bu farq ancha katta bo'ladi (takroriy yozuvlar, buzuq qatorlar, kerak bo'lmagan ustunlar).
Haqiqiy sakrash oltin qatlamda: 164 KB dan 2 KB ga. Sabab oddiy — oltin qatlam ma'lumotni saqlamaydi, u javobni saqlaydi. 11 906 qator o'rniga 10 ta tuman.
Direktorning savoliga javob shuning uchun tezroq keladi:
python3 -c " import sys, time; sys.path.insert(0, 'src') from quvur.muz import katalog import pandas as pd c = katalog() kumush = c.load_table('kumush.elon') oltin = c.load_table('oltin.tuman_xulosa') t0 = time.perf_counter() d = kumush.scan().to_pandas() r1 = d.groupby('tuman')['narx_mln'].mean().sort_values(ascending=False) a = (time.perf_counter()-t0)*1000 t0 = time.perf_counter() r2 = oltin.scan().to_pandas().sort_values('ort_narx', ascending=False) b = (time.perf_counter()-t0)*1000 print(f'kumushdan hisoblash : {a:7.1f} ms') print(f'oltindan o\\'qish : {b:7.1f} ms') print(f'oltin {a/b:.1f}x tez') print() print(f'javob bir xilmi: {abs(r1.iloc[0] - r2[\"ort_narx\"].iloc[0]) < 1e-9}') print(f' eng qimmat tuman: {r2[\"tuman\"].iloc[0]} ({r2[\"ort_narx\"].iloc[0]:.3f} mln)') "
kumushdan hisoblash : 40.8 ms oltindan o'qish : 12.2 ms oltin 3.4x tez javob bir xilmi: True eng qimmat tuman: Yakkasaroy (7.816 mln)
Farq bu yerda kichik ko'rinadi, chunki kumush qatlam ham atigi 12 ming qator. Milliard qatorli jadvalda oltin qatlam soniyalarni daqiqalarga emas, millisekundlarga aylantiradi — chunki o'qiladigan qatorlar soni hajmga bog'liq emas, u doim 10 ta bo'lib qoladi.
Oltin qatlam ko'payib ketadi
Har yangi savol uchun yangi oltin jadval yaratish oson. Olti oydan keyin 80 ta oltin jadval bo'ladi va ularning yarmi bir-biriga zid.
Sabab: ikki jadval "faol e'lon" ni turlicha hisoblagan, ikkalasi ham to'g'ri ko'rinadi.
Qoida: oltin jadvallar kumushdan quriladi va ta'riflar bitta joyda yoziladi. Yangi oltin jadval qo'shishdan oldin "bu savolga mavjud jadval javob bera oladimi?" deb so'rang.
Qaysi biri sizga kerak
Lakehouse moda bo'lgani uchun tanlanmasligi kerak. Uchta holat bor:
| Sizda | Tavsiya |
|---|---|
| Ma'lumot bir necha GB, faqat SQL kerak | Oddiy baza (Postgres) yetarli |
| Terabaytlar, faqat hisobot kerak | Klassik ombor ham yaxshi ishlaydi |
| Terabaytlar + ML + har xil format | Lakehouse |
Eng ko'p uchraydigan xato
Kichik kompaniya 50 GB ma'lumot bilan lakehouse quradi: Iceberg, katalog, Spark, uch qatlam.
Natija: infratuzilmani saqlash uchun bir kishi to'liq band, hisobot esa Postgres dagidan sekinroq.
Lakehouse masshtab muammosini hal qiladi. Masshtab muammosi bo'lmasa, u faqat murakkablik qo'shadi. 50 GB — Postgres uchun katta emas.
Tozalash
python3 -c " import sys; sys.path.insert(0, 'src') from quvur.muz import katalog c = katalog() for t in ('bronza.elon', 'kumush.elon', 'oltin.tuman_xulosa'): try: c.drop_table(t); print(f'{t} o\\'chirildi') except Exception: pass print('rostuy.elon saqlanadi') "
bronza.elon o'chirildi kumush.elon o'chirildi oltin.tuman_xulosa o'chirildi rostuy.elon saqlanadi
Xulosa
| Tushuncha | Asosiy fikr |
|---|---|
| Ko'l | Arzon, hamma format, lekin tartibsiz |
| Ombor | Tartibli va tez, lekin qimmat va cheklangan |
| Ikki nusxa narxi | Pul, kechikish va ikki xil raqam |
| Lakehouse | Ko'l narxi + ombor tartibi (Iceberg orqali) |
| Bitta manba | pandas, SQL va Spark bir xil fayllarni o'qiydi |
| Mantiqiy nusxa | Format hal qilmaydi — kelishilgan ta'rif kerak |
| Bronza | Xom, o'zgartirilmagan; qayta hisoblash uchun |
| Kumush | Tozalangan; ishonchli asos |
| Oltin | Yig'ilgan; hisobot uchun tez |
| Qachon kerak emas | Ma'lumot kichik bo'lsa — Postgres yetarli |
Talaba ishi
- Bronza qatlamga takroriy
elon_idlar bor. Nechta? Kumushda ular nima bo'ldi? - Yangi oltin jadval yarating: xonalar soni bo'yicha o'rtacha narx. Uni kumushdan quring.
- Kumush qatlamga yangi qoida qo'shing (
tavsif_uzunligi > 0). Nechta qator yo'qoldi va oltin qatlamdagi raqamlar o'zgardimi? - Bronza jadvalni o'chirib ko'ring va kumushni qayta qurishga urinib ko'ring. Nega bronza qatlam saqlanishi kerakligini tushuntiring.
Bu bobni bepul o'qidingiz.
Kitob o'rgatadi, lekin sizning kodingiz nega ishlamaganini ayta olmaydi. Kursda uy vazifangiz tekshiriladi.