Antimönstret monolitisk beständighet
Att placera alla data för ett program i ett enda datalager kan försämra prestandan, eftersom det leder till resurskonkurrens eller för att datalagret inte passar vissa data.
Problembeskrivning
Historiskt sett har programmen ofta använt ett enda datalager, oavsett de olika typerna av data som programmet kan behöva lagra. Normalt gjordes det för att förenkla programdesignen eller för att matcha de befintliga kunskaperna hos utvecklingsteamet.
Moderna molnbaserade system har ofta ytterligare funktionella och icke-funktionella krav och behöver lagra många olika typer av data, som dokument, bilder, cachelagrade data, köplacerade meddelanden, programloggar och telemetri. Att arbeta traditionellt och placera all information i samma datalager kan försämra prestandan av två huvudanledningar:
- Att lagra och hämta stora mängder orelaterade data i samma datalager kan orsaka konkurrens, vilket i sin tur leder till långa svarstider och anslutningsfel.
- Oavsett vilket datalager som väljs kan det passa dåligt för vissa typer av data eller så kanske det inte är optimerat för de åtgärder som programmet utför.
I följande exempel visas en ASP.NET-webb-API-kontrollant som lägger till en ny post i en databas och även registrerar resultatet i en logg. Loggen lagras i samma databas som affärsdata. Du hittar hela exemplet här.
public class MonoController : ApiController
{
private static readonly string ProductionDb = ...;
public async Task<IHttpActionResult> PostAsync([FromBody]string value)
{
await DataAccess.InsertPurchaseOrderHeaderAsync(ProductionDb);
await DataAccess.LogAsync(ProductionDb, LogTableName);
return Ok();
}
}
Hastigheten som loggposterna genereras på påverkar troligen verksamhetsåtgärdernas prestanda. Och om en annan komponent, till exempel en programprocessövervakare, regelbundet läser och bearbetar loggdata kan det också påverka verksamheten.
Åtgärda problemet
Separera data enligt användning. För varje datauppsättning väljer du ett datalager som bäst matchar hur datauppsättningen ska användas. I det tidigare exemplet bör programmet logga i ett annat lager än databasen som lagrar affärsdata:
public class PolyController : ApiController
{
private static readonly string ProductionDb = ...;
private static readonly string LogDb = ...;
public async Task<IHttpActionResult> PostAsync([FromBody]string value)
{
await DataAccess.InsertPurchaseOrderHeaderAsync(ProductionDb);
// Log to a different data store.
await DataAccess.LogAsync(LogDb, LogTableName);
return Ok();
}
}
Att tänka på
Separera data enligt användning och åtkomst. Lagra till exempel inte logginformation och affärsdata i samma datalager. De här typerna av data har betydligt olika krav och åtkomstmönster. Loggposter är sekventiella till sin natur, medan affärsdata mer troligt kräver slumpmässig åtkomst och är ofta relationella.
Överväg dataåtkomstmönstret för varje typ av data. Du kan till exempel lagra formaterade rapporter och dokument i en dokumentdatabas, till exempel Azure Cosmos DB, men använda Azure Cache for Redis för att cachelagra tillfälliga data.
Om du följer den här vägledningen men ändå når databasens begränsningar kanske du behöver skala upp databasen. Överväg även att skala vågrätt och partitionera belastningen över databasservrar. Men partitionering kan kräva en ny design för programmet. Mer information finns i Datapartitionering.
Identifiera problemet
Systemet blir troligen dramatiskt långsammare och misslyckas till sist, eftersom dess resurser, till exempel databasanslutningar, tar slut.
Du kan göra följande för att identifiera orsaken.
- Instrumentera systemet att registrera viktig prestandastatistik. Samla in tidsinformation för varje åtgärd och de punkter där programmet läser och skriver data.
- Övervaka, om möjligt, när systemet körs i en produktionsmiljö för att få en verklig bild av hur systemet används. Om det inte är möjligt kör du skriptbaserade belastningstest med en realistisk mängd virtuella användare som utför ett antal typiska åtgärder.
- Använd telemetridata för att identifiera perioder med sämre prestanda.
- Identifiera vilka datalager som har använts under de här perioderna.
- Identifiera datalagringsresurser där det kan finnas konkurrens.
Exempeldiagnos
I följande avsnitt används stegen på exempelprogrammet som beskrivs ovan.
Instrumentera och övervaka systemet
I följande diagram visas resultatet av belastningstest av exempelprogrammet som beskrivs ovan. Testet använder en stegbelastning på upp till 1 000 samtidiga användare.
När belastningen ökar till 700 användare ökar även dataflödet. Men då planar dataflödet ut och systemet verkar köras på maxkapacitet. Det genomsnittliga svaret ökar gradvis med användarbelastningen, vilket visar att systemet inte kan hålla jämna steg med behovet.
Identifiera perioder med sämre prestanda
Om du övervakar produktionssystemet kan du märka mönster. Till exempel kan svarstiderna bli betydligt längre vid samma tid varje dag. Det kan bero på en vanlig arbetsbelastning eller ett schemalagt batchjobb, eller bara för att systemet har fler användare på vissa tider. Du bör fokusera på telemetridata för de här händelserna.
Titta efter samband mellan ökade svarstider och ökad databasaktivitet eller I/O för delade resurser. Om det finns samband betyder det att databasen kan vara en flaskhals.
Identifiera vilka datalager som används under de här perioderna
Nästa diagram visar användningen av databasdataflödesenheter (DTU:er) under belastningstestet. (En DTU är ett mått på tillgänglig kapacitet och är en kombination av processoranvändning, minnesallokering, I/O-hastighet.) Användningen av DTU:er nådde snabbt 100 %. Det här är ungefär den punkt då dataflödet nådde sin topp i det tidigare diagrammet. Databasanvändningen var mycket hög tills testet avslutades. Det finns en liten minskning mot slutet, vilket kan orsakas av begränsning, konkurrens om databasanslutningar eller andra faktorer.
Granska datalagrens telemetri
Instrumentera datalagren för att registrera detaljerna på låg nivå för aktiviteten. I exempelprogrammet visade dataåtkomststatistiken en stor mängd infoga-åtgärder som utfördes mot både tabellen PurchaseOrderHeader
och tabellen MonoLog
.
Identifiera resurskonkurrens
Nu kan du granska källkoden, med fokus på de punkter där konkurrensutsatta resurser används av programmet. Titta efter följande situationer:
- Data som är logiskt olika skrivs till samma lager. Data som loggar, rapporter och köplacerade meddelanden bör inte lagras i samma databas som affärsinformation.
- Ett matchningsfel mellan valet av datalager och typen av data, till exempel stora blobbar eller XML-dokument i en relationsdatabas.
- Data med betydligt olika användningsmönster som delar samma lager, till exempel data med hög skrivning/låg läsning lagras med data med låg skrivning/hög läsning.
Implementera lösningen och verifiera resultatet
Programmet har ändrats till att skriva loggar till ett separat datalager. Här är belastningstestresultatet:
Dataflödets mönster liknar den tidigare diagrammet men den punkt då prestandan når sin topp är cirka 500 begäranden per sekund högre. Den genomsnittliga svarstiden är marginellt lägre. Men den här statistiken berättar inte hela historien. Telemetri för affärsdatabasen visar att DTU-användningen når sin topp runt 75 %, istället för 100 %.
Och den maximala DTU-användningen för loggdatabasen når bara cirka 70 %. Databaserna är inte längre den begränsande faktorn i systemets prestanda.