6.1 Battle of Abbreviations: BASE vs. ACID

"Sa kimika, sinusukat ng pH ang relatibong kaasiman ng isang may tubig na solusyon. Ang sukat ng pH ay tumatakbo mula 0 (malakas na acidic na mga sangkap) hanggang 14 (malakas na alkaline na mga sangkap); ang purong tubig sa 25°C ay may pH na 7 at neutral.

Ginawa ng mga inhinyero ng data ang metapora na ito upang ihambing ang mga database tungkol sa pagiging maaasahan ng mga transaksyon."

Marahil, ang ideya ay ito: mas mataas ang pH, i.e. mas malapit ang database sa "alkaline" ("BASE"), hindi gaanong maaasahan ang mga transaksyon.

Ang mga sikat na relational database, tulad ng MySQL, ay lumitaw lamang sa batayan ng ACID. Ngunit sa nakalipas na sampung taon, ang tinatawag na mga database ng NoSQL, na pinagsasama-sama ang iba't ibang uri ng mga database sa ilalim ng pangalang ito, ay mahusay na nagawa nang walang ACID. Sa katunayan, mayroong isang malaking bilang ng mga developer na nagtatrabaho sa mga database ng NoSQL at walang pakialam sa lahat tungkol sa mga transaksyon at kanilang pagiging maaasahan. Tingnan natin kung tama sila.

Hindi ka maaaring makipag-usap sa pangkalahatan tungkol sa database ng NoSQL, dahil ito ay isang mahusay na abstraction. Ang mga database ng NoSQL ay naiiba sa bawat isa sa disenyo ng mga subsystem ng pag-iimbak ng data, at maging sa mga modelo ng data: Ang NoSQL ay parehong nakatuon sa dokumentong CouchDB at graph na Neo4J. Ngunit kung pag-uusapan natin ang mga ito sa konteksto ng mga transaksyon, lahat sila ay may posibilidad na magkatulad sa isang bagay: nagbibigay sila ng mga limitadong bersyon ng atomicity at paghihiwalay, at samakatuwid ay hindi nagbibigay ng mga garantiya ng ACID. Upang maunawaan kung ano ang ibig sabihin nito, sagutin natin ang tanong: ano ang inaalok nila, kung hindi ACID? Wala?

Hindi naman. Pagkatapos ng lahat, sila, tulad ng mga relational database, ay kailangan ding ibenta ang kanilang sarili sa isang magandang pakete. At nakabuo sila ng sarili nilang "chemical" abbreviation - BASE.

6.2 BASE bilang isang antagonist

At dito muli hindi ako pupunta sa pagkakasunud-sunod ng mga titik, ngunit magsisimula ako sa pangunahing termino - pagkakapare-pareho. Kakailanganin kong i-level ang iyong epekto sa pagkilala, dahil ang pagkakapare-pareho na ito ay walang gaanong kinalaman sa pagkakapare-pareho mula sa ACID. Ang problema sa terminong consistency ay ang paggamit nito sa napakaraming konteksto. Ngunit ang pagkakapare-parehong ito ay may mas malawak na konteksto ng paggamit, at sa katunayan ito ang eksaktong pagkakapare-pareho na tinatalakay kapag tinatalakay ang mga distributed system.

Ang mga relational database na napag-usapan natin sa itaas ay nagbibigay ng iba't ibang antas ng paghihiwalay ng transaksyon, at ang pinakamahigpit sa mga ito ay tinitiyak na ang isang transaksyon ay hindi makakakita ng mga di-wastong pagbabago na ginawa ng isa pang transaksyon. Kung nakatayo ka sa checkout sa isang tindahan, at sa sandaling iyon ang pera para sa upa ay na-withdraw mula sa iyong account, ngunit ang transaksyon sa paglipat ng pera para sa upa ay nabigo at ang iyong account ay bumalik sa dati nitong halaga (ang pera ay hindi na-debit), kung gayon ang iyong transaksyon sa pagbabayad sa checkout ay hindi mapapansin ng lahat ang mga galaw na ito - pagkatapos ng lahat, ang transaksyong iyon ay hindi kailanman dumaan, at batay sa kinakailangan ng paghihiwalay ng transaksyon, ang mga pansamantalang pagbabago nito ay hindi mapapansin ng iba pang mga transaksyon.

Maraming mga database ng NoSQL ang humiwalay sa garantiya sa paghihiwalay at nag-aalok ng "kapare-pareho sa wakas" kung saan makikita mo sa kalaunan ang wastong data, ngunit may pagkakataon na ang iyong transaksyon ay magbabasa ng mga di-wastong halaga - iyon ay, pansamantala, o bahagyang na-update, o luma na. Posibleng maging pare-pareho ang data sa "lazy" mode kapag nagbabasa ("lazily at read time").

Ang NoSQL ay naisip bilang isang database para sa real-time na analytics, at upang makamit ang higit na bilis, isinakripisyo nila ang pagkakapare-pareho. At si Eric Brewer, ang parehong tao na lumikha ng terminong BASE, ay bumalangkas ng tinatawag na "CAP theorem", ayon sa kung saan:

Para sa anumang pagpapatupad ng distributed computing, posibleng magbigay ng hindi hihigit sa dalawa sa sumusunod na tatlong katangian:

  • pagkakapare-pareho ng data ( consistency ) - ang data sa iba't ibang mga node (mga pagkakataon) ay hindi sumasalungat sa bawat isa;
  • availability ( availability ) - anumang kahilingan sa isang distributed system ay nagtatapos sa isang tamang tugon, ngunit walang garantiya na ang mga tugon ng lahat ng system node ay pareho;
  • partition tolerance (partition tolerance ) - Kahit na walang koneksyon sa pagitan ng mga node, patuloy silang gumagana nang hiwalay sa isa't isa.

Kung gusto mo ng napakasimpleng paliwanag ng CAP, pagkatapos ay narito ka.

May mga opinyon na ang CAP theorem ay hindi gumagana, at sa pangkalahatan ay formulated masyadong abstractly. Sa isang paraan o iba pa, ang mga database ng NoSQL ay madalas na tumatanggi sa pagkakapare-pareho sa konteksto ng CAP theorem, na naglalarawan sa sumusunod na sitwasyon: ang data ay na-update sa isang cluster na may ilang mga pagkakataon, ngunit ang mga pagbabago ay hindi pa naka-synchronize sa lahat ng mga pagkakataon. Tandaan, binanggit ko ang halimbawa ng DynamoDB sa itaas, na nagsabi sa akin: naging matibay ang iyong mga pagbabago - narito ang isang HTTP 200 para sa iyo - ngunit nakita ko lang ang mga pagbabago pagkatapos ng 10 segundo? Ang isa pang halimbawa mula sa pang-araw-araw na buhay ng isang developer ay DNS, ang domain name system. Kung sinuman ang hindi nakakaalam, ito ang eksaktong "diksyonaryo" na nagsasalin ng mga http (mga) address sa mga IP address.

Ang na-update na tala ng DNS ay pinalaganap sa mga server ayon sa mga setting ng agwat ng pag-cache - kaya hindi agad napapansin ang mga update. Well, ang isang katulad na temporal na hindi pagkakapare-pareho (ibig sabihin, sa kalaunan ay pagkakapare-pareho) ay maaaring mangyari sa isang relational database cluster (sabihin, MySQL) - pagkatapos ng lahat, ang pagkakapare-pareho na ito ay walang kinalaman sa pagkakapare-pareho mula sa ACID. Samakatuwid, mahalagang maunawaan na sa ganitong kahulugan, ang mga database ng SQL at NoSQL ay malamang na hindi magkaiba pagdating sa ilang mga pagkakataon sa isang kumpol.

Bilang karagdagan, ang end-to-end na pagkakapare-pareho ay maaaring mangahulugan na ang mga kahilingan sa pagsulat ay gagawin nang wala sa pagkakasunud-sunod: ibig sabihin, ang lahat ng data ay isusulat, ngunit ang halaga na kalaunan ay matatanggap ay hindi ang huling isa sa write queue. .

Ang mga non-ACID na database ng NoSQL ay may tinatawag na "soft state" dahil sa end-to-end consistency model, na nangangahulugan na ang estado ng system ay maaaring magbago sa paglipas ng panahon, kahit na walang input. Ngunit ang mga ganitong sistema ay nagsusumikap na magbigay ng higit na accessibility. Ang pagbibigay ng 100% availability ay hindi isang maliit na gawain, kaya ang pinag-uusapan natin ay tungkol sa "basic availability". At magkasama ang tatlong konseptong ito: "basically available", "soft state" (“soft state”) at “eventual consistency” ang bumubuo sa acronym na BASE.

Upang maging matapat, ang konsepto ng BASE ay tila sa akin ay isang mas walang laman na marketing wrapper kaysa sa ACID - dahil hindi ito nagbibigay ng bago at hindi nagpapakilala sa database sa anumang paraan. At ang pag-attach ng mga label (ACID, BASE, CAP) sa ilang partikular na database ay maaari lamang malito sa mga developer. Napagpasyahan kong ipakilala pa rin sa iyo ang terminong ito, dahil mahirap i-bypass ito kapag pinag-aaralan ang database, ngunit ngayong alam mo na kung ano ito, gusto kong kalimutan mo ito sa lalong madaling panahon. At bumalik tayo sa konsepto ng paghihiwalay.

6.3 Kaya ang mga database ng BASE ay hindi nakakatugon sa pamantayan ng ACID?

Sa esensya, kung saan ang mga database ng ACID ay naiiba sa mga hindi ACID ay ang mga hindi ACID ay talagang tinatalikuran ang paghihiwalay. Ito ay mahalagang maunawaan. Ngunit mas mahalaga na basahin ang dokumentasyon ng database at subukan ang mga ito sa paraang ginagawa ng mga lalaki mula sa proyekto ng Hermitage. Hindi gaanong mahalaga kung paano eksaktong tinatawag ng mga tagalikha nito o ng database na iyon ang kanilang ideya - ACID o BASE, CAP o hindi CAP. Ang mahalagang bagay ay kung ano ang eksaktong ibinibigay nito o ang database na iyon.

Kung inaangkin ng mga tagalikha ng database na nagbibigay ito ng mga garantiya ng ACID, malamang na may dahilan para dito, ngunit ipinapayong subukan ito sa iyong sarili upang maunawaan kung ito ay totoo at hanggang saan. Kung idineklara nila na ang kanilang database ay hindi nagbibigay ng ganoong mga garantiya, kung gayon ito ay maaaring mangahulugan ng mga sumusunod na bagay:

  • Ang DB ay hindi nagbibigay ng garantiya ng atomicity. Habang ang ilang mga database ng NoSQL ay nag-aalok ng isang hiwalay na API para sa atomic operations (hal. DynamoDB);

  • Ang DB ay hindi nagbibigay ng garantiya sa paghihiwalay. Maaaring mangahulugan ito, halimbawa, na hindi isusulat ng database ang data sa pagkakasunud-sunod kung saan isinulat ang mga ito.

Tulad ng para sa garantiya ng tibay, maraming mga database ang nakompromiso sa puntong ito para sa kapakanan ng pagganap. Ang pagsusulat sa disk ay masyadong mahaba ang operasyon, at may ilang mga paraan upang malutas ang problemang ito. Hindi ko nais na pumunta sa teorya ng database, ngunit upang maunawaan mo kung aling paraan ang titingnan, ilalarawan ko sa mga pangkalahatang tuntunin kung paano malulutas ng iba't ibang mga database ang problema sa tibay.

Upang ihambing ang iba't ibang mga database, bukod sa iba pang mga bagay, kailangan mong malaman kung anong mga istruktura ng data ang sumasailalim sa imbakan ng data at subsystem ng pagkuha ng isang partikular na database. Sa madaling salita: ang iba't ibang mga database ay may iba't ibang mga pagpapatupad ng pag-index - iyon ay, pag-aayos ng access sa data. Ang ilan sa mga ito ay nagpapahintulot sa iyo na magsulat ng data nang mas mabilis, ang iba - mas mabilis na basahin ito. Ngunit hindi masasabi sa pangkalahatan na ang ilang mga istruktura ng data ay ginagawang mas mataas o mas mababa ang tibay.

6.4 kung paano ini-index ng iba't ibang mga database ang data, at kung paano ito nakakaapekto sa tibay, at higit pa

Mayroong dalawang pangunahing diskarte sa pag-iimbak at pagkuha ng data.

Ang pinakamadaling paraan upang mag-save ng data ay ang magdagdag ng mga operasyon sa dulo ng file sa isang log-like na paraan (iyon ay, isang append operation ay palaging nangyayari): hindi mahalaga kung gusto naming magdagdag, magbago o magtanggal ng data - lahat Ang mga operasyon ng CRUD ay nakasulat lamang sa log. Ang paghahanap sa log ay hindi epektibo, at doon pumapasok ang index - isang espesyal na istraktura ng data na nag-iimbak ng metadata tungkol sa eksaktong kung saan naka-imbak ang data. Ang pinakasimpleng diskarte sa pag-index para sa mga log ay isang hash map na sumusubaybay sa mga key at value. Ang mga halaga ay magiging mga sanggunian sa byte offset para sa data na nakasulat sa loob ng file, na siyang log (log) at naka-imbak sa disk. Ang istraktura ng data na ito ay ganap na nakaimbak sa memorya, habang ang data mismo ay nasa disk, at tinatawag na LSM tree (log structured merge).

Marahil ay nagtaka ka: kung isusulat namin ang aming mga operasyon sa journal sa lahat ng oras, kung gayon ito ay lalago nang labis? Oo, at samakatuwid ang compaction technique ay naimbento, na "naglilinis" ng data na may ilang periodicity, ibig sabihin, nag-iiwan lamang ng pinaka-nauugnay na halaga para sa bawat key, o tinatanggal ito. At kung mayroon kaming higit sa isang log sa disk, ngunit marami, at lahat sila ay pinagsunod-sunod, pagkatapos ay makakakuha kami ng isang bagong istraktura ng data na tinatawag na SSTable ("pinagsunod-sunod na talahanayan ng string"), at ito ay walang alinlangan na mapapabuti ang aming pagganap. Kung nais nating pag-uri-uriin sa memorya, makakakuha tayo ng isang katulad na istraktura - ang tinatawag na MemTable, ngunit kasama nito ang problema ay kung ang isang nakamamatay na pag-crash ng database ay nangyari, pagkatapos ay ang data na nakasulat sa huli (na matatagpuan sa MemTable, ngunit hindi pa nakasulat sa disk) ay nawala . sa totoo lang,

Ang isa pang diskarte sa pag-index ay batay sa B-trees (“B-trees”). Sa isang B-tree, ang data ay isinusulat sa disk sa nakapirming laki ng mga pahina. Ang mga bloke ng data na ito ay kadalasang humigit-kumulang 4 KB ang laki at may mga pares ng key-value na pinagsunod-sunod ayon sa key. Ang isang B-tree node ay parang array na may mga link sa hanay ng mga page. Max. ang bilang ng mga link sa isang array ay tinatawag na branch factor. Ang bawat hanay ng pahina ay isa pang B-tree node na may mga link sa iba pang hanay ng pahina.

Sa kalaunan, sa antas ng sheet, makikita mo ang mga indibidwal na pahina. Ang ideyang ito ay katulad ng mga pointer sa mababang antas ng mga programming language, maliban na ang mga page reference na ito ay nakaimbak sa disk sa halip na sa memorya. Kapag nangyari ang mga INSERT at DELETE sa database, maaaring hatiin ang ilang node sa dalawang subtree upang tumugma sa branching factor. Kung nabigo ang database sa anumang kadahilanan sa gitna ng proseso, maaaring makompromiso ang integridad ng data. Upang maiwasang mangyari ito, ang mga database na gumagamit ng B-trees ay nagpapanatili ng "write-ahead log", o WAL, kung saan ang bawat solong transaksyon ay naitala. Ang WAL na ito ay ginagamit upang maibalik ang estado ng B-tree kung ito ay nasira. At tila ito ang dahilan kung bakit mas mahusay ang mga database gamit ang B-trees sa mga tuntunin ng tibay. Ngunit ang mga database na nakabase sa LSM ay maaari ding magpanatili ng isang file na mahalagang gumaganap ng parehong function bilang WAL. Samakatuwid, uulitin ko ang nasabi ko na, at marahil higit sa isang beses: unawain ang mga mekanismo ng pagpapatakbo ng database na iyong pinili.

Ano ang tiyak tungkol sa mga B-tree, gayunpaman, ay ang mga ito ay mabuti para sa transactionality: ang bawat key ay nangyayari sa isang lugar lamang sa index, habang ang mga naka-journal na storage subsystem ay maaaring magkaroon ng maraming kopya ng parehong key sa iba't ibang shards (halimbawa, hanggang sa isasagawa ang susunod na compaction).

Gayunpaman, ang disenyo ng index ay direktang nakakaapekto sa pagganap ng database. Sa isang LSM tree, ang mga pagsusulat sa disk ay sunud-sunod, at ang mga B-tree ay nagdudulot ng maraming random na pag-access sa disk, kaya ang mga operasyon sa pagsulat ay mas mabilis sa LSM kaysa sa mga B-tree. Ang pagkakaiba ay lalong makabuluhan para sa magnetic hard disk drive (HDDs), kung saan ang sequential writes ay mas mabilis kaysa sa random writes. Mas mabagal ang pagbabasa sa mga puno ng LSM dahil kailangan mong tumingin sa iba't ibang istruktura ng data at mga talahanayan ng SS na nasa iba't ibang yugto ng compaction. Sa mas detalyado, ganito ang hitsura nito. Kung gagawa tayo ng simpleng query sa database gamit ang LSM, hahanapin muna natin ang key sa MemTable. Kung wala ito, tinitingnan namin ang pinakabagong SSTable; kung wala doon, pagkatapos ay titingnan natin ang penultimate SSTable, at iba pa. Kung ang hiniling na susi ay hindi umiiral, pagkatapos ay sa LSM malalaman natin ito sa huli. Ang mga puno ng LSM ay ginagamit sa, halimbawa: LevelDB, RocksDB, Cassandra at HBase.

Inilalarawan ko ang lahat ng ito sa ganoong detalye upang maunawaan mo na kapag pumipili ng isang database, kailangan mong isaalang-alang ang maraming iba't ibang mga bagay: halimbawa, inaasahan mo bang magsulat o magbasa ng data nang higit pa. At hindi ko pa nabanggit ang pagkakaiba sa mga modelo ng data (kailangan mo bang i-traverse ang data, gaya ng pinapayagan ng modelo ng graph? Mayroon bang anumang mga ugnayan sa pagitan ng iba't ibang mga yunit sa iyong data - pagkatapos ay ang mga relational database ay darating upang iligtas?), at 2 uri ng mga schemas ng data - kapag nagsusulat (tulad ng sa maraming NoSQL) at nagbabasa (tulad ng sa relational).

Kung babalik tayo sa aspeto ng tibay, kung gayon ang konklusyon ay ang mga sumusunod: anumang database na nagsusulat sa disk, anuman ang mga mekanismo ng pag-index, ay maaaring magbigay ng magandang garantiya para sa tibay ng iyong data, ngunit kailangan mong harapin ang bawat partikular na database , kung ano ang eksaktong inaalok nito.

6.5 Paano gumagana ang mga in-memory na DB

Sa pamamagitan ng paraan, bilang karagdagan sa mga database na sumulat sa disk, mayroon ding mga tinatawag na "in-memory" na mga database na pangunahing gumagana sa RAM. Sa madaling salita, ang mga in-memory na database ay karaniwang nag-aalok ng mas mababang tibay para sa mas mabilis na bilis ng pagsulat at pagbasa, ngunit maaaring ito ay angkop para sa ilang mga application.

Ang katotohanan ay ang memorya ng RAM ay matagal nang mas mahal kaysa sa mga disk, ngunit kamakailan lamang ay nagsimula itong mabilis na maging mas mura, na nagdulot ng isang bagong uri ng database - na lohikal, dahil sa bilis ng pagbabasa at pagsulat ng data mula sa RAM. Ngunit tama ang itatanong mo: ano ang tungkol sa kaligtasan ng data ng mga database na ito? Dito muli, kailangan mong tingnan ang mga detalye ng pagpapatupad. Sa pangkalahatan, ang mga developer ng naturang mga database ay nag-aalok ng mga sumusunod na mekanismo:

  • Maaari mong gamitin ang RAM na pinapagana ng mga baterya;
  • Posibleng magsulat ng mga change log sa disk (tulad ng mga WAL na nabanggit sa itaas), ngunit hindi ang data mismo;
  • Maaari kang pana-panahong magsulat ng mga kopya ng estado ng database sa disk (na, nang hindi gumagamit ng iba pang mga opsyon, ay hindi nagbibigay ng garantiya, ngunit nagpapabuti lamang ng tibay);
  • Maaari mong kopyahin ang estado ng RAM sa iba pang mga makina.

Halimbawa, ang in-memory na Redis database, na pangunahing ginagamit bilang isang message queue o cache, ay walang tibay mula sa ACID: hindi nito ginagarantiyahan na ang isang matagumpay na naisakatuparan na command ay maiimbak sa disk, dahil ang Redis ay nag-flush ng data sa disk (kung ikaw ay pinagana ang pagtitiyaga) nang hindi magkakasabay, sa mga regular na pagitan.

Gayunpaman, hindi ito kritikal para sa lahat ng application: Nakakita ako ng halimbawa ng EtherPad cooperative online editor, na nag-flush tuwing 1-2 segundo, at posibleng mawalan ng ilang titik o isang salita ang user, na halos hindi kritikal. Kung hindi man, dahil ang mga in-memory na database ay mahusay dahil nagbibigay sila ng mga modelo ng data na mahirap ipatupad sa mga disk index, maaaring gamitin ang Redis upang ipatupad ang mga transaksyon - ang priority queue nito ay nagpapahintulot sa iyo na gawin ito.