Fler utvecklare borde veta detta om databaser

Notera. transl.: Jaana Dogan är en erfaren ingenjör på Google som för närvarande arbetar med observerbarhet av företagets produktionstjänster skrivna i Go. I denna artikel, som fick stor popularitet bland den engelsktalande publiken, samlade hon på 17 punkter viktiga tekniska detaljer angående DBMS (och ibland distribuerade system i allmänhet) som är användbara att tänka på för utvecklare av stora/krävande applikationer.

Fler utvecklare borde veta detta om databaser

De allra flesta datorsystem håller reda på sitt tillstånd och kräver därför någon form av datalagringssystem. Jag samlade på mig kunskap om databaser under lång tid, längs vägen och gjorde designmisstag som ledde till dataförlust och avbrott. I system som bearbetar stora mängder information ligger databaser i hjärtat av systemarkitekturen och fungerar som en nyckelfaktor för att välja den optimala lösningen. Trots att man ägnar stor uppmärksamhet åt databasens arbete är problemen som applikationsutvecklare försöker förutse ofta bara toppen av ett isberg. I den här serien av artiklar delar jag några idéer som kommer att vara användbara för utvecklare som inte är specialiserade på detta område.

  1. Du har tur om nätverket 99,999 % av gångerna inte orsakar problem.
  2. SYRA betyder många olika saker.
  3. Varje databas har sina egna mekanismer för att säkerställa konsekvens och isolering.
  4. Optimistisk blockering kommer till undsättning när det är svårt att upprätthålla den vanliga.
  5. Det finns andra anomalier förutom smutsiga läsningar och dataförlust.
  6. Databasen och användaren kommer inte alltid överens om tillvägagångssättet.
  7. Skärning på applikationsnivå kan flyttas utanför applikationen.
  8. Autoinkrementering kan vara farligt.
  9. Inaktuella data kan vara användbara och behöver inte låsas.
  10. Förvrängningar är typiska för alla tidskällor.
  11. Fördröjning har många betydelser.
  12. Prestandakrav bör utvärderas för en specifik transaktion.
  13. Kapslade transaktioner kan vara farliga.
  14. Transaktioner bör inte vara knutna till applikationsstatus.
  15. Frågeplanerare kan berätta mycket om databaser.
  16. Migrering online är svårt, men möjligt.
  17. En betydande ökning av databasen medför en ökad oförutsägbarhet.

Jag vill tacka Emmanuel Odeke, Rein Henrichs och andra för deras feedback på en tidigare version av denna artikel.

Du har tur om nätverket 99,999 % av gångerna inte orsakar problem.

Frågan kvarstår om hur tillförlitlig modern nätverksteknik är och hur ofta system är nere på grund av nätverksfel. Informationen i denna fråga är knapphändig och forskningen domineras ofta av stora organisationer med specialiserade nätverk, utrustning och personal.

Med en tillgänglighetsgrad på 99,999 % för Spanner (Googles globalt distribuerade databas) hävdar Google att endast 7,6% problem är relaterade till nätverket. Samtidigt kallar företaget sitt specialiserade nätverk för "huvudpelaren" för hög tillgänglighet. Studie Bailis och Kingsbury, genomförd 2014, utmanar en av "missuppfattningar om distribuerad datoranvändning", som Peter Deutsch formulerade 1994. Är nätverket verkligen pålitligt?

Omfattande forskning utanför jätteföretag, utförd för det bredare Internet, existerar helt enkelt inte. Det finns inte heller tillräckligt med data från de stora aktörerna om hur stor andel av deras kunders problem som är nätverksrelaterade. Vi är väl medvetna om avbrott i nätverksstacken av stora molnleverantörer som kan ta ner en hel del av internet under flera timmar bara för att det är högprofilerade händelser som påverkar ett stort antal människor och företag. Nätverksavbrott kan orsaka problem i många fler fall, även om inte alla dessa fall är i rampljuset. Kunder av molntjänster vet inte heller något om orsakerna till problemen. Om det finns ett fel är det nästan omöjligt att hänföra det till ett nätverksfel på tjänsteleverantörens sida. För dem är tredjepartstjänster svarta lådor. Det är omöjligt att bedöma effekten utan att vara en stor tjänsteleverantör.

Med tanke på vad de stora spelarna rapporterar om sina system, är det säkert att säga att du har tur om nätverkssvårigheter bara står för en liten andel av potentiella stilleståndsproblem. Nätverkskommunikation lider fortfarande av så vardagliga saker som hårdvarufel, topologiändringar, administrativa konfigurationsändringar och strömavbrott. Nyligen blev jag förvånad över att höra att listan över möjliga problem lades till hajbett (ja, du hörde rätt).

SYRA betyder många olika saker

Akronymen ACID står för Atomicity, Consistency, Isolation, Reliability. Dessa egenskaper hos transaktioner är avsedda att säkerställa deras giltighet i händelse av fel, fel, hårdvarufel etc. Utan ACID eller liknande system skulle det vara svårt för applikationsutvecklare att skilja på vad de ansvarar för och vad databasen ansvarar för. De flesta relationella transaktionsdatabaser försöker vara ACID-kompatibla, men nya tillvägagångssätt som NoSQL har gett upphov till många databaser utan ACID-transaktioner eftersom de är dyra att implementera.

När jag först kom in i branschen talade vår tekniska ledare om hur relevant ACID-konceptet var. För att vara rättvis anses ACID vara en grov beskrivning snarare än en strikt implementeringsstandard. Idag tycker jag att det mestadels är användbart eftersom det tar upp en specifik kategori av problem (och föreslår en rad möjliga lösningar).

Inte alla DBMS är ACID-kompatibla; Samtidigt förstår databasimplementeringar som stöder ACID kraven på olika sätt. En av anledningarna till att ACID-implementeringar är ojämna beror på de många avvägningar som måste göras för att implementera ACID-kraven. Skapare kan presentera sina databaser som ACID-kompatibla, men tolkningen av kantfall kan variera dramatiskt, liksom mekanismen för att hantera "osannolika" händelser. Åtminstone kan utvecklare få en förståelse på hög nivå av krångligheterna med basimplementeringar för att få en ordentlig förståelse för deras speciella beteende och designavvägningar.

Debatten om huruvida MongoDB uppfyller ACID-kraven fortsätter även efter releasen av version 4. MongoDB har inte stötts på länge skogsavverkning, även om data som standard inte överfördes till disken mer än en gång var 60:e sekund. Föreställ dig följande scenario: en ansökan postar två skrivningar (w1 och w2). MongoDB lagrar framgångsrikt w1, men w2 går förlorad på grund av ett maskinvarufel.

Fler utvecklare borde veta detta om databaser
Diagram som illustrerar scenariot. MongoDB kraschar innan den kan skriva data till disk

Att binda sig till disk är en dyr process. Genom att undvika frekventa commits förbättrar utvecklarna inspelningsprestandan på bekostnad av tillförlitligheten. MongoDB stöder för närvarande loggning, men smutsiga skrivningar kan fortfarande påverka dataintegriteten eftersom loggar fångas var 100:e ms som standard. Det vill säga, ett liknande scenario är fortfarande möjligt för loggar och de ändringar som presenteras i dem, även om risken är mycket lägre.

Varje databas har sina egna konsistens- och isoleringsmekanismer

Av ACID-kraven har konsekvens och isolering det största antalet olika implementeringar eftersom utbudet av avvägningar är bredare. Det måste sägas att konsekvens och isolering är ganska dyra funktioner. De kräver samordning och ökar konkurrensen för datakonsistens. Problemets komplexitet ökar avsevärt när det är nödvändigt att skala databasen horisontellt över flera datacenter (särskilt om de är belägna i olika geografiska regioner). Att uppnå en hög nivå av konsekvens är mycket svårt, eftersom det också minskar tillgängligheten och ökar nätverkssegmenteringen. För en mer allmän förklaring av detta fenomen råder jag dig att hänvisa till CAP-sats. Det är också värt att notera att applikationer kan hantera små mängder inkonsekvens, och programmerare kan förstå nyanserna av problemet tillräckligt bra för att implementera ytterligare logik i applikationen för att hantera inkonsekvens utan att förlita sig mycket på databasen för att hantera det.

DBMS ger ofta olika nivåer av isolering. Applikationsutvecklare kan välja den mest effektiva baserat på deras preferenser. Låg isolering möjliggör ökad hastighet, men ökar också risken för ett datarace. Hög isolering minskar denna sannolikhet, men saktar ner arbetet och kan leda till konkurrens, vilket kommer att leda till sådana bromsar i basen att fel börjar.

Fler utvecklare borde veta detta om databaser
Genomgång av befintliga samtidighetsmodeller och relationer dem emellan

SQL-standarden definierar endast fyra isoleringsnivåer, även om det i teori och praktik finns många fler. Jepson.io ger en utmärkt överblick över befintliga samtidighetsmodeller. Till exempel garanterar Google Spanner extern serialiserbarhet med klocksynkronisering, och även om detta är ett striktare isoleringsskikt, är det inte definierat i standardisoleringsskikt.

SQL-standarden nämner följande isoleringsnivåer:

  • serializable (strängast och dyrast): Serialiserbar exekvering har samma effekt som viss sekventiell transaktionsexekvering. Sekventiell exekvering innebär att varje efterföljande transaktion börjar först efter att den föregående är slutförd. Det bör noteras att nivån serializable implementeras ofta som så kallad snapshot-isolering (till exempel i Oracle) på grund av skillnader i tolkning, även om snapshot-isolering i sig inte är representerad i SQL-standarden.
  • Upprepningsbara läsningar: Oengagerade poster i den aktuella transaktionen är tillgängliga för den aktuella transaktionen, men ändringar gjorda av andra transaktioner (som nya rader) inte synlig.
  • Läs engagerad: Oengagerad data är inte tillgänglig för transaktioner. I det här fallet kan transaktioner endast se engagerad data, och fantomläsningar kan förekomma. Om en transaktion infogar och bekräftar nya rader, kommer den aktuella transaktionen att kunna se dem när de efterfrågas.
  • Läs oengagerad (minst strikt och dyr nivå): Smutsiga läsningar är tillåtna, transaktioner kan se oengagerade ändringar gjorda av andra transaktioner. I praktiken kan denna nivå vara användbar för grova uppskattningar, såsom frågor COUNT(*) på bordet.

Nivå serializable minimerar risken för dataraces, samtidigt som den är den dyraste att implementera och resulterar i den högsta konkurrensbelastningen på systemet. Andra isoleringsnivåer är lättare att implementera, men ökar sannolikheten för dataraces. Vissa DBMS låter dig ställa in en anpassad isoleringsnivå, andra har starka preferenser och inte alla nivåer stöds.

Stöd för isoleringsnivåer annonseras ofta i ett givet DBMS, men bara en noggrann studie av dess beteende kan avslöja vad som faktiskt händer.

Fler utvecklare borde veta detta om databaser
Granskning av samtidiga anomalier på olika isoleringsnivåer för olika DBMS

Martin Kleppmann i sitt projekt eremit Jämför olika isoleringsnivåer, talar om samtidiga anomalier och om databasen kan hålla sig till en viss isoleringsnivå. Kleppmanns forskning visar hur olika databasutvecklare tänker kring isoleringsnivåer.

Optimistisk blockering kommer till undsättning när det är svårt att upprätthålla den vanliga.

Blockering kan bli mycket dyrt, inte bara för att det ökar konkurrensen i databasen, utan också för att det kräver att applikationsservrarna ständigt ansluter till databasen. Nätverkssegmentering kan förvärra exklusiva låsningssituationer och leda till låsningar som är svåra att identifiera och lösa. I de fall exklusiv låsning inte är lämplig hjälper optimistisk låsning.

Optimistiskt lås är en metod där den tar hänsyn till dess version, kontrollsumma eller tidpunkt för senaste ändring när den läser en sträng. Detta låter dig säkerställa att det inte finns någon atomär versionsändring innan du ändrar en post:

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

I det här fallet, uppdatering av tabellen products kommer inte att utföras om en annan operation tidigare gjort ändringar på den här raden. Om inga andra operationer utfördes på den här raden kommer ändringen för en rad att ske och vi kan säga att uppdateringen lyckades.

Det finns andra anomalier förutom smutsiga läsningar och dataförlust

När det gäller datakonsistens ligger fokus på potentialen för tävlingsförhållanden som kan leda till smutsiga avläsningar och dataförlust. Dataavvikelser slutar dock inte där.

Ett exempel på sådana anomalier är inspelningsförvrängning (skriv skevt). Förvrängningar är svåra att upptäcka eftersom de vanligtvis inte aktivt letas efter. De beror inte på smutsiga läsningar eller dataförlust, utan på överträdelser av logiska begränsningar som lagts på data.

Låt oss till exempel överväga en övervakningsapplikation som kräver att en operatör är jour hela tiden:

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;

I ovanstående situation kommer en postkorruption att inträffa om båda transaktionerna genomförs framgångsrikt. Även om det inte förekom några smutsiga läsningar eller dataförluster, äventyrades dataintegriteten: nu anses två personer jourande samtidigt.

Serialiserbar isolering, schemadesign eller databasbegränsningar kan hjälpa till att eliminera skrivkorruption. Utvecklare måste kunna identifiera sådana anomalier under utvecklingen för att undvika dem i produktionen. Samtidigt är inspelningsförvrängningar extremt svåra att leta efter i kodbasen. Speciellt i stora system, när olika utvecklingsteam är ansvariga för att implementera funktioner baserade på samma tabeller och inte är överens om detaljerna för dataåtkomst.

Databasen och användaren är inte alltid överens om vad de ska göra

En av nyckelfunktionerna i databaser är garantin för exekveringsorder, men denna order i sig kanske inte är transparent för mjukvaruutvecklaren. Databaser utför transaktioner i den ordning de tas emot, inte i den ordning programmerare avser. Ordningen på transaktioner är svår att förutsäga, särskilt i högt belastade parallella system.

Under utveckling, särskilt när man arbetar med icke-blockerande bibliotek, kan dålig stil och låg läsbarhet få användare att tro att transaktioner exekveras sekventiellt, när de i själva verket kan komma in i databasen i vilken ordning som helst.

Vid första anblicken, i programmet nedan, kallas T1 och T2 sekventiellt, men om dessa funktioner är icke-blockerande och omedelbart returnerar resultatet i formuläret löfte, då kommer ordningen på samtalen att bestämmas av de ögonblick då de kom in i databasen:

resultat1 = T1() // verkliga resultat är löften
resultat2 = T2()

Om atomicitet krävs (det vill säga antingen alla operationer måste slutföras eller avbrytas) och sekvensen spelar roll, måste operationerna T1 och T2 utföras inom en enda transaktion.

Skärning på applikationsnivå kan flyttas utanför applikationen

Sharding är en metod för att horisontellt partitionera en databas. Vissa databaser kan automatiskt dela data horisontellt, medan andra inte kan, eller inte är särskilt bra på det. När dataarkitekter/utvecklare kan förutsäga exakt hur data kommer att nås, kan de skapa horisontella partitioner i användarutrymmet istället för att delegera detta arbete till databasen. Denna process kallas "skärning på applikationsnivå" (skärning på applikationsnivå).

Tyvärr skapar detta namn ofta missuppfattningen att sharding lever i applikationstjänster. Faktum är att det kan implementeras som ett separat lager framför databasen. Beroende på datatillväxt och schemaupprepningar kan skärningskraven bli ganska komplexa. Vissa strategier kan dra nytta av möjligheten att iterera utan att behöva omdistribuera applikationsservrar.

Fler utvecklare borde veta detta om databaser
Ett exempel på en arkitektur där applikationsservrar är separerade från fragmenteringstjänsten

Att flytta sharding till en separat tjänst utökar möjligheten att använda olika shardingstrategier utan att behöva omdistribuera applikationer. Vitess är ett exempel på ett sådant skärningssystem på applikationsnivå. Vitess tillhandahåller horisontell skärning för MySQL och låter klienter ansluta till det via MySQL-protokollet. Systemet segmenterar data i olika MySQL-noder som inte vet något om varandra.

Autoinkrementering kan vara farligt

AUTOINCREMENT är ett vanligt sätt att generera primärnycklar. Det finns ofta fall då databaser används som ID-generatorer och databasen innehåller tabeller utformade för att generera identifierare. Det finns flera anledningar till varför det är långt ifrån idealiskt att generera primärnycklar med automatisk inkrementering:

  • I en distribuerad databas är automatisk inkrementering ett allvarligt problem. För att generera ID:t krävs ett globalt lås. Istället kan du generera ett UUID: detta kräver inte interaktion mellan olika databasnoder. Automatisk inkrementering med lås kan leda till konflikter och avsevärt minska prestandan på skär i distribuerade situationer. Vissa DBMS:er (till exempel MySQL) kan kräva speciell konfiguration och mer noggrann uppmärksamhet för att korrekt organisera master-master-replikering. Och det är lätt att göra misstag när du konfigurerar, vilket kommer att leda till inspelningsfel.
  • Vissa databaser har partitioneringsalgoritmer baserade på primärnycklar. På varandra följande ID kan leda till oförutsägbara hot spots och ökad belastning på vissa partitioner medan andra förblir inaktiva.
  • En primärnyckel är det snabbaste sättet att komma åt en rad i en databas. Med bättre sätt att identifiera poster kan sekventiella ID:n förvandla den viktigaste kolumnen i tabeller till en värdelös kolumn fylld med meningslösa värden. Därför, när det är möjligt, välj en globalt unik och naturlig primärnyckel (t.ex. användarnamn).

Innan du bestämmer dig för ett tillvägagångssätt, överväg inverkan av auto-inkrementerande ID:n och UUID:n på indexering, partitionering och sönderdelning.

Inaktuella data kan vara användbara och kräver inte låsning

Multiversion Concurrency Control (MVCC) implementerar många av konsistenskraven som diskuterades kort ovan. Vissa databaser (till exempel Postgres, Spanner) använder MVCC för att "mata" transaktioner med ögonblicksbilder - äldre versioner av databasen. Ögonblicksbildstransaktioner kan också serialiseras för att säkerställa konsekvens. När man läser från en gammal ögonblicksbild läses inaktuella data.

Att läsa lite inaktuella data kan vara användbart, till exempel när man genererar analyser från data eller beräknar ungefärliga aggregerade värden.

Den första fördelen med att arbeta med äldre data är låg latens (särskilt om databasen är fördelad över olika geografier). Det andra är att skrivskyddade transaktioner är låsfria. Detta är en betydande fördel för applikationer som läser mycket, så länge de kan hantera inaktuella data.

Fler utvecklare borde veta detta om databaser
Applikationsservern läser data från den lokala repliken som är 5 sekunder inaktuell, även om den senaste versionen är tillgänglig på andra sidan Stilla havet

DBMS rensar automatiskt äldre versioner och låter dig i vissa fall göra detta på begäran. Till exempel tillåter Postgres användare att göra VACUUM på begäran, och utför även regelbundet denna operation automatiskt. Spanner driver en sophämtare för att bli av med ögonblicksbilder äldre än en timme.

Alla tidskällor är föremål för förvrängning

Den bäst bevarade hemligheten inom datavetenskap är att alla tidsgränssnitt ljuger. Faktum är att våra maskiner inte vet den exakta aktuella tiden. Datorer innehåller kvartskristaller som genererar vibrationer som används för att hålla tiden. De är dock inte tillräckligt exakta och kan ligga före/släpa efter den exakta tiden. Skiftet kan nå 20 sekunder per dag. Därför måste tiden på våra datorer periodvis synkroniseras med nätverket.

NTP-servrar används för synkronisering, men själva synkroniseringsprocessen är föremål för nätverksförseningar. Även synkronisering med en NTP-server i samma datacenter tar lite tid. Det är tydligt att arbete med en offentlig NTP-server kan leda till ännu större förvrängning.

Atomklockor och deras GPS-motsvarigheter är bättre för att bestämma den aktuella tiden, men de är dyra och kräver komplex installation, så de kan inte installeras på varje bil. På grund av detta använder datacenter ett stegvis tillvägagångssätt. Atom- och/eller GPS-klockor visar den exakta tiden, varefter den sänds till andra maskiner via sekundära servrar. Detta innebär att varje maskin kommer att uppleva en viss förskjutning från den exakta tiden.

Situationen förvärras av att applikationer och databaser ofta finns på olika maskiner (om inte i olika datacenter). Sålunda kommer tiden att skilja sig inte bara på DB-noder fördelade över olika maskiner. Det kommer också att vara annorlunda på applikationsservern.

Google TrueTime tar ett helt annat tillvägagångssätt. De flesta tror att Googles framsteg i denna riktning förklaras av den banala övergången till atom- och GPS-klockor, men detta är bara en del av helheten. Så här fungerar TrueTime:

  • TrueTime använder två olika källor: GPS och atomur. Dessa klockor har icke-korrelerade fellägen. [se sidan 5 för detaljer här - cirka. översätt.), så deras gemensamma användning ökar tillförlitligheten.
  • TrueTime har ett ovanligt API. Den returnerar tid som ett intervall med mätfel och osäkerhet inbyggd. Det faktiska ögonblicket ligger någonstans mellan intervallets övre och nedre gränser. Spanner, Googles distribuerade databas, väntar helt enkelt tills det är säkert att säga att den aktuella tiden är utanför intervallet. Denna metod introducerar viss latens i systemet, särskilt om osäkerheten på masterna är hög, men säkerställer korrekthet även i en globalt distribuerad situation.

Fler utvecklare borde veta detta om databaser
Spanner-komponenterna använder TrueTime, där TT.now() returnerar ett intervall, så nyckeln sover helt enkelt tills den kan vara säker på att den aktuella tiden har passerat en viss punkt

Minskad noggrannhet vid bestämning av den aktuella tiden innebär en ökning av varaktigheten av nyckelfunktioner och en minskning av prestanda. Det är därför det är viktigt att upprätthålla högsta möjliga noggrannhet även om det är omöjligt att få en helt exakt klocka.

Fördröjning har många betydelser

Frågar du ett dussin experter om vad en försening är så får du förmodligen olika svar. I DBMS kallas latens ofta "databaslatens" och skiljer sig från vad som uppfattas av klienten. Faktum är att klienten observerar summan av nätverksfördröjningen och databasfördröjningen. Möjligheten att isolera typen av latens är avgörande vid felsökning av växande problem. När du samlar in och visar mätvärden, försök alltid hålla ett öga på båda typerna.

Prestandakrav bör utvärderas för en specifik transaktion

Ibland specificeras prestandaegenskaperna för ett DBMS och dess begränsningar i termer av skriv/läs genomströmning och latens. Detta ger en allmän översikt över viktiga systemparametrar, men när man utvärderar prestandan för ett nytt DBMS är ett mycket mer omfattande tillvägagångssätt att separat utvärdera kritiska operationer (för varje fråga och/eller transaktion). Exempel:

  • Skriv genomströmning och latens när du infogar en ny rad i tabell X (med 50 miljoner rader) med specificerade begränsningar och radutfyllnad i relaterade tabeller.
  • Fördröjning med att visa vänners vänner till en viss användare när det genomsnittliga antalet vänner är 500.
  • Fördröjning för att hämta de 100 bästa posterna från en användares historik när användaren följer 500 andra användare med X poster per timme.

Utvärdering och experiment kan innefatta sådana kritiska fall tills du är säker på att databasen uppfyller prestandakraven. En liknande tumregel tar också hänsyn till denna uppdelning när man samlar in latensmått och bestämmer SLO:er.

Var medveten om hög kardinalitet när du samlar in mätvärden för varje operation. Använd loggar, händelseinsamling eller distribuerad spårning för att erhålla felsökningsdata med hög effekt. I artikeln "Vill du felsöka latens?» du kan bekanta dig med metoder för fördröjd felsökning.

Kapslade transaktioner kan vara farliga

Inte alla DBMS stöder kapslade transaktioner, men när de gör det kan sådana transaktioner resultera i oväntade fel som inte alltid är lätta att upptäcka (det vill säga det borde vara uppenbart att det finns någon form av anomali).

Du kan undvika att använda kapslade transaktioner med hjälp av klientbibliotek som kan upptäcka och kringgå dem. Om kapslade transaktioner inte kan överges, var särskilt försiktig vid implementeringen för att undvika oväntade situationer där slutförda transaktioner av misstag avbryts på grund av kapslade transaktioner.

Att kapsla in transaktioner i olika lager kan leda till oväntade kapslade transaktioner och ur kodläsbarhetssynpunkt kan det göra det svårt att förstå författarens avsikter. Ta en titt på följande program:

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

Vad blir resultatet av ovanstående kod? Kommer det att rulla tillbaka båda transaktionerna, eller bara den inre? Vad händer om vi förlitar oss på flera lager av bibliotek som kapslar in skapandet av transaktioner åt oss? Kommer vi att kunna identifiera och förbättra sådana fall?

Föreställ dig ett datalager med flera operationer (t.ex. newAccount) är redan implementerad i sina egna transaktioner. Vad händer om du kör dem som en del av affärslogik på högre nivå som körs inom sin egen transaktion? Vad skulle vara isoleringen och konsekvensen i det här fallet?

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

Istället för att söka efter svar på sådana oändliga frågor är det bättre att undvika kapslade transaktioner. När allt kommer omkring kan ditt datalager enkelt utföra operationer på hög nivå utan att skapa egna transaktioner. Dessutom kan affärslogiken själv initiera en transaktion, utföra operationer på den, begå eller avbryta en transaktion.

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.

Transaktioner bör inte vara knutna till applikationsstatus

Ibland är det frestande att använda applikationstillstånd i transaktioner för att ändra vissa värden eller justera frågeparametrar. Den kritiska nyansen att överväga är det korrekta tillämpningsomfånget. Klienter startar ofta om transaktioner när det finns nätverksproblem. Om transaktionen sedan beror på ett tillstånd som ändras av någon annan process, kan den välja fel värde beroende på möjligheten till ett datarace. Transaktioner måste ta hänsyn till risken för datarace-förhållanden i applikationen.

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

Ovanstående transaktion kommer att öka sekvensnumret varje gång den exekveras, oavsett slutresultatet. Om commit misslyckas på grund av nätverksproblem, kommer begäran att utföras med ett annat sekvensnummer när du försöker igen.

Frågeplanerare kan berätta mycket om en databas

Frågeplanerare bestämmer hur en fråga ska köras i en databas. De analyserar också förfrågningar och optimerar dem innan de skickas. Planerare kan bara tillhandahålla några möjliga uppskattningar baserat på de signaler som står till deras förfogande. Vilken är till exempel den bästa sökmetoden för följande fråga?

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

Resultaten kan hämtas på två sätt:

  • Full bordsskanning: Du kan titta på varje post i tabellen och returnera artiklar med ett matchande författarnamn och sedan beställa dem.
  • Indexskanning: Du kan använda ett index för att hitta matchande ID:n, hämta dessa rader och sedan beställa dem.

Frågeplanerarens uppgift är att avgöra vilken strategi som är bäst. Det är värt att tänka på att frågeplanerare bara har begränsade prediktionsmöjligheter. Detta kan leda till dåliga beslut. DBA:er eller utvecklare kan använda dem för att diagnostisera och finjustera underpresterande frågor. Nya versioner av DBMS kan konfigurera frågeplanerare, och självdiagnos kan hjälpa till vid uppdatering av databasen om den nya versionen leder till prestandaproblem. Långsamma frågeloggar, rapporter om latensproblem eller statistik över körningstid kan hjälpa till att identifiera frågor som behöver optimeras.

Vissa mätvärden som presenteras av frågeplaneraren kan vara föremål för brus (särskilt när man uppskattar latens eller CPU-tid). Ett bra tillägg till schemaläggare är verktyg för att spåra och spåra exekveringsvägen. De låter dig diagnostisera sådana problem (det är tyvärr inte alla DBMS som tillhandahåller sådana verktyg).

Migrering online är svårt men möjligt

Onlinemigrering, direktmigrering eller realtidsmigrering innebär att flytta från en databas till en annan utan driftstopp eller datakorruption. Livemigrering är lättare att genomföra om övergången sker inom samma DBMS/motor. Situationen blir mer komplicerad när det är nödvändigt att flytta till ett nytt DBMS med olika prestanda- och schemakrav.

Det finns olika migreringsmodeller online. Här är en av dem:

  • Aktivera dubbelinmatning i båda databaserna. Den nya databasen har i detta skede inte all data, utan accepterar bara de senaste uppgifterna. När du är säker på detta kan du gå vidare till nästa steg.
  • Aktivera läsning från båda databaserna.
  • Konfigurera systemet så att läsning och skrivning i första hand utförs på den nya databasen.
  • Sluta skriva till den gamla databasen medan du fortsätter att läsa data från den. I detta skede saknar den nya databasen fortfarande en del data. De ska kopieras från den gamla databasen.
  • Den gamla databasen är skrivskyddad. Kopiera de data som saknas från den gamla databasen till den nya. När migreringen är klar byter du sökvägarna till den nya databasen och stoppar den gamla och tar bort den från systemet.

För ytterligare information rekommenderar jag att du kontaktar artikeln, som beskriver Stripes migreringsstrategi baserad på denna modell.

En betydande ökning av databasen medför en ökad oförutsägbarhet

Tillväxten av databasen leder till oförutsägbara problem i samband med dess skala. Ju mer vi vet om den interna strukturen i en databas, desto bättre kan vi förutsäga hur den kommer att skala. Vissa ögonblick är dock fortfarande omöjliga att förutse.
När basen växer kan tidigare antaganden och förväntningar om datavolym och nätverksbandbreddskrav bli föråldrade. Det är då frågan uppstår om större designöversyn, storskaliga operativa förbättringar, omtänkta implementeringar eller migrering till andra DBMS för att undvika potentiella problem.

Men tro inte att utmärkt kunskap om den interna strukturen i den befintliga databasen är det enda som behövs. Nya vågar kommer att föra med sig nya okända. Oförutsägbara smärtpunkter, ojämn datadistribution, oväntade bandbredds- och hårdvaruproblem, ständigt ökande trafik och nya nätverkssegment kommer att tvinga dig att ompröva din databasmetod, datamodell, implementeringsmodell och databasstorlek.

.

När jag började fundera på att publicera den här artikeln fanns det redan fem objekt till på min ursprungliga lista. Sedan kom ett stort antal nya idéer om vad mer som kan täckas. Därför berör artikeln de minst uppenbara problemen som kräver maximal uppmärksamhet. Detta betyder dock inte att ämnet har uttömts och jag kommer inte längre att återkomma till det i mitt framtida material och kommer inte att göra ändringar i det nuvarande.

PS

Läs även på vår blogg:

Källa: will.com

Lägg en kommentar