Ontwerp van toepassingen voor databaseprestaties

Wijzig de eigenschap text van een object SQLStatement na het uitvoeren niet meer. Gebruik in plaats daarvan één instantie SQLStatement voor elke SQL-instructie en gebruik instructieparameters voor de verschillende waarden.

Voordat een SQL-instructie wordt uitgevoerd, wordt deze door de runtime voorbereid (gecompileerd) om de stappen te bepalen die intern moeten worden uitgevoerd om de instructie uit te voeren. Als u SQLStatement.execute() aanroept voor een SQLStatement-instantie die nog niet is·uitgevoerd, wordt de instructie automatisch voorbereid voordat deze wordt uitgevoerd. De volgende keren dat u de methode execute() aanroept, is de instructie al voorbereid, mits de eigenschap SQLStatement.text niet is gewijzigd. Als gevolg daarvan wordt deze sneller uitgevoerd.

Als u het maximale voordeel wilt halen uit het hergebruik van instructies, en waarden tussen de uitvoering van instructies door gewijzigd moeten worden, gebruikt u instructieparameters om uw instructie aan te passen. (U geeft instructieparameters op via de associatieve-array-eigenschap SQLStatement.parameters .) Bij het wijzigen van de eigenschap text van de SQLStatement-instantie moet de runtime de instructie opnieuw voorbereiden. Als u de waarden van instructieparameters wijzigt, is dit niet het geval.

Als u een SQLStatement-instantie opnieuw gebruikt, moet uw toepassing een verwijzing naar de SQLStatement-instantie opslaan wanneer deze is voorbereid. Hiervoor definieert u de variabele als een variabele van het type ‘klasse’ en niet van het type ‘functie’. Als u de SQLStatement-instantie als variabele van het type klasse wilt definiëren, kunt u dit het beste doen door de toepassing zo te structureren dat een SQL-instructie aan één klasse wordt toegewezen. U kunt ook een groep instructies die gecombineerd worden uitgevoerd toewijzen aan één klasse. (Deze techniek wordt het command design-patroon genoemd.) Wanneer u de instanties als lidvariabelen van de klasse definieert, blijven ze bestaan zolang de instantie van de klasse waaraan ze zijn toegewezen in de toepassing bestaat. Als minimale oplossing kunt u gewoon een variabele definiëren die de SQLStatement-instantie buiten een functie bevat zodat de instantie in het geheugen blijft bestaan. Definieer de SQLStatement-instantie bijvoorbeeld als een lidvariabele in een ActionScript-klasse of als een niet-functievariabele in een JavaScript-bestand. Vervolgens kunt u de parameterwaarden van de instructie instellen en de methode execute() van de instructie aanroepen wanneer u de query daadwerkelijk wilt uitvoeren.

Gebruik database-indexeringen om het vergelijken en sorteren van gegevens sneller uit te voeren.

Wanneer u een index voor een kolom maakt, slaat de database een kopie van de gegevens van die kolom op. De kopie wordt alfabetisch of numeriek gesorteerd opgeslagen. De sortering zorgt ervoor dat de database snel waarden kan zoeken (bijvoorbeeld met de operator voor gelijkheid) en de gevonden gegevens kan sorteren met de component ORDER BY .

Database-indexen worden voortdurend vernieuwd, waardoor bewerkingen waarmee gegevens worden gewijzigd (INSERT of UPDATE) voor die tabel enigszins langzamer verlopen. Het ophalen van gegevens kan echter wel veel sneller zijn. Ondanks deze netto prestatiewinst is het niet verstandig om zomaar elke kolom van elke tabel te indexeren. Hanteer in plaats daarvan een strategie voor het definiëren van indexen. Gebruik de volgende richtlijnen om uw indexeringsstrategie te plannen:

  • Indexeer alleen kolommen die worden gebruikt in samengevoegde tabellen, WHERE-componenten·of ORDER BY-componenten

  • Als kolommen vaak tezamen worden gebruikt, indexeert u deze tezamen in één index

  • Als een kolom tekstgegevens bevat die alfabetisch worden opgehaald, geeft u als sortering COLLATE NOCASE voor de index op

U kunt SQL-instructies ook vooraf compileren op momenten dat de toepassing niet actief is.

De eerste keer wordt een SQL-instructie langzamer uitgevoerd omdat de SQL-tekst moet worden voorbereid (gecompileerd) door de database-engine. Aangezien het voorbereiden en uitvoeren van een instructie zeer veeleisend kan zijn, is het verstandig om de initiële gegevens vooraf te laden en vervolgens andere instructies op de achtergrond uit te voeren:

  1. Laad de gegevens die de toepassing het eerst nodig heeft.

  2. Nadat het initiële opstarten van uw toepassing is voltooid, of tijdens een andere periode van inactiviteit binnen de toepassing, voert u andere instructies uit.

Stel dat uw·toepassing bij het weergeven van het beginscherm helemaal geen verbinding maakt met de database. Wacht in dat geval totdat het scherm wordt weergegeven voordat u de databaseverbinding opent. Maak tot slot de SQLStatement-instanties en voer deze waar mogelijk uit.

Het is echter ook mogelijk dat uw toepassing bij het opstarten onmiddellijk bepaalde gegevens weergeeft, zoals het resultaat van een query. In dat geval voert u de SQLStatement-instantie voor de desbetreffende query uit. Nadat de initiële gegevens zijn geladen en weergegeven, maakt·u SQLStatement-instanties voor andere databasebewerkingen en voert u indien mogelijk later andere instructies uit die nodig zijn.

Als u SQLStatement-instanties hergebruikt, is de extra tijd die het voorbereiden van de instructie kost slechts eenmalig. Dit heeft waarschijnlijk geen grote gevolgen voor de algehele prestaties.

Groepeer meerdere bewerkingen waarin SQL-gegevens worden gewijzigd in een transactie.

Stel dat u een groot aantal SQL-instructies uitvoert waarbij gegevens worden toegevoegd of gewijzigd ( INSERT - of UPDATE -instructies). U kunt een aanzienlijke prestatieverbetering verkrijgen door alle instructies binnen een expliciete transactie uit te voeren. Als u niet expliciet een transactie begint, wordt elke instructie binnen zijn eigen, automatisch gegenereerde transactie uitgevoerd. Nadat elke transactie (elke instructie) is voltooid, schrijft de runtime de resultaatgegevens naar het databasebestand op de schijf.

Anderzijds moet u rekening houden met wat er gebeurt als u expliciet een transactie genereert en de instructies in het kader van die transactie uitvoert. De runtime voert alle wijzigingen in het geheugen uit en schrijft ze vervolgens alle tegelijk naar het databasebestand wanneer de transactie wordt bevestigd. Het naar schijf schrijven van gegevens is doorgaans het meest tijdsintensieve deel van de bewerking. Dit betekent dat u de prestaties aanzienlijk kunt verbeteren door de gegevens alle tegelijk naar schijf te schrijven in plaats van één keer per SQL-instructie.

Verwerk de grote hoeveelheden resultaten van SELECT -query's in delen met behulp van de methode execute() (met de parameter prefetch ) en de methode· next() van de klasse SQLStatement.

Stel dat u een SQL-instructie uitvoert waarmee een grote resultaatset·wordt opgehaald. De toepassing verwerkt elke rij gegevens in een lusbewerking. De gegevens worden bijvoorbeeld opgemaakt of er worden objecten van gemaakt. Het verwerken van zulke gegevens kan lang duren, waardoor er renderingproblemen kunnen optreden zoals een vastgelopen scherm. Zoals beschreven in Asynchrone bewerkingen kunt u het werk splitsen. Dankzij de API van de SQL-database is het heel eenvoudig om gegevensverwerking te splitsen.

De methode execute() van de klasse SQLStatement heeft een optionele parameter prefetch (de eerste parameter). Als u een waarde opgeeft, bepaalt u het maximumaantal resultaatrijen dat door de database wordt geretourneerd wanneer de verwerking is voltooid:

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

Wanneer de eerste serie resultaten wordt geretourneerd, kunt u de methode next() aanroepen om de uitvoering van de instructie voort te zetten en de volgende serie resultaatrijen op te halen. Net zoals de methode execute() accepteert de methode next() een parameter prefetch waarmee u kunt aangeven hoeveel rijen er maximaal worden geretourneerd:

// 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); 
        } 
    } 
}

U kunt de methode next() blijven aanroepen totdat alle gegevens zijn geladen. Zoals hierboven aangegeven kunt u bepalen wanneer alle gegevens zijn geladen. Controleer de eigenschap complete van het object SQLResult dat elke keer dat de methode execute() of next() wordt voltooid wordt gemaakt.

Opmerking: Gebruik de parameter prefetch en de methode next() om de verwerking van resultaatgegevens te splitsen. Gebruik deze parameter en methode niet om de resultaten van een query te beperken tot een gedeelte van de resultaatset. Als u slechts een beperkt aantal rijen van de resultaatset van een instructie wilt ophalen. gebruikt u de component LIMIT van de instructie SELECT . Ook als er sprake is van een grote resultaatset kunt u met de parameter prefetch en de methode next() de verwerking van de resultaten splitsen.
Gebruik meerdere asynchrone objecten SQLConnection in combinatie met één database om meerdere instructies tegelijk uit te voeren.

Wanneer een object SQLConnection door middel van de methode openAsync() met een database wordt verbonden, wordt het object·op de achtergrond uitgevoerd en niet in de belangrijkste uitvoeringsthread van de runtime. Daarnaast voert elke SQLConnection een eigen achtergrondthread uit. Als u meerdere objecten SQLConnection gebruikt, kunt u effectief meerdere SQL-instructies tegelijk uitvoeren.

Deze benadering heeft mogelijk ook een paar negatieve effecten. Ten eerste is er voor elk extra object SQLStatement extra geheugen nodig. Daarnaast vormt het gelijktijdig uitvoeren van bewerkingen een zwaardere belasting voor de processor, vooral op computers met slechts één CPU of CPU-core. Om deze redenen is deze benadering niet geschikt voor gebruik op mobiele apparaten.

Bovendien kan het potentiële voordeel van het hergebruik van objecten SQLStatements verloren gaan wanneer een object SQLStatement aan één object SQLConnection wordt gekoppeld. Hierdoor kan het object SQLStatement niet worden hergebruikt als het bijbehorende object SQLConnection al wordt gebruikt.

Wanneer er meerdere objecten SQLConnection met één database zijn verbonden, voert elk object zijn instructies in een eigen transactie uit. Zorg ervoor dat u deze afzonderlijke transacties verwerkt in code waarmee gegevens worden gewijzigd, zoals toevoegen, wijzigen of verwijderen.

Paul Robertson heeft een opensource codebibliotheek gemaakt waarmee u de voordelen van het gebruik van meerdere objecten SQLConnection kunt benutten en tegelijkertijd de mogelijke negatieve effecten tot een minimum kunt beperken. De bibliotheek maakt gebruik van·een groep objecten SQLConnection en beheert de bijbehorende objecten SQLStatement. Op deze manier wordt ervoor gezorgd dat de objecten SQLStatement worden hergebruikt en dat er meerdere objecten SQLConnection beschikbaar zijn voor het gelijktijdig uitvoeren van meerdere instructies. Ga naar http://probertson.com/projects/air-sqlite/ voor meer informatie en om de bibliotheek te downloaden.