Higit pang mga developer ang dapat malaman ito tungkol sa mga database

Tandaan. transl.: Si Jaana Dogan ay isang bihasang inhinyero sa Google na kasalukuyang nagtatrabaho sa obserbasyon ng mga serbisyo ng produksyon ng kumpanya na nakasulat sa Go. Sa artikulong ito, na nakakuha ng mahusay na katanyagan sa mga nagsasalita ng Ingles na madla, nakolekta niya sa 17 puntos ang mahahalagang teknikal na detalye tungkol sa mga DBMS (at kung minsan ay ipinamamahaging mga system sa pangkalahatan) na kapaki-pakinabang na isaalang-alang para sa mga developer ng malalaking/demanding application.

Higit pang mga developer ang dapat malaman ito tungkol sa mga database

Sinusubaybayan ng karamihan ng mga computer system ang kanilang estado at, nang naaayon, nangangailangan ng ilang uri ng data storage system. Nakaipon ako ng kaalaman tungkol sa mga database sa loob ng mahabang panahon, habang gumagawa ako ng mga pagkakamali sa disenyo na humantong sa pagkawala at pagkawala ng data. Sa mga system na nagpoproseso ng malalaking volume ng impormasyon, ang mga database ay nasa gitna ng arkitektura ng system at nagsisilbing pangunahing elemento sa pagpili ng pinakamainam na solusyon. Sa kabila ng katotohanan na ang malapit na pansin ay binabayaran sa gawain ng database, ang mga problema na sinusubukan ng mga developer ng application na asahan ay kadalasang dulo lamang ng malaking bato ng yelo. Sa seryeng ito ng mga artikulo, nagbabahagi ako ng ilang ideya na magiging kapaki-pakinabang para sa mga developer na hindi dalubhasa sa larangang ito.

  1. Maswerte ka kung 99,999% ng oras na ang network ay hindi nagdudulot ng mga problema.
  2. Ang ACID ay nangangahulugang maraming iba't ibang bagay.
  3. Ang bawat database ay may sariling mga mekanismo para sa pagtiyak ng pare-pareho at paghihiwalay.
  4. Ang optimistikong pagharang ay sumasagip kapag mahirap panatilihin ang karaniwan.
  5. Mayroong iba pang mga anomalya bukod sa maruming pagbabasa at pagkawala ng data.
  6. Ang database at ang user ay hindi palaging magkasundo sa kurso ng aksyon.
  7. Maaaring ilipat ang sharding sa antas ng aplikasyon sa labas ng application.
  8. Maaaring mapanganib ang autoincrementing.
  9. Maaaring maging kapaki-pakinabang ang lipas na data at hindi kailangang i-lock.
  10. Ang mga pagbaluktot ay karaniwan para sa anumang pinagmumulan ng oras.
  11. Ang pagkaantala ay maraming kahulugan.
  12. Dapat suriin ang mga kinakailangan sa pagganap para sa isang partikular na transaksyon.
  13. Maaaring mapanganib ang mga nested na transaksyon.
  14. Ang mga transaksyon ay hindi dapat iugnay sa estado ng aplikasyon.
  15. Maraming masasabi sa iyo ang mga tagaplano ng query tungkol sa mga database.
  16. Ang online migration ay mahirap, ngunit posible.
  17. Ang isang makabuluhang pagtaas sa database ay nangangailangan ng pagtaas sa hindi mahuhulaan.

Gusto kong pasalamatan sina Emmanuel Odeke, Rein Henrichs at iba pa para sa kanilang puna sa isang mas naunang bersyon ng artikulong ito.

Maswerte ka kung 99,999% ng oras na ang network ay hindi nagdudulot ng mga problema.

Ang tanong ay nananatili tungkol sa kung gaano maaasahan ang mga modernong teknolohiya ng network at kung gaano kadalas ang mga system ay bumaba dahil sa mga pagkabigo sa network. Ang impormasyon sa isyung ito ay kakaunti at ang pananaliksik ay madalas na pinangungunahan ng malalaking organisasyon na may mga espesyal na network, kagamitan at tauhan.

Sa availability rate na 99,999% para sa Spanner (ang database na ipinamamahagi sa buong mundo ng Google), sinasabi ng Google na 7,6% ang mga problema ay nauugnay sa network. Kasabay nito, tinawag ng kumpanya ang dalubhasang network nito bilang "pangunahing haligi" ng mataas na kakayahang magamit. Mag-aral Bailis at Kingsbury, na isinagawa noong 2014, hinahamon ang isa sa "maling kuru-kuro tungkol sa distributed computing", na binuo ni Peter Deutsch noong 1994. Maaasahan ba talaga ang network?

Ang komprehensibong pananaliksik sa labas ng mga higanteng kumpanya, na isinagawa para sa mas malawak na Internet, ay hindi umiiral. Wala ring sapat na data mula sa mga pangunahing manlalaro tungkol sa kung ilang porsyento ng mga problema ng kanilang mga customer ang nauugnay sa network. Alam na alam namin ang mga outage sa network stack ng malalaking cloud provider na maaaring magtanggal ng isang buong bahagi ng Internet sa loob ng ilang oras dahil lang sa mga high-profile na kaganapan ang mga ito na nakakaapekto sa malaking bilang ng mga tao at kumpanya. Ang mga pagkawala ng network ay maaaring magdulot ng mga problema sa marami pang mga kaso, kahit na hindi lahat ng mga kasong iyon ay nasa spotlight. Ang mga kliyente ng mga serbisyo sa ulap ay hindi rin alam ang anumang bagay tungkol sa mga sanhi ng mga problema. Kung may kabiguan, halos imposibleng maiugnay ito sa isang error sa network sa panig ng service provider. Para sa kanila, ang mga serbisyo ng third-party ay mga black box. Imposibleng masuri ang epekto nang hindi isang malaking service provider.

Dahil sa kung ano ang iniulat ng malalaking manlalaro tungkol sa kanilang mga system, ligtas na sabihin na ikaw ay swerte kung ang mga paghihirap sa network ay tumutukoy lamang sa maliit na porsyento ng mga potensyal na isyu sa downtime. Ang mga komunikasyon sa network ay nagdurusa pa rin sa mga makamundong bagay tulad ng mga pagkabigo ng hardware, mga pagbabago sa topology, mga pagbabago sa configuration ng administratibo, at pagkawala ng kuryente. Kamakailan, nagulat ako nang malaman na ang listahan ng mga posibleng problema ay idinagdag kagat ng pating (oo, tama ang narinig mo).

Ang ibig sabihin ng ACID ay maraming iba't ibang bagay

Ang acronym na ACID ay kumakatawan sa Atomicity, Consistency, Isolation, Reliability. Ang mga katangiang ito ng mga transaksyon ay nilayon upang matiyak ang kanilang bisa sa kaganapan ng mga pagkabigo, mga error, mga pagkabigo sa hardware, atbp. Kung walang ACID o katulad na mga scheme, magiging mahirap para sa mga developer ng application na pag-iba-ibahin kung ano ang kanilang pananagutan at kung ano ang responsable para sa database. Karamihan sa mga relational transactional database ay sinusubukang maging ACID compliant, ngunit ang mga bagong approach tulad ng NoSQL ay nagbunga ng maraming database na walang ACID transactions dahil mahal ang mga ito sa pagpapatupad.

Noong una akong pumasok sa industriya, napag-usapan ng aming teknikal na lead kung gaano kaugnay ang konsepto ng ACID. Upang maging patas, ang ACID ay itinuturing na isang magaspang na paglalarawan sa halip na isang mahigpit na pamantayan sa pagpapatupad. Sa ngayon, higit na kapaki-pakinabang ito dahil naglalabas ito ng partikular na kategorya ng mga isyu (at nagmumungkahi ng hanay ng mga posibleng solusyon).

Hindi lahat ng DBMS ay sumusunod sa ACID; Kasabay nito, ang mga pagpapatupad ng database na sumusuporta sa ACID ay nakakaunawa sa hanay ng mga kinakailangan sa ibang paraan. Isa sa mga dahilan kung bakit tagpi-tagpi ang mga pagpapatupad ng ACID ay dahil sa maraming trade-off na kailangang gawin upang ipatupad ang mga kinakailangan ng ACID. Maaaring ipakita ng mga creator ang kanilang mga database bilang ACID-compliant, ngunit ang interpretasyon ng mga edge case ay maaaring mag-iba nang malaki, gayundin ang mekanismo para sa paghawak ng "malamang" na mga kaganapan. Hindi bababa sa, ang mga developer ay maaaring makakuha ng isang mataas na antas ng pag-unawa sa mga intricacies ng mga batayang pagpapatupad upang makakuha ng isang wastong pag-unawa sa kanilang mga espesyal na gawi at disenyo trade-off.

Ang debate tungkol sa kung ang MongoDB ay sumusunod sa mga kinakailangan ng ACID ay nagpapatuloy kahit na pagkatapos ng paglabas ng bersyon 4. Matagal nang hindi sinusuportahan ang MongoDB pagtotroso, bagama't bilang default, ang data ay nakatuon sa disk nang hindi hihigit sa isang beses bawat 60 segundo. Isipin ang sumusunod na senaryo: ang isang aplikasyon ay nag-post ng dalawang sulat (w1 at w2). Matagumpay na naiimbak ng MongoDB ang w1, ngunit nawala ang w2 dahil sa pagkabigo ng hardware.

Higit pang mga developer ang dapat malaman ito tungkol sa mga database
Diagram na naglalarawan ng senaryo. Nag-crash ang MongoDB bago ito makapagsulat ng data sa disk

Ang pag-commit sa disk ay isang mamahaling proseso. Sa pamamagitan ng pag-iwas sa madalas na pag-commit, pinapahusay ng mga developer ang pagganap ng pag-record sa kapinsalaan ng pagiging maaasahan. Kasalukuyang sinusuportahan ng MongoDB ang pag-log, ngunit ang maruruming pagsusulat ay maaari pa ring makaapekto sa integridad ng data dahil ang mga log ay nakukuha tuwing 100ms bilang default. Iyon ay, ang isang katulad na senaryo ay posible pa rin para sa mga log at ang mga pagbabago na ipinakita sa kanila, kahit na ang panganib ay mas mababa.

Ang bawat database ay may sariling pagkakapare-pareho at mga mekanismo ng paghihiwalay

Sa mga kinakailangan sa ACID, ipinagmamalaki ng consistency at isolation ang pinakamalaking bilang ng iba't ibang pagpapatupad dahil mas malawak ang hanay ng mga trade-off. Dapat sabihin na ang pagkakapare-pareho at paghihiwalay ay medyo mahal na mga pag-andar. Nangangailangan sila ng koordinasyon at pagtaas ng kumpetisyon para sa pagkakapare-pareho ng data. Ang pagiging kumplikado ng problema ay tumataas nang malaki kapag kinakailangan upang sukatin ang database nang pahalang sa maraming data center (lalo na kung sila ay matatagpuan sa iba't ibang heyograpikong rehiyon). Ang pagkamit ng mataas na antas ng pagkakapare-pareho ay napakahirap, dahil binabawasan din nito ang kakayahang magamit at pinatataas ang segmentasyon ng network. Para sa isang mas pangkalahatang paliwanag ng hindi pangkaraniwang bagay na ito, ipinapayo ko sa iyo na sumangguni sa CAP theorem. Nararapat ding tandaan na ang mga application ay maaaring pangasiwaan ang maliit na halaga ng hindi pagkakapare-pareho, at ang mga programmer ay maaaring maunawaan ang mga nuances ng problema nang sapat upang ipatupad ang karagdagang lohika sa application upang mahawakan ang hindi pagkakapare-pareho nang hindi umaasa nang husto sa database upang mahawakan ito.

Ang mga DBMS ay kadalasang nagbibigay ng iba't ibang antas ng paghihiwalay. Maaaring piliin ng mga developer ng application ang pinakaepektibo batay sa kanilang mga kagustuhan. Ang mababang paghihiwalay ay nagbibigay-daan para sa pagtaas ng bilis, ngunit pinapataas din ang panganib ng isang lahi ng data. Ang mataas na pagkakabukod ay binabawasan ang posibilidad na ito, ngunit nagpapabagal sa trabaho at maaaring humantong sa kumpetisyon, na hahantong sa mga naturang preno sa base na magsisimula ang mga pagkabigo.

Higit pang mga developer ang dapat malaman ito tungkol sa mga database
Suriin ang mga kasalukuyang modelo ng concurrency at mga relasyon sa pagitan ng mga ito

Ang pamantayan ng SQL ay tumutukoy lamang sa apat na antas ng paghihiwalay, bagaman sa teorya at pagsasanay ay marami pa. Jepson.io nag-aalok ng mahusay na pangkalahatang-ideya ng mga kasalukuyang modelo ng concurrency. Halimbawa, ginagarantiyahan ng Google Spanner ang external na serializability sa pag-synchronize ng orasan, at bagama't isa itong mas mahigpit na layer ng paghihiwalay, hindi ito tinukoy sa mga karaniwang layer ng paghihiwalay.

Binabanggit ng pamantayan ng SQL ang mga sumusunod na antas ng paghihiwalay:

  • Serializable (pinaka mahigpit at mahal): Ang serializable execution ay may parehong epekto gaya ng ilang sequential transaction execution. Ang sequential execution ay nangangahulugan na ang bawat kasunod na transaksyon ay magsisimula lamang pagkatapos makumpleto ang nauna. Dapat tandaan na ang antas Serializable madalas na ipinapatupad bilang tinatawag na snapshot isolation (halimbawa, sa Oracle) dahil sa mga pagkakaiba sa interpretasyon, bagaman ang snapshot isolation mismo ay hindi kinakatawan sa SQL standard.
  • Mga paulit-ulit na pagbabasa: Ang mga hindi nakasaad na tala sa kasalukuyang transaksyon ay magagamit sa kasalukuyang transaksyon, ngunit ang mga pagbabagong ginawa ng iba pang mga transaksyon (tulad ng mga bagong hilera) hindi nakikita.
  • Read committed: Hindi available ang uncommitted data para sa mga transaksyon. Sa kasong ito, makikita lang ng mga transaksyon ang naka-commit na data, at maaaring mangyari ang mga phantom reads. Kung ang isang transaksyon ay maglalagay at gumawa ng mga bagong row, ang kasalukuyang transaksyon ay makikita ang mga ito kapag na-query.
  • Basahin nang hindi nakatuon (hindi bababa sa mahigpit at mahal na antas): Pinahihintulutan ang mga maruruming pagbabasa, makikita ng mga transaksyon ang mga hindi ginawang pagbabago na ginawa ng ibang mga transaksyon. Sa pagsasagawa, ang antas na ito ay maaaring maging kapaki-pakinabang para sa mga magaspang na pagtatantya, gaya ng mga query COUNT(*) sa mesa.

Π£Ρ€ΠΎΠ²Π΅Π½ΡŒ Serializable pinapaliit ang panganib ng mga karera ng data, habang ito ang pinakamahal na ipatupad at nagreresulta sa pinakamataas na mapagkumpitensyang pagkarga sa system. Ang iba pang mga antas ng paghihiwalay ay mas madaling ipatupad, ngunit pinapataas ang posibilidad ng mga karera ng data. Binibigyang-daan ka ng ilang DBMS na magtakda ng pasadyang antas ng paghihiwalay, ang iba ay may malakas na kagustuhan at hindi lahat ng antas ay sinusuportahan.

Ang suporta para sa mga antas ng paghihiwalay ay madalas na ina-advertise sa isang partikular na DBMS, ngunit ang maingat na pag-aaral lamang ng pag-uugali nito ang makakapagpakita kung ano ang aktwal na nangyayari.

Higit pang mga developer ang dapat malaman ito tungkol sa mga database
Suriin ang mga concurrency na anomalya sa iba't ibang antas ng paghihiwalay para sa iba't ibang DBMS

Martin Kleppmann sa kanyang proyekto ermitanyo Naghahambing ng iba't ibang antas ng paghihiwalay, pinag-uusapan ang mga anomalya ng pagkakatugma, at kung ang database ay nakakasunod sa isang partikular na antas ng paghihiwalay. Ipinapakita ng pananaliksik ni Kleppmann kung paano naiiba ang iniisip ng mga developer ng database tungkol sa mga antas ng paghihiwalay.

Ang optimistikong pagharang ay sumasagip kapag mahirap panatilihin ang karaniwan.

Ang pagharang ay maaaring maging napakamahal, hindi lamang dahil pinapataas nito ang kumpetisyon sa database, kundi dahil nangangailangan din ito ng mga server ng application na patuloy na kumonekta sa database. Maaaring palalain ng pagse-segment ng network ang mga eksklusibong sitwasyon ng pag-lock at humantong sa mga deadlock na mahirap tukuyin at lutasin. Sa mga kaso kung saan hindi angkop ang eksklusibong pag-lock, nakakatulong ang optimistikong pag-lock.

Optimistic na lock ay isang paraan kung saan kapag nagbabasa ng isang string, isinasaalang-alang nito ang bersyon, checksum, o oras ng huling pagbabago. Binibigyang-daan ka nitong matiyak na walang pagbabago sa bersyon ng atomic bago baguhin ang isang entry:

UPDATE products
SET name = 'Telegraph receiver', version = 2
WHERE id = 1 AND version = 1

Sa kasong ito, ang pag-update ng talahanayan products ay hindi isasagawa kung ang isa pang operasyon ay naunang gumawa ng mga pagbabago sa row na ito. Kung walang ibang operasyon na isinagawa sa row na ito, magaganap ang pagbabago para sa isang row at masasabi nating matagumpay ang pag-update.

Mayroong iba pang mga anomalya bukod sa maruming pagbabasa at pagkawala ng data

Pagdating sa pagkakapare-pareho ng data, ang focus ay sa potensyal para sa mga kondisyon ng lahi na maaaring humantong sa maruming pagbabasa at pagkawala ng data. Gayunpaman, ang mga anomalya ng data ay hindi titigil doon.

Ang isang halimbawa ng gayong mga anomalya ay ang pagbaluktot sa pagtatala (sumulat ng mga skews). Mahirap matukoy ang mga pagbaluktot dahil karaniwang hindi sila aktibong hinahanap. Ang mga ito ay hindi dahil sa maruming pagbabasa o pagkawala ng data, ngunit sa mga paglabag sa lohikal na mga hadlang na inilagay sa data.

Halimbawa, isaalang-alang natin ang isang application sa pagsubaybay na nangangailangan ng isang operator na on-call sa lahat ng oras:

BEGIN tx1;                      BEGIN tx2;
SELECT COUNT(*)
FROM operators
WHERE oncall = true;
0                               SELECT COUNT(*)
                                FROM operators
                                WHERE oncall = TRUE;
                                0
UPDATE operators                UPDATE operators
SET oncall = TRUE               SET oncall = TRUE
WHERE userId = 4;               WHERE userId = 2;
COMMIT tx1;                     COMMIT tx2;

Sa sitwasyon sa itaas, ang isang record na katiwalian ay magaganap kung ang parehong mga transaksyon ay matagumpay na ginawa. Bagama't walang maruming nabasa o pagkawala ng data, nakompromiso ang integridad ng data: ngayon, dalawang tao ang itinuturing na on-call sa parehong oras.

Makakatulong ang serializable na paghihiwalay, disenyo ng schema, o database ng mga hadlang upang maalis ang katiwalian sa pagsulat. Dapat na matukoy ng mga developer ang mga naturang anomalya sa panahon ng pag-unlad upang maiwasan ang mga ito sa produksyon. Kasabay nito, ang pag-record ng mga pagbaluktot ay napakahirap hanapin sa base ng code. Lalo na sa malalaking sistema, kapag ang iba't ibang mga development team ay may pananagutan sa pagpapatupad ng mga function batay sa parehong mga talahanayan at hindi sumasang-ayon sa mga detalye ng pag-access ng data.

Ang database at ang user ay hindi palaging magkasundo sa kung ano ang gagawin

Ang isa sa mga pangunahing tampok ng mga database ay ang garantiya ng execution order, ngunit ang order na ito mismo ay maaaring hindi transparent sa software developer. Ang mga database ay nagsasagawa ng mga transaksyon sa pagkakasunud-sunod na natanggap ang mga ito, hindi sa pagkakasunud-sunod na nilayon ng mga programmer. Ang pagkakasunud-sunod ng mga transaksyon ay mahirap hulaan, lalo na sa mataas na load parallel system.

Sa panahon ng pag-unlad, lalo na kapag nagtatrabaho sa mga hindi naka-block na mga aklatan, ang hindi magandang istilo at mababang pagiging madaling mabasa ay maaaring maging sanhi ng mga user na maniwala na ang mga transaksyon ay isinasagawa nang sunud-sunod, ngunit sa katunayan maaari silang dumating sa database sa anumang pagkakasunud-sunod.

Sa unang sulyap, sa programa sa ibaba, ang T1 at T2 ay tinatawag na sunud-sunod, ngunit kung ang mga function na ito ay hindi naka-block at agad na ibabalik ang resulta sa form pangako, pagkatapos ay ang pagkakasunud-sunod ng mga tawag ay matutukoy sa pamamagitan ng mga sandali kapag sila ay pumasok sa database:

resulta1 = T1() // ang mga tunay na resulta ay mga pangako
resulta2 = T2()

Kung ang atomicity ay kinakailangan (iyon ay, alinman sa lahat ng mga operasyon ay dapat makumpleto o i-abort) at pagkakasunud-sunod ng mga bagay, pagkatapos ay ang mga operasyon T1 at T2 ay dapat na isagawa sa loob ng iisang transaksyon.

Maaaring ilipat ang sharding sa antas ng aplikasyon sa labas ng application

Ang Sharding ay isang paraan ng pahalang na paghahati ng database. Ang ilang mga database ay maaaring awtomatikong hatiin ang data nang pahalang, habang ang iba ay hindi, o hindi masyadong mahusay dito. Kapag ang mga arkitekto/developer ng data ay nahulaan nang eksakto kung paano maa-access ang data, maaari silang lumikha ng mga pahalang na partisyon sa espasyo ng user sa halip na italaga ang gawaing ito sa database. Ang prosesong ito ay tinatawag na "application-level sharding" (sharding sa antas ng aplikasyon).

Sa kasamaang palad, ang pangalang ito ay madalas na lumilikha ng maling kuru-kuro na ang sharding ay nabubuhay sa mga serbisyo ng aplikasyon. Sa katunayan, maaari itong ipatupad bilang isang hiwalay na layer sa harap ng database. Depende sa paglaki ng data at mga pag-ulit ng schema, maaaring maging kumplikado ang mga kinakailangan sa sharding. Ang ilang mga diskarte ay maaaring makinabang mula sa kakayahang umulit nang hindi kinakailangang muling i-deploy ang mga server ng application.

Higit pang mga developer ang dapat malaman ito tungkol sa mga database
Isang halimbawa ng isang arkitektura kung saan ang mga server ng application ay pinaghihiwalay mula sa serbisyo ng sharding

Ang paglipat ng sharding sa isang hiwalay na serbisyo ay nagpapalawak ng kakayahang gumamit ng iba't ibang mga diskarte sa sharding nang hindi nangangailangan ng muling pag-deploy ng mga application. Vitess ay isang halimbawa ng naturang sharding system sa antas ng aplikasyon. Nagbibigay ang Vitess ng pahalang na sharding para sa MySQL at pinapayagan ang mga kliyente na kumonekta dito sa pamamagitan ng MySQL protocol. Hinahati ng system ang data sa iba't ibang MySQL node na walang alam tungkol sa isa't isa.

Maaaring mapanganib ang autoincrementing

Ang AUTOINCREMENT ay isang karaniwang paraan upang bumuo ng mga pangunahing key. Mayroong madalas na mga kaso kapag ang mga database ay ginagamit bilang mga generator ng ID, at ang database ay naglalaman ng mga talahanayan na idinisenyo upang bumuo ng mga identifier. Mayroong ilang mga dahilan kung bakit ang pagbuo ng mga pangunahing key gamit ang auto-incrementing ay malayo sa perpekto:

  • Sa isang distributed database, ang auto-incrementing ay isang seryosong problema. Para mabuo ang ID, kailangan ng pandaigdigang lock. Sa halip, maaari kang bumuo ng isang UUID: hindi ito nangangailangan ng pakikipag-ugnayan sa pagitan ng iba't ibang mga node ng database. Ang awtomatikong pagdaragdag gamit ang mga lock ay maaaring humantong sa pagtatalo at makabuluhang bawasan ang pagganap sa mga pagsingit sa mga distributed na sitwasyon. Ang ilang mga DBMS (halimbawa, MySQL) ay maaaring mangailangan ng espesyal na pagsasaayos at mas maingat na atensyon upang maayos na ayusin ang master-master replication. At madaling magkamali kapag nagko-configure, na hahantong sa mga pagkabigo sa pag-record.
  • Ang ilang mga database ay may mga algorithm ng partitioning batay sa mga pangunahing key. Ang magkakasunod na ID ay maaaring humantong sa hindi mahuhulaan na mga hot spot at tumaas na load sa ilang partition habang ang iba ay nananatiling idle.
  • Ang pangunahing key ay ang pinakamabilis na paraan upang ma-access ang isang row sa isang database. Gamit ang mas mahusay na mga paraan upang matukoy ang mga talaan, ang mga sequential ID ay maaaring gawing isang walang kwentang column na puno ng walang kabuluhang mga halaga ang mga sequential ID. Samakatuwid, hangga't maaari, mangyaring pumili ng natatangi at natural na pangunahing key sa buong mundo (hal. username).

Bago magpasya sa isang diskarte, isaalang-alang ang epekto ng auto-incrementing ID at UUID sa pag-index, partitioning, at sharding.

Maaaring maging kapaki-pakinabang ang lipas na data at hindi nangangailangan ng pag-lock

Ang Multiversion Concurrency Control (MVCC) ay nagpapatupad ng marami sa mga kinakailangan sa pagkakapare-pareho na panandaliang tinalakay sa itaas. Ang ilang mga database (halimbawa, Postgres, Spanner) ay gumagamit ng MVCC upang "magpakain" ng mga transaksyon na may mga snapshotβ€”mga mas lumang bersyon ng database. Ang mga transaksyon sa snapshot ay maaari ding i-serialize upang matiyak ang pagkakapare-pareho. Kapag nagbabasa mula sa isang lumang snapshot, binabasa ang lumang data.

Maaaring maging kapaki-pakinabang ang pagbabasa ng bahagyang lipas na data, halimbawa, kapag bumubuo ng analytics mula sa data o kinakalkula ang tinatayang mga pinagsama-samang halaga.

Ang unang bentahe ng pagtatrabaho sa legacy na data ay mababang latency (lalo na kung ang database ay ipinamamahagi sa iba't ibang heograpiya). Ang pangalawa ay ang mga read-only na transaksyon ay walang lock. Ito ay isang makabuluhang bentahe para sa mga application na maraming nagbabasa, hangga't maaari nilang pangasiwaan ang lipas na data.

Higit pang mga developer ang dapat malaman ito tungkol sa mga database
Ang server ng application ay nagbabasa ng data mula sa lokal na replica na 5 segundo na hindi napapanahon, kahit na ang pinakabagong bersyon ay available sa kabilang panig ng Karagatang Pasipiko

Awtomatikong nililinis ng mga DBMS ang mga mas lumang bersyon at, sa ilang mga kaso, pinapayagan kang gawin ito kapag hiniling. Halimbawa, pinapayagan ng Postgres ang mga user na gawin VACUUM kapag hiniling, at pana-panahon ding ginagawa ang operasyong ito nang awtomatiko. Ang Spanner ay nagpapatakbo ng isang basurero para maalis ang mga snapshot na mas matanda sa isang oras.

Ang anumang mga mapagkukunan ng oras ay napapailalim sa pagbaluktot

Ang pinakamahusay na itinatagong sikreto sa computer science ay ang lahat ng timing API ay nagsisinungaling. Sa katunayan, hindi alam ng aming mga makina ang eksaktong kasalukuyang oras. Ang mga computer ay naglalaman ng mga quartz crystal na bumubuo ng mga vibrations na ginagamit upang panatilihin ang oras. Gayunpaman, ang mga ito ay hindi sapat na tumpak at maaaring mauna/nahuhuli sa eksaktong oras. Ang shift ay maaaring umabot ng 20 segundo bawat araw. Samakatuwid, ang oras sa aming mga computer ay dapat na pana-panahong naka-synchronize sa network ng isa.

Ang mga NTP server ay ginagamit para sa pag-synchronize, ngunit ang proseso ng pag-synchronize mismo ay napapailalim sa mga pagkaantala sa network. Kahit na ang pag-synchronize sa isang NTP server sa parehong data center ay tumatagal ng ilang oras. Malinaw na ang pagtatrabaho sa isang pampublikong NTP server ay maaaring humantong sa mas malaking pagbaluktot.

Ang mga atomic na orasan at ang kanilang mga katapat na GPS ay mas mahusay para sa pagtukoy ng kasalukuyang oras, ngunit ang mga ito ay mahal at nangangailangan ng kumplikadong pag-setup, kaya hindi sila maaaring mai-install sa bawat kotse. Dahil dito, ang mga data center ay gumagamit ng isang tiered na diskarte. Ipinapakita ng mga orasan ng atomic at/o GPS ang eksaktong oras, pagkatapos nito ay i-broadcast ito sa ibang mga makina sa pamamagitan ng mga pangalawang server. Nangangahulugan ito na ang bawat makina ay makakaranas ng isang tiyak na offset mula sa eksaktong oras.

Ang sitwasyon ay pinalala ng katotohanan na ang mga application at database ay madalas na matatagpuan sa iba't ibang mga makina (kung hindi sa iba't ibang mga sentro ng data). Kaya, ang oras ay mag-iiba hindi lamang sa mga DB node na ibinahagi sa iba't ibang machine. Magiging iba din ito sa server ng application.

Ang Google TrueTime ay gumagamit ng ganap na kakaibang diskarte. Karamihan sa mga tao ay naniniwala na ang pag-unlad ng Google sa direksyong ito ay ipinaliwanag sa pamamagitan ng karaniwang paglipat sa atomic at GPS na mga orasan, ngunit ito ay bahagi lamang ng malaking larawan. Narito kung paano gumagana ang TrueTime:

  • Gumagamit ang TrueTime ng dalawang magkaibang pinagmulan: GPS at mga atomic na orasan. Ang mga orasan na ito ay may mga non-correlated na failure mode. [tingnan ang pahina 5 para sa mga detalye dito β€” tinatayang. transl.), kaya ang kanilang pinagsamang paggamit ay nagpapataas ng pagiging maaasahan.
  • Ang TrueTime ay may hindi pangkaraniwang API. Ibinabalik nito ang oras bilang isang agwat na may error sa pagsukat at kawalan ng katiyakan na nakapaloob dito. Ang aktwal na sandali sa oras ay nasa pagitan ng itaas at ibabang mga hangganan ng pagitan. Ang Spanner, ang ibinahagi na database ng Google, ay naghihintay lamang hanggang sa ligtas na sabihin na ang kasalukuyang oras ay wala sa saklaw. Ang pamamaraang ito ay nagpapakilala ng ilang latency sa system, lalo na kung ang kawalan ng katiyakan sa mga master ay mataas, ngunit tinitiyak ang kawastuhan kahit na sa isang sitwasyong ipinamamahagi sa buong mundo.

Higit pang mga developer ang dapat malaman ito tungkol sa mga database
Ang mga bahagi ng Spanner ay gumagamit ng TrueTime, kung saan ang TT.now() ay nagbabalik ng isang agwat, kaya ang Spanner ay natutulog lamang hanggang sa punto kung saan maaari itong maging kumpiyansa na ang kasalukuyang oras ay lumipas sa isang tiyak na punto

Ang pinababang katumpakan sa pagtukoy sa kasalukuyang oras ay nangangahulugan ng pagtaas sa tagal ng mga operasyon ng Spanner at pagbaba sa pagganap. Ito ang dahilan kung bakit mahalagang mapanatili ang pinakamataas na posibleng katumpakan kahit na imposibleng makakuha ng ganap na tumpak na relo.

Ang pagkaantala ay maraming kahulugan

Kung tatanungin mo ang isang dosenang eksperto tungkol sa kung ano ang pagkaantala, malamang na makakakuha ka ng iba't ibang mga sagot. Sa DBMS latency ay madalas na tinatawag na "database latency" at iba ito sa kung ano ang nakikita ng kliyente. Ang katotohanan ay ang kliyente ay nagmamasid sa kabuuan ng pagkaantala ng network at ang pagkaantala ng database. Ang kakayahang ihiwalay ang uri ng latency ay kritikal kapag nagde-debug ng mga lumalaking problema. Kapag nangongolekta at nagpapakita ng mga sukatan, palaging subukang bantayan ang parehong uri.

Dapat suriin ang mga kinakailangan sa pagganap para sa isang partikular na transaksyon

Minsan ang mga katangian ng pagganap ng isang DBMS at ang mga limitasyon nito ay tinukoy sa mga tuntunin ng write/read throughput at latency. Nagbibigay ito ng pangkalahatang pangkalahatang-ideya ng mga pangunahing parameter ng system, ngunit kapag sinusuri ang pagganap ng isang bagong DBMS, ang isang mas komprehensibong diskarte ay ang hiwalay na pagsusuri ng mga kritikal na operasyon (para sa bawat query at/o transaksyon). Mga halimbawa:

  • Sumulat ng throughput at latency kapag naglalagay ng bagong row sa table X (na may 50 milyong row) na may mga tinukoy na limitasyon at row padding sa mga nauugnay na talahanayan.
  • Pagkaantala sa pagpapakita ng mga kaibigan ng mga kaibigan ng isang user kapag ang average na bilang ng mga kaibigan ay 500.
  • Latency sa pagkuha ng nangungunang 100 entry mula sa history ng user kapag sinundan ng user ang 500 iba pang user na may X entry kada oras.

Maaaring kasama sa pagsusuri at pag-eeksperimento ang mga ganitong kritikal na kaso hanggang sa kumpiyansa ka na natutugunan ng database ang mga kinakailangan sa pagganap. Isinasaalang-alang din ng isang katulad na panuntunan ng thumb ang breakdown na ito kapag nangongolekta ng mga sukatan ng latency at pagtukoy ng mga SLO.

Magkaroon ng kamalayan sa mataas na cardinality kapag nangongolekta ng mga sukatan para sa bawat operasyon. Gumamit ng mga log, koleksyon ng kaganapan, o distributed na pagsubaybay para makakuha ng high-power na data sa pag-debug. Sa artikulong "Gustong i-debug ang Latency?Β» maaari mong gawing pamilyar ang iyong sarili sa mga pamamaraan ng delay debugging.

Maaaring mapanganib ang mga nested na transaksyon

Hindi lahat ng DBMS ay sumusuporta sa mga nested na transaksyon, ngunit kapag ginawa nila, ang mga naturang transaksyon ay maaaring magresulta sa mga hindi inaasahang error na hindi laging madaling makita (iyon ay, dapat na malinaw na mayroong ilang uri ng anomalya).

Maaari mong iwasan ang paggamit ng mga nested na transaksyon gamit ang mga library ng kliyente na maaaring makakita at maka-bypass sa mga ito. Kung hindi maaaring iwanan ang mga nested na transaksyon, mag-ingat sa pagpapatupad ng mga ito para maiwasan ang mga hindi inaasahang sitwasyon kung saan aksidenteng naabort ang mga nakumpletong transaksyon dahil sa mga nested.

Ang pag-encapsulate ng mga transaksyon sa iba't ibang layer ay maaaring humantong sa mga hindi inaasahang nested na transaksyon, at mula sa pagiging madaling mabasa ng code point of view, maaari itong maging mahirap na maunawaan ang mga intensyon ng may-akda. Tingnan ang sumusunod na programa:

with newTransaction():
   Accounts.create("609-543-222")
   with newTransaction():
       Accounts.create("775-988-322")
       throw Rollback();

Ano ang magiging output ng code sa itaas? Ibabalik ba nito ang parehong mga transaksyon, o ang panloob lang? Ano ang mangyayari kung umaasa tayo sa maraming layer ng mga aklatan na sumasaklaw sa paglikha ng mga transaksyon para sa atin? Matutukoy at mapapabuti ba natin ang mga ganitong kaso?

Isipin ang isang layer ng data na may maraming operasyon (hal. newAccount) ay ipinatupad na sa sarili nitong mga transaksyon. Ano ang mangyayari kung patakbuhin mo ang mga ito bilang bahagi ng mas mataas na antas ng lohika ng negosyo na tumatakbo sa loob ng sarili nitong transaksyon? Ano ang magiging paghihiwalay at pagkakapare-pareho sa kasong ito?

function newAccount(id string) {
  with newTransaction():
      Accounts.create(id)
}

Sa halip na maghanap ng mga sagot sa mga walang katapusang tanong, mas mabuting iwasan ang mga nested transaction. Pagkatapos ng lahat, ang iyong layer ng data ay madaling makapagsagawa ng mataas na antas ng mga operasyon nang hindi gumagawa ng sarili nitong mga transaksyon. Bilang karagdagan, ang lohika ng negosyo mismo ay may kakayahang magsimula ng isang transaksyon, magsagawa ng mga operasyon dito, gumawa o mag-abort ng isang transaksyon.

function newAccount(id string) {
   Accounts.create(id)
}
// In main application:
with newTransaction():
   // Read some data from database for configuration.
   // Generate an ID from the ID service.
   Accounts.create(id)
   Uploads.create(id) // create upload queue for the user.

Ang mga transaksyon ay hindi dapat iugnay sa estado ng aplikasyon

Minsan nakakatukso na gumamit ng estado ng aplikasyon sa mga transaksyon para baguhin ang ilang partikular na halaga o i-tweak ang mga parameter ng query. Ang kritikal na nuance na dapat isaalang-alang ay ang tamang saklaw ng aplikasyon. Ang mga kliyente ay madalas na nag-restart ng mga transaksyon kapag may mga problema sa network. Kung ang transaksyon ay depende sa isang estado na binago ng ibang proseso, maaari itong pumili ng maling halaga depende sa posibilidad ng isang data race. Dapat isaalang-alang ng mga transaksyon ang panganib ng mga kundisyon ng data race sa application.

var seq int64
with newTransaction():
    newSeq := atomic.Increment(&seq)
    Entries.query(newSeq)
    // Other operations...

Ang transaksyon sa itaas ay dagdagan ang sequence number sa bawat oras na ito ay isasagawa, anuman ang huling resulta. Kung nabigo ang commit dahil sa mga problema sa network, isasagawa ang kahilingan gamit ang ibang sequence number kapag sinubukan mong muli.

Maraming masasabi sa iyo ang mga tagaplano ng query tungkol sa isang database

Tinutukoy ng mga tagaplano ng query kung paano isasagawa ang isang query sa isang database. Sinusuri din nila ang mga kahilingan at ino-optimize ang mga ito bago ipadala ang mga ito. Ang mga tagaplano ay maaari lamang magbigay ng ilang posibleng pagtatantya batay sa mga signal na kanilang itapon. Halimbawa, ano ang pinakamahusay na paraan ng paghahanap para sa sumusunod na query?

SELECT * FROM articles where author = "rakyll" order by title;

Ang mga resulta ay maaaring makuha sa dalawang paraan:

  • Pag-scan ng buong talahanayan: Maaari mong tingnan ang bawat entry sa talahanayan at ibalik ang mga artikulo na may katugmang pangalan ng may-akda, at pagkatapos ay i-order ang mga ito.
  • Index scan: Maaari kang gumamit ng index upang maghanap ng mga tumutugmang ID, kunin ang mga row na iyon, at pagkatapos ay i-order ang mga ito.

Ang trabaho ng query planner ay upang matukoy kung aling diskarte ang pinakamahusay. Ito ay nagkakahalaga ng pagsasaalang-alang na ang mga tagaplano ng query ay mayroon lamang limitadong mga kakayahan sa paghuhula. Ito ay maaaring humantong sa masasamang desisyon. Maaaring gamitin ng mga DBA o developer ang mga ito para mag-diagnose at mag-fine-tune ng mga query na hindi maganda ang performance. Maaaring i-configure ng mga bagong bersyon ng DBMS ang mga tagaplano ng query, at makakatulong ang self-diagnosis kapag ina-update ang database kung humahantong sa mga problema sa pagganap ang bagong bersyon. Ang mabagal na mga log ng query, mga ulat sa isyu ng latency, o mga istatistika ng oras ng pagpapatupad ay maaaring makatulong na matukoy ang mga query na nangangailangan ng pag-optimize.

Ang ilang sukatan na ipinakita ng tagaplano ng query ay maaaring sumailalim sa ingay (lalo na kapag tinatantya ang latency o oras ng CPU). Ang isang magandang karagdagan sa mga scheduler ay mga tool para sa pagsubaybay at pagsubaybay sa execution path. Pinapayagan ka nitong i-diagnose ang mga naturang problema (sayang, hindi lahat ng DBMS ay nagbibigay ng mga ganitong tool).

Ang online migration ay mahirap ngunit posible

Ang online na paglipat, live na paglipat, o real-time na paglipat ay nangangahulugan ng paglipat mula sa isang database patungo sa isa pa nang walang downtime o data corruption. Ang live na paglipat ay mas madaling isagawa kung ang paglipat ay nangyayari sa loob ng parehong DBMS/engine. Ang sitwasyon ay nagiging mas kumplikado kapag ito ay kinakailangan upang lumipat sa isang bagong DBMS na may iba't ibang mga kinakailangan sa pagganap at schema.

Mayroong iba't ibang mga online na modelo ng paglilipat. Narito ang isa sa kanila:

  • Paganahin ang double entry sa parehong database. Ang bagong database sa yugtong ito ay wala ang lahat ng data, ngunit tinatanggap lamang ang pinakabagong data. Kapag sigurado ka na dito, maaari kang magpatuloy sa susunod na hakbang.
  • Paganahin ang pagbabasa mula sa parehong mga database.
  • I-configure ang system upang ang pagbabasa at pagsusulat ay pangunahing gumanap sa bagong database.
  • Itigil ang pagsusulat sa lumang database habang patuloy na nagbabasa ng data mula dito. Sa yugtong ito, ang bagong database ay wala pa ring ilang data. Dapat silang kopyahin mula sa lumang database.
  • Ang lumang database ay read-only. Kopyahin ang nawawalang data mula sa lumang database patungo sa bago. Pagkatapos makumpleto ang paglipat, ilipat ang mga landas sa bagong database, at itigil ang luma at tanggalin ito sa system.

Para sa karagdagang impormasyon, inirerekomenda kong makipag-ugnayan Artikulo, na nagdedetalye ng diskarte sa paglipat ni Stripe batay sa modelong ito.

Ang isang makabuluhang pagtaas sa database ay nangangailangan ng pagtaas sa hindi mahuhulaan

Ang paglaki ng database ay humahantong sa mga hindi inaasahang problema na nauugnay sa sukat nito. Kung mas marami tayong nalalaman tungkol sa panloob na istraktura ng isang database, mas mahusay nating mahulaan kung paano ito susukat. Gayunpaman, ang ilang mga sandali ay imposible pa ring mahulaan.
Habang lumalaki ang base, ang mga nakaraang pagpapalagay at inaasahan tungkol sa dami ng data at mga kinakailangan sa bandwidth ng network ay maaaring maging luma na. Ito ay kapag ang tanong ay lumitaw sa mga pangunahing pag-overhaul sa disenyo, malakihang pagpapahusay sa pagpapatakbo, muling pag-iisip ng mga deployment, o paglipat sa iba pang mga DBMS upang maiwasan ang mga potensyal na problema.

Ngunit huwag isipin na ang mahusay na kaalaman sa panloob na istraktura ng umiiral na database ay ang tanging bagay na kinakailangan. Ang mga bagong kaliskis ay magdadala ng mga bagong hindi alam. Ang hindi mahuhulaan na mga punto ng sakit, hindi pantay na pamamahagi ng data, hindi inaasahang bandwidth at mga isyu sa hardware, patuloy na pagtaas ng trapiko at mga bagong segment ng network ay pipilitin kang pag-isipang muli ang iyong diskarte sa database, modelo ng data, modelo ng pag-deploy, at laki ng database.

...

Sa oras na sinimulan kong isipin ang tungkol sa pag-publish ng artikulong ito, mayroon nang limang higit pang mga item sa aking orihinal na listahan. Pagkatapos ay dumating ang isang malaking bilang mga bagong ideya tungkol sa kung ano pa ang maaaring saklawin. Samakatuwid, ang artikulo ay humipo sa hindi gaanong malinaw na mga problema na nangangailangan ng maximum na pansin. Gayunpaman, hindi ito nangangahulugan na ang paksa ay naubos na at hindi na ako babalik dito sa aking mga materyal sa hinaharap at hindi na gagawa ng mga pagbabago sa kasalukuyan.

PS

Basahin din sa aming blog:

Pinagmulan: www.habr.com

Magdagdag ng komento