Design de aplicativo para desempenho de banco de dados

Não altere a propriedade text do objeto SQLStatement depois de executá-lo. Em vez disso, use uma instância de SQLStatement em cada instrução SQL e use os parâmetros da instrução para fornecer valores diferentes.

Antes de qualquer instrução SQL ser executada, o tempo de execução a prepara (compila) para determinar as etapas realizadas internamente para executar a instrução. Quando você chama SQLStatement.execute() em uma ocorrência de SQLStatement que não foi executada anteriormente, a instrução é preparada de forma automática antes de ser executada. Nas chamadas subsequentes do método execute() , desde que a propriedade SQLStatement.text não tenha sido alterada, a instrução ainda será preparada. Como resultado, ela será executada mais rapidamente.

Para aproveitar ao máximo a capacidade de reutilizar instruções, se os valores alterarem entre as execuções de uma instrução, utilize parâmetros de instrução para personalizar a sua instrução. (Os parâmetros de instrução são especificados através da propriedade de matriz associativa SQLStatement.parameters ). Diferentemente de quando se altera a propriedade text da ocorrência de SQLStatement, se você alterar os valores de parâmetros de instrução, o tempo de execução não terá de preparar a instrução novamente.

Quando você reutiliza uma ocorrência de SQLStatement, o aplicativo deve armazenar uma referência à ocorrência de SQLStatement depois que ela foi preparada. Para isso, declare a variável como uma variável com escopo de classe e não como uma variável com escopo de função. Uma boa maneira de fazer isso é estruturar o aplicativo para que a instrução SQL seja colocada em uma única classe. Um grupo de instruções que são executadas em combinação também pode ser colocado em uma única classe. (Essa técnica é conhecida por utilizar o padrão de design Command). Ao definir as instâncias de SQLStatement como variáveis da classe, elas persistirão enquanto a instância da classe wrapper existir no aplicativo. No mínimo, basta definir uma variável que contenha a ocorrência de SQLStatement fora de uma função para que a ocorrência persista na memória. Por exemplo, declare a ocorrência de SQLStatement como variável de membro em uma classe do ActionScript ou como variável não função em um arquivo JavaScript. Em seguida, você pode definir os valores de parâmetro da instrução e chamar o método execute() correspondente quando quiser executar a consulta.

Utilize os índices do banco de dados para melhorar a velocidade da execução para comparar e ordenar dados.

Quando você cria um índice para uma coluna, o banco de dados armazena uma cópia dos dados dessa coluna. A cópia é mantida ordenada em ordem numérica ou alfabética. A ordenação permite que o banco de dados faça rapidamente a correspondência dos valores (como ao utilizar o operador de igualdade) e ordene os dados dos resultados usando a cláusula ORDER BY .

Os índices de banco de dados são mantidos continuamente atualizados, o que faz com que as operações de atualização de dados (INSERT ou UPDATE) naquela tabela tornem-se um pouco mais lentas. No entanto, o aumento na velocidade da recuperação de dados pode ser significativo. Por causa dessa alteração de desempenho, você não deve simplesmente indexar cada coluna de cada tabela. Em vez disso, use uma estratégia para definir seus índices. Utilize as seguintes orientações para planejar a estratégia de indexação:

  • Colunas de índices utilizadas em tabelas conjuntas, em cláusulas WHERE ou cláusulas ORDER BY

  • Se as colunas são utilizadas em conjunto com frequência, indexe-as em conjunto em um índice simples

  • Em uma coluna que contém dados de texto que você recuperou ordenada por ordem alfabética, especifique o agrupamento COLLATE NOCASE para o índice

Considere as instruções SQL de pré-compilação durante os tempos inativos do aplicativo .

Na primeira vez que uma instrução SQL é executada, ela é mais lenta porque o texto SQL é preparado (compilado) pelo mecanismo do banco de dados. Visto que preparar e executar uma instrução pode ser uma operação trabalhosa, uma boa estratégia é pré-carregar os dados iniciais e então executar outras instruções em segundo plano:

  1. Carregue os dados de que o aplicativo precisa primeiro.

  2. Quando as primeiras operações de inicialização do aplicativo forem concluídas, ou durante algum período de “inatividade” no aplicativo, execute outras instruções.

Por exemplo, suponha que o aplicativo não acessa o banco de dados para exibir a tela inicial. Nesse caso, aguarde até que a tela exiba antes de abrir a conexão do banco de dados. Finalmente, crie as ocorrências SQL e execute as ocorrências que conseguir.

Como alternativa, suponha que, quando o seu aplicativo é inicializado, ele imediatamente exibe alguns dados, como o resultado de uma determinada consulta. Nesse caso, vá em frente e execute a ocorrência de SQLStatement referente a essa consulta. Depois que os dados iniciais forem carregados e exibidos, crie ocorrências de SQLStatement para outras operações de banco de dados e, se possível, execute outras instruções que serão necessárias posteriormente.

Na prática, se você estiver reutilizando ocorrências de SQLStatement, o tempo adicional para preparar a instrução corresponde apenas a custos iniciais. Provavelmente não tem grande impacto no desempenho global.

Operações de alterações de dados SQL em múltiplos grupos em uma transação.

Suponha que você está executando muitas instruções SQL que envolvem a inclusão ou a alteração de dados (instruções INSERT ou UPDATE ). Você pode conseguir um aumento de desempenho considerável se executar todas as instruções em uma transação explícita. Se você não iniciar uma transação de forma explícita, cada uma das instruções será executada em sua própria transação criada automaticamente. Depois que a execução de cada transação (cada instrução) for concluída, o tempo de execução gravará os dados resultantes no arquivo de banco de dados do disco.

Por outro lado, pense no que acontece se você cria uma transação explicitamente e executa as instruções no contexto dessa transação. O tempo de execução faz todas as alterações na memória e, depois, grava todas as alterações no arquivo de banco de dados de uma só vez quando a transação é confirmada. Gravar dados em disco normalmente é a parte mais demorada da operação. Como consequência, gravar no disco de uma só vez e não uma vez por instrução SQL pode melhorar o desempenho significativamente.

O processamento de consultas SELECT grandes resulta em partes utilizando o método execute() da classe SQLStatement (com parâmetro prefetch ) e o método next() .

Suponha que você executa uma instrução SQL que recupera um conjunto de resultados grande. O aplicativo então processa cada fila de dados em um loop. Por exemplo, formata os dados ou cria objetos a partir dele. O processamento desses dados pode demorar muito tempo, o que faz com ocorram problemas de renderização como, por exemplo, tela congelada ou não responsiva. Conforme descrito em Operações assíncronas , uma solução é dividir o trabalhos em blocos. A API do banco de dados SQL facilita a divisão do processamento de dados.

O método execute() da classe SQLStatement tem um parâmetro prefetch opcional (o primeiro parâmetro) Se fornecer o valor, o parâmetro especifica o número máximo de filas de resultado que o banco de dados retorna quando a execução completa:

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

Depois que o primeiro conjunto de dados de resultados retorna, você pode chamar o método next para continuar a executar a instrução e recuperar outro conjunto de filas de resultados. De forma semelhante ao método execute() , o método next aceita o parâmetro prefetch para especificar o número máximo de filas a retornar:

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

Você pode continuar chamando o método next() até que os dados carreguem. Conforma mostra a listagem anterior, é possível determinar quando os dados foram todos carregados. Verifique a propriedade complete do objeto SQLResult criada cada vez que o método execute() ou next() termina.

Nota: Use o parâmetro prefetch e o método next() para dividir o processamentos dos dados de resultado. Não use este parâmetro e este método para limitar os resultados de uma pesquisa a uma parte do seu conjunto de resultados. Se quiser recuperar um subconjunto de linhas em uma definição de resultado de uma instrução, use a cláusula LIMIT da instrução SELECT . Se o conjunto de resultados for grande, ainda é possível utilizar o parâmetro prefetch e o método next() para dividir o processamento dos resultados.
Considere a utilização de múltiplos objetos SQLConnection assíncronos com um banco de dados simples para executar múltiplas instruções simultaneamente..

Quando o objeto SQLConnection é conectado a um banco de dados utilizando o método openAsync() , é executado em segundo plano em vez de no encadeamento da execução do tempo de execução principal. Além disso, cada conexão SQL executa seu próprio encadeamento em segundo plano. Ao usar múltiplos objetos SQLConnection, você pode efetivamente executar múltiplas instruções SQL simultaneamente.

Existem também potenciais desvantagens nessa abordagem. Mais significativamente, cada objeto SQLStatement adicional requer memória adicional. Além disso, execuções simultâneas também levam o processador a trabalhar mais, principalmente, em máquinas que têm somente uma CPU ou núcleo da CPU. Devido a essas questões, não se recomenda essa abordagem para utilização em dispositivos portáteis.

Uma questão adicional é que o potencial benefício de reutilizar objetos SQLStatement pode se perder porque um objeto SQLStatement está ligado a um objeto SQLConnection simples. Consequentemente, não é possível reutilizar o objeto SQLStatement, se seu objeto SQLConnection associado já está em uso.

Se escolher utilizar múltiplos objetos SQLConnection conectados a um banco de dados simples, tenha em mente que cada um executa suas instruções em sua própria transação. Certifique-se de considerar estas transações separadas em qualquer código que altere dados, tal como adicionar, modificar ou excluir dados.

Paul Robertson criou uma biblioteca de código-fonte aberta que ajuda você a incorporar os benefícios de utilizar múltiplos SQLConnection enquanto diminui potenciais desvantagens. A biblioteca utiliza um pool de objetos SQLConnection e gerencia os objetos SQLStatement associados. Dessa maneira, assegura que os objetos SQLStatement sejam reutilizados e múltiplos objetos SQLConnection estejam disponíveis para executar múltiplas declarações simultaneamente. Para mais informações e baixar a biblioteca, visite http://probertson.com/projects/air-sqlite/ .