Programdesign för databasprestanda

Ändra inte ett SQLStatement-objekts text -egenskap när det har verkställts. Använd i stället en SQLStatement-instans för varje SQL-sats och satsparametrar för att ange olika värden.

Innan en SQL-sats körs förbereds (kompileras) den i körningen för att avgöra vilka steg som ska användas internt för att utföra satsen. När du anropar SQLStatement.execute() på en SQLStatement-instans som inte har körts tidigare förbereds satsen automatiskt innan den körs. För efterföljande anrop till metoden execute() förbereds satsen fortfarande, såvida inte egenskapen SQLStatement.text har ändrats. Därmed körs den snabbare.

För att dra störst nytta av att återanvända satser bör du använda satsparametrar för att anpassa din sats om värden ändras mellan satskörningar. (Satsparametrar anges med egenskapen SQLStatement.parameters (associativ array).) Om du ändrar värdena för satsparametrarna behöver inte satsen förberedas på nytt i körningen, till skillnad från när du ändrar SQLStatement-instansens text -egenskap.

När du återanvänder en SQLStatement-instans måste en referens till SQLStatement-instansen sparas i programmet när den har förberetts. Detta gör du genom att deklarera variabeln som en klassomfångsvariabel i stället för en funktionsomfångsvariabel. Ett bra sätt att göra SQLStatement till en klassomfångsvariabel är att strukturera programmet så att en SQL-sats placeras i en enskild klass. En grupp satser som körs i kombination kan också placeras i en enskild klass. (Tekniken kallas för ett Command-designmönster.) Om du definierar instanserna som medlemsvariabler i klassen finns de kvar så länge instansen av klassen wrapper finns i programmet. Du kan, som ett minimum, definiera en variabel som innehåller SQLStatement-instansen utanför en funktion så att instansen finns kvar i minnet. Du kan till exempel deklarera SQLStatement-instansen som en medlemsvariabel i en ActionScript-klass eller som en icke-funktionsvariabel i en JavaScript-fil. Du kan sedan ange satsens parametervärden och anropa dess execute() -metod när du vill köra frågan.

Använd databasindex för att förbättra körhastigheten när data jämförs och sorteras.

När du skapar ett index för en kolumn lagrar databasen en kopia av data i kolumnen. Kopian hålls sorterad i numerisk eller alfabetisk ordning. Det gör att databasen snabbt kan matcha värden (t.ex. när likhetsoperatorn används) och sortera resulterande data med ORDER BY -satsen.

Databasindex uppdateras kontinuerligt vilket gör att dataändringar (INSERT eller UPDATE) utförs en aning långsammare i tabellen. Hastigheten vid datahämtning kan dock öka avsevärt. På grund av denna prestandakompromiss bör du inte indexera varenda kolumn i alla tabeller. Använd i stället en strategi när du definierar index. Använd riktlinjerna nedan när du planerar en strategi för indexering:

  • Indexera kolumner som används i kopplade tabeller efter WHERE-satser eller ORDER BY-satser

  • Indexera kolumner som ofta används tillsammans som ett index

  • Ange COLLATE NOCASE-kollation för ett index när en kolumn som innehåller textdata hämtas sorterad i alfabetisk ordning

Överväg att kompilera SQL-satser i förväg när programmet är inaktivt.

Den första gången som en SQL-sats verkställs, tar det längre tid eftersom SQL-texten förbereds (kompileras) av databasmotorn. Eftersom det kan vara krävande att förbereda och köra en sats är en strategi att läsa in initiala data i förväg och sedan köra övriga satser i bakgrunden:

  1. Läs först in data som behövs i programmet.

  2. Kör de övriga satserna när de initiala startåtgärderna för programmet har slutförts eller vid något inaktivt läge i programmet.

Anta t.ex. att programmet inte alls behöver åtkomst till databasen för att visa den inledande skärmen. I sådana fall kan du vänta tills skärmen visas innan databasanslutningen öppnas. Skapa till sist SQLStatement-instanser och verkställ alla som du kan.

Eller anta att vissa data visas direkt när programmet startar, till exempel resultatet av en viss fråga. I så fall är det bara att köra SQLStatement-instansen för den frågan. När initiala data har lästs in och visats kan du skapa SQLStatement-instanser för andra databasåtgärder och, om möjligt, köra andra satser som behövs senare.

I praktiken är den extra tiden som krävs att förbereda satsen en engångskostnad när SQLStatement-instanser återanvänds. Det har förmodligen inte någon större inverkan på den övergripande prestandan.

Gruppera flera SQL dataändringsåtgärder som en transaktion.

Anta att du kör många SQL-satser som innefattar tillägg eller ändringar av data ( INSERT - eller UPDATE -satser). I så fall kan du öka prestanda markant genom att köra alla satser i en explicit transaktion. Om du inte börjar en transaktion explicit körs var och en av satserna i en egen transaktion som skapas automatiskt. När körningen av varje transaktion (varje sats) har slutförts skrivs alla resulterande data till databasfilen på disken.

Å andra sidan bör du även tänka på vad som händer om du skapar en transaktion explicit och kör satserna i kontexten för den transaktionen. Vid körningen görs alla ändringar i minnet och alla ändringar skrivs sedan på en gång till databasfilen när transaktionen har verkställts. Att skriva data till disk är vanligtvis den mest tidskrävande delen av åtgärden. Därför förbättras prestanda markant om skrivningen till disk endast sker en gång i stället för en gång per SQL-sats.

Bearbeta stora SELECT -frågeresultat i delar med SQLStatement-klassens execute() -metod (med parametern prefetch ) och next() -metoden.

Anta att du verkställer en SQL-sats som hämtar en stor resultatuppsättning. Programmet bearbetar sedan varje datarad i en slinga. Data formateras eller objekt skapas till exempel. Att bearbeta dessa data kan ta lång tid vilket kan leda till återgivningsproblem, t.ex. att skärmen fryser eller inte svarar. Så som beskrivs i Asynkrona åtgärder , är en lösning att dela upp arbetet i segment. SQL-databasens API gör det lätt att dela upp databearbetningen.

SQLStatement-klassens execute() -metod har en valfri prefetch -parameter (den första parametern). Om du tillhandahåller ett värde anger den det maximala antalet resultatrader som databasen returnerar när körningen är klar:

dbStatement.addEventListener(SQLEvent.RESULT, resultHandler); 
dbStatement.execute(100); // 100 rows maximum returned in the first set

När den första uppsättningen resultatdata returneras kan du anropa next() -metoden för att fortsätta att verkställa satsen och hämta ytterligare en uppsättning resultatrader. Precis som execute() -metoden, accepterar next() -metoden en prefetch -parameter som anger maximalt antal rader som ska returneras:

// This method is called when the execute() or next() method completes 
function resultHandler(event:SQLEvent):void 
{ 
    var result:SQLResult = dbStatement.getResult(); 
    if (result != null) 
    { 
        var numRows:int = result.data.length; 
        for (var i:int = 0; i < numRows; i++) 
        { 
            // Process the result data 
        } 
         
        if (!result.complete) 
        { 
            dbStatement.next(100); 
        } 
    } 
}

Du kan fortsätta att anropa next() -metoden tills alla data har lästs in. Så som visades tidigare kan du avgöra när alla data har lästs in. Kontrollera egenskapen complete för SQLResult-objektet som skapas varje gång metoden execute() eller next() avslutas.

Obs! Använd parametern prefetch och metoden next() när du ska dela upp bearbetning av resultatdata. Använd inte parametern och metoden när du ska begränsa resultatet av en fråga till en del av resultatuppsättningen. Om du bara vill hämta en delmängd av rader i resultatuppsättningen för en sats ska du använda LIMIT -satsen i satsen SELECT . Om resultatuppsättningen är stor kan du fortfarande använda prefetch -parametern och next() -metoden för att dela upp resultatbearbetningen.
Överväg att använda flera asynkrona SQLConnection-objekt med en databas för att verkställa flera satser samtidigt.

När ett SQLConnection-objekt ansluts till en databas med metoden openAsync() , körs det i bakgrunden i stället för i den huvudsakliga körningstråden. Dessutom kör varje SQLConnection sin egen bakgrundstråd. Genom att använda flera SQLConnection-objekt kan du effektivt köra flera SQL-satser samtidigt.

Det finns vissa potentiella nackdelar med denna metod. Den viktigaste är att varje extra SQLStatement-objekt kräver mer minne. Samtidig körning kan även innebära mer arbete för processorn, speciellt på maskiner som bara har en CPU eller CPU-kärna. På grund av detta rekommenderas inte metoden för användning på mobila enheter.

Ytterligare en nackdel är att den potentiella fördelen med att återanvända SQLStatement-objekt kan förloras eftersom ett SQLStatement-objekt är länkat till ett enda SQLConnection-objekt. Det innebär att SQLStatement-objektet inte kan återanvändas om dess associerade SQLConnection-objekt redan används.

Om du väljer att använda flera SQLConnection-objekt anslutna till en databas bör du tänka på att varje objekt verkställer satser i sin egen transaktion. Kom ihåg att ta dessa separata transaktioner med i beräkningen i kod som ändrar data, t.ex. när data läggs till, ändras eller tas bort.

Paul Robertson har skapat ett bibliotek med öppen källkod som hjälper dig att dra nytta av fördelarna med att använda flera SQLConnection-objekt samtidigt som de potentiella nackdelarna minimeras. Biblioteket använder en pool av SQLConnection-objekt och hanterar associerade SQLStatement-objekt. Det ser till att SQLStatement-objekt återanvänds och flera SQLConnection-objekt är tillgängliga för att verkställa flera satser samtidigt. Gå till http://probertson.com/projects/air-sqlite/ om du vill ha mer information eller vill hämta biblioteket.