Čili index jakožto struktura je uložena na disku. Jaká data jsou uložena v té RAM? To jsou ta data, ke kterým se odkazujeme přes ty indexy nebo přímo ty indexy?
Vy si nedáte pokoj, dokud se nepustíme pod kapotu, žejo? Zkusím to nakousnout. Sám znám jenom obrysy. Možná vytrollím pár dalších, kteří doplní chybějící kousky, únavné podrobnosti, vyvrátí nepravdy atd.
No z velmi vysokého nadhledu potřebujete data v RAMce, aby s nimi kód běžící na CPU mohl pracovat, a potřebujete je i ukládat na disk - kvůli perzistenci (aby se počítač směl někdy vypnout) a taky proto, že se Vám celá databáze do RAMky často ani náhodou nevejde. Takže potřebujete mít převodní mechanismy mezi formátem v RAMce a na disku, a potřebujete algoritmy které si dokážou dotahovat z disku do RAM jenom nezbytné minimum, které momentálně potřebují k práci (a případně využití dostupné RAM nějak optimalizovat). A konkrétně u databází to znamená, že jak indexy tak "užitečná data tabulek" potřebujete na disku i v RAMce. (Byl by k něčemu užitečný index, který by se musel vejít do RAMky, a konstruoval by se vždy při startu RDBMS procesu?) Což nevylučuje možnost, že v RAMce budou nějaké pracovní, efemérní indexy a další struktury "navíc", které se na disk neukládají (po restartu se vytvoří znova).
V širším úhlu pohledu se opět jedná o obecný programátorský problém :-) Jiné datové struktury jsou vhodné pro manipulaci s daty v RAM, jiné pro uložení dat na disku. RAMka používá jiné alokační strategie a organizaci dat než diskový prostor, a jiné způsoby odkazování/adresace. V RAMce je výhodné (rychlé) používat přímé pointery na strojové adresy v paměťovém prostoru CPU, kde je alokován nějaký struct nebo jeho member, [ i ]tá položka v poli apod. Přímo nad těmito strukturami pracují algoritmy zkompilované do binárního strojového kódu, běžícího na CPU = přímo s nativními pointery pracují instrukce CPU. Samozřejmě by šlo i v RAMce letmo dohledávat nepřímé odkazy přes "lidské" identifikátory... možná to tak zrovna databáze dělají, nevím. (Teď bych sem nepletl interpretované jazyky a varianty jejich "vm" - v zásadě je tam tlustá vrstva abstrakce navíc.) Naproti tomu na disku je bloková vrstva adresace, nad ní oddíly, v nich souborový systém, a v rámci souboru ofsety po bajtech od začátku... a formát pro uložení v souboru může vypadat velmi různě. Každopádně "přímo na disku" nemůže nic běžet, protože v datech na disku se CPU přehrabuje hodně dlouhým klacíkem... Err... v dnes obvyklých architekturách :-)
Obecně čím víc má být formát na disku lidsky čitelný, tím víc bude tíhnout k využití lidsky čitelné znakové sady (ASCII, Unicode apod.), řádky a pole uvnitř řádků budou členěny "čitelnými" znaky (čárky, středníky, tabulátory, konce řádku apod.), formát může tolerovat volné místo, komentáře apod. Viz třeba CVS, YAML, XML apod. Zároveň "lidsky čitelné" formáty souborů vyžadují nejvíc zpracování při převodu mezi runtime podobou dat v RAMce a formátem souborů. Proto se často používají jako exportní/importní formáty.
Naopak "těžce binární" formáty umožňují přesouvat data mezi RAM a diskem s menší mírou letmého přežvykování. Binární formáty čísel a řetězců, odkazy na bajtové ofsety spíš než na "lidské" identifikátory / čísla řádek/záznamů, implicitní velikosti uložených "structů" apod. Dovedl bych si třeba představit, že v on-disk formátu s pevnou délkou "DB řádku" zůstanou volná místa pro machine-level pointery (pokud jsou potřeba) ale to si cucám z prstu. Binární formáty jsou obvyklejší jako "nativní pracovní formát" všelijakého softwaru. (Radši nebudu odbočovat, co je to relokovatelný executable kód, dynamické linkování sdílených knihoven apod.)
Přiznám bez mučení, že netuším, jak vypadá on-disk formát dnešních reálných RDBMS. Našel jsem nějaká hesla na wikipedii
1 2 , ale přijde mi to čtení stále dost abstraktní. Mohu jenom doporučit zdrojáky nějaké open-source DB.
Traduje se, že RDBMS nefungují příliš rády skrz souborový systém. Raději fungují přímo na vyhrazený diskový oddíl. Odpadne tím magie souborového systému, která může zpomalovat, násobit počet seeků apod. Ze stejného důvodu se tradovalo, že velikost stránky virtuální paměti hostitelského CPU (na x86 tradičně 4 kB) je pro RDBMS optimální alokační jednotkou na disku (sektor tradičně 512B, u nových disků točivých i SSD převažuje 4 kB). Ale celé to má další zádrhele, třeba huge pages apod. Chtěl jsem říct, že takovéto souznění velikosti stránek by nasvědčovalo přímočarému kopírování dat 1:1 mezi RAM a diskem, bez složitého přežvykování = on-disk formát velmi podobný pracovnímu formátu v RAM. Ale jestli je to tak doopravdy...
Jinak je asi vcelku známá věc, jak funguje swapování. RAMka je "overbooked".
Virtuální paměťové prostory user-space procesů jsou v součtu větší než skutečná fyzická RAM. User-space proces alokuje paměť podle potřeby. Stránky přidělené user-space procesům v kernelu eviduje memory management subsystém a pomocí
LRU a spol si mezi stránkami vybírá ty "nejuleželejsí" - které v případě nedostatku RAM odkládá do swapu na disk. V tom případě se ovšem může snadno stát, že konkrétní user-space proces později sáhne "do prázdna" = chce stránku, která není v RAMce, protože byla odložena na disk. Tím vyvolá na procesoru výjimku zvanou
page fault - vlastně obsluhu IRQ a přepnutí kontextu do kernelu. Kernel si nějakou tu stránku v paměti uvolní (podle LRU), přimapuje ji procesu který zrovna způsobil page fault, loadne do ní příslušnou stránku ze swapu a proces může pokračovat v práci (je v plánovači vrácen mezi "běžící"). Proč to tady vytahuju? Hned to bude.
Se soubory se totiž dá pracovat dvěma způsoby:
- Jednak známějším open()/seek()/read()/write()/close() = soubor v programu otevřete a nějak ho žužláte, třeba do něj po jednom sekvenčně zapisujete ASCII řádky nebo nějaké binární "záznamy". Tzn. data podáváte syscallům skrz nějaký buffer v user space, a data se následně kopírují do kernelu.
- No a druhý způsob je, že se soubor dá namapovat do paměti. Tuhle fintu umí syscall mmap(). Přesněji řečeno, mmap() je třeba zavolat na otevřený file descriptor (tzn. napřed proběhne open()) a jsou tam nějaké nuance jaké flagy mmap()u předat. Tímto způsobem není potřeba data kopírovat mezi kernelem a user space - prostě se do user space namapuje pár stránek přímo z bufferu blokové vrstvy. A tento mechanismus funguje prakticky stejně jako paging podložený swapem (nuance/flagy stranou) tzn. dá se zařídit, aby se ze souboru dotahovaly stránky až když jsou potřeba (díky page faults). V těch heslech na wikipedii se dá dočíst, že page faultů je vlastně několik stupňů, a že se může stát, že bloková vrstva díky read-aheadu načte do bufferů pár stránek navíc, nepřimapují se všechny hned, ale příští page-fault třeba sáhne jenom po další stránce v bufferu (nemusí čekat na diskovou operaci).
Čili opravdu líný RDBMS engine (= user-space démon) by mohl třeba jenom namapovat z disku celý extent / table-space do svého adresního prostoru a těžkou práci s ládováním z disku do RAM (a naopak odkládáním na disk) nechat na memory-managementu hostitelského OS :-) = na jeho LRU, page-faultech, blokové vrstvě, managementu disk IO bufferů a tak. Nicméně pokud jsem měl možnost pozorovat, RDBMS si naopak tyhle věci rády spravují samy... naalokují si ze systému co nejvíc RAMky a pak si s ní interně hospodaří po svém.
Tohle jsou opravdu jenom velmi hrubé obrysy. Na rootu bacha, "kdo se moc ptá, moc se dozví." Jestli se návnady chytí někdo od fochu, budu nakonec se svým tapetováním vypadat jako břídil.