MySQL-ova arhitektura se poprilično razlikuje od arhitekture drugih database servera i kao takva može da se koristi u velikom broju slučajeva. MySQL nije savršen, ali je dovoljno fleksibilan za rad u zahtevnim okruženjima kao što su Web aplikacije. Pored njih, MySQL je pogodan za embedded aplikacije, data warehouses, delivery softver, HA i OLTP sisteme i tako dalje.
Da biste mogli da dobijete maksimum od MySQL-a, neophodno je da razumete njegov dizajn i kako da iskoristite sve prednosti koje on nudi. Jedna od najbitnijih prednosti je mogućnost korišćenja različitiih tipova storage engine-a čiji dizajn razdvaja procesiranje kverija od preuzimanja i čuvanja podataka.
Logička arhitektura
Prvi i osnovni nivo predstavlja servise koji nisu svojstveni samo za MySQL. U pitanju su client/server alati vezani za sam mrežni pristup – konekcije, autentifikacije, sigurnost i tako dalje.
Drugi nivo je deo u kojem stvari bivaju malo zanimljivije, jer je najveći deo MySQL mozga sadržan upravo u ovom delu – parsiranje kverija, analiza, optimizacija, keširanje i sve dostupne funkcije za rad sa vremenom, datumima, matematičke operacije, enkrpcija. Sve funkcionalnosti koje su dostupne putem nekog storage engine-a se nalaze ovde – stored procedure, triggeri i sl.
Treći nivo sadrži storage engine. Oni su odgovorni za upis i vraćanje podataka koji se nalaze u MySQL-u. Ne postoji savršen storage engine kao što ne postoji savršen fajl sistem jer svaki ima neke svoje prednosti i nedostatke. Server komunicira sa storage engine-om putem posebnog API-ja koji predstavlja interfejs iza kojeg se kriju bitne razlike između različitih storage engine-a ali sa druge strane ih čini poprilično transparentnim na nivou samog kverija. API se sastoji od velikog broja low-level funkcija koje se bave operacijama poput “startuj transakciju” ili “dohvati red koji sadrži taj i taj primarni ključ”. Storage engine ne služi za parsovanje kverija.
Hendlovanje konekcija i bezbednost
Svaki klijent zakačen za server dobija svoj thread u okviru samog procesa. Svi thread-ovi se keširaju tako da ne moraju biti kreirani ili uništeni za svaku novu konekciju.
Autentifikacija se bazira na korisničkom imenu, hosta sa kojeg dolazi i šifre. Dodatne sigurnosne mere uključuju SSL kao i X.509 sertifikat. Kada klijent ostvari konekciju sa serverom, server vrši verifikaciju privilegija za svaki izvršeni kveri (tipa da li klijent može da vrši UPDATE nad nekom bazom).
Optimizacija
MySQL vrši prevođenje (parsovanje, eng. parsing) kverija u internu strkturu (parse tree), nakon čega primenjuje niz optimizacija. Mnoge od ovih optimizacija mogu dovesti do potpunog prepisivanja kverija, ali i do oređivanja poretka kojim će se tabele čitati. Takođe, mogu da utiču i na izbor tipa indeksa i tako dalje. Ukoliko želite da utičete na sam proces optimizacije, to je moguće uraditi slanjem specifičnih parametara.
Od samog servera je moguće tražiti specifičan tip izveštaja u vidu objašnjenja kompletnog procesa optimizacije. Na ovaj način možete saznati koje odluke u procesu optimizacije je server doneo kako bi se vaš kveri izvršio na najefikasniji mogući način.
Sistem za optimizaciju kverija, generalno, ne mari mnogo za tip storage engine-a koji neka tabela koristi ali sam storage engine itekako utiče na sam proces optimizacije jer svaki put kada je potrebno izvršiti odgovarajući query, MySQL proverava koje mogućnosti poseduje storage engine. Pre nego što kveri počne da se parsuje, MySQL će prvo proveriti stanje query keša, koji čuva SELECT upite. Ukoliko je već neko izvršio identičan query, MySQL neće vršiti parsovanje, optimizaciju ili izvršenje upita već će vratiti rezultat pravo iz keša.
Konkurentnost
Često se postavlja pitanje – kako se rešava problem koji nastane kada dva upita u isto vreme treba da promene isti podatak? Srećom, MySQL poseduje nekoliko različitih tipova mehanizama koji se brinu o ovome i, generalno, to radi na dva nivoa: serverskom i storage engine nivou. Kontrola konkurentnosti nije nimalo naivna tema, u stvari postoji jako velika količina literature koja se bavi ovim problemom.
Da bi preciznije objasnio kako to funkcioniše, kao primer ću uzeti mailbox, mbox formata, unutar kojeg se čuvaju poruke. Sve poruke koje se čuvaju su konkatenirane zajedno, jedna za drugom. Ovakav način čuvanja informacija olakšava kasnije parsovanje poruka ali i sam proces isporučivanja – sve što je potrebno je dodati novu poruku na kraju fajla.
Pitanje koje se postavlja je: šta se dešava kada dva procesa pokušaju da isporuče poruke u isto vreme u isti mailbox? Ono što je jasno je da bi takav slučaj vrlo lako oštetio mailbox jer bi dobili dve ispreplitane poruke na kraju fajla. Za takve slučajeve postoji sistem zaključavanja koji sprečava korupciju fajlova. Ukoliko klijent pokuša da izvrši dostavu dok je mailbox zaključan, moraće da sačeka sve dok ne bude u mogućnosti da izvrši zaključavanje za sebe kako bi isporučio poruku.
Ovaj sistem radi jako dobro u praksi ali ne omogućava konkurentnost jer u jedinici vremena samo jedan proces ima pravo upisa, što može biti ozbiljan problem ukoliko treba da primite veliku količinu poruka.
Read/write zaključavanje
Čitanje poruka u okviru mailboxa nije nikakav problem jer čitanje nije destruktivna operacija pa u slučaju da dva procesa žele da čitaju u isto vreme, ništa strašno ne bi trebalo da se desi. Ono što može da bude problem je ako neko pokuša da obriše poruku koja se nalazi u sredini dok neko drugi pokušava da pročita sadržaj?
Ukoliko o mailboxu razmišljate kao o tabeli i o svakoj poruci kao redu u okviru te tabele, vrlo je lako shvatiti koliko problema može da nastane. U stvari, mailbox jeste vrlo jednostavan koncept baze podataka jer pisanje ili uklanjanje postojećih poruka je istovetno dodavanju ili brisanju redova u okviru same tabele.
Klasično rešenje problema konkurentnosti je u stvari jako jednostavno. Sistemi kod kojih postoji potreba za konkurentnim čitanjem/pisanjem često implementiraju sistem zaključavanja koji je često poznat kao ekskluzivno zaključavanje (exclusive locks) iliti – zaključavanje prilikom čitanja (read locks) i zaključavanje prilikom upisa (write locks).
Zaključavanje prilikom čitanja nad nekim resursom je deljeno tj. međusobno neblokirajuće – mnogobrojni klijenti mogu da čitaju u isto vreme a da ne utiču jedni na druge. Kod zaključavanja prilikom upisa situacija je malo drugačija, jer je ovaj tip zaključavanja ekskluzivan tj. ovakav tip zaključavanja blokira čitanje i upis drugih klijenata zarad očuvanja konherentnosti.
U svetu baza podataka, mehanizmi za zaključavanje su manje-više slični.
Granularno zaključavanje
Jedan od načina za unapređenje konkurentnosti nad deljenim resursom je da pažljivije birate šta ćete u nekom momentu zaključati. Drugim rečima, umesto da zaključate kompletan resurs, zaključajte samo onaj deo koji sadrži podatke koje želite da promenite.
Svaka operacija zaključavanja – postavljanje, provera da li je neki drugi proces izvršio zaključavanje, pa čak i otključavanje resursa ima odgovarajuće posledice. Ako sistem troši jako mnogo vremena pokušavajući da isprati sva zaključavanja umesto da smešta i vraća podatke, to može jako loše da se odrazi na performanse.
Mnogobrojne komercijalno dostupne baze podataka vam ne daju previše izbora – možete da birate između zaključavanja na nivou reda (row-level locking) ili nekih drugih tipova koji su poprilično kompleksni ali su optimizovani zarad ostvarivanja boljih performansi.
Table lock
Ovo je osnovni tip zaključavanja koji MySQL nudi i ima najmanji overhead. Zaključavanje cele tabele je analogno zaključavanju kompletnog mailboxa. Kada klijent želi da izvrši neki upis u tabelu (insert, delete, update), mora da zatraži table lock kako bi obezbedio ekskluzivnost nad resursom. Pošto u tom slučaju nema drugih klijenata koji vrše promene, drugi klijenti koji žele da vrše čitanja mogu da pristupe podacima jer ne predstavljaju nikakvu opasnost.
Zaključavanje tabele ima i svoje prednosti po pitanju samih performansi. Na primer, zaključavanje zarad upisa ima viši prioritet od svih drugih tipova, tako da zahtev za zaključavanjem zarad upisa će uvek biti prvi u queue listi iako u njoj već postoje zahtevi za zaključavanjem zarad čitanja (dok obrnuta situacija nije moguća).
Iako različiti tipovi storage engine-a umeju da se izbore sa zahtevima za zaključavanjem, MySQL sam često koristi različite metode koje se svoje na table lock u raznim situacijama. Recimo, kada pokušate da izvršite ALTER TABLE upit, server će izvršiti table lock bez obzira na tip storage engine-a.
Row lock
Ovaj tip zaključavanja nudi najveću konkurentnost ali ima i največi overhead. Zaključavanje na nivou reda je dostupno i ako koristite InnoDB i Falcon storage engine tj. ovaj tip zaključavanja je implementiran na nivou storage engine-a a ne na nivou celog servera. MySQL uopšte ne razaznaje raličite lock mehanizme koji su implementirani u okviru storage engine-a.
Za kraj
Ovom prilikom sam obradio osnove MySQL arhitekture, kao i pitanje konkurentnosti. Ovo su neke od osnovnih stvari koje je potrebno znati kako bi se ostvarile bolje performanse i, uopšte, razumela suština rada MySQL-a kao jednog od najzastupljenijih tipova relacionih baza podataka. U narednim tekstovima, baviću se drugim aspektima MySQL-a kao što su transakcije i tipovima storage engine-a.