Desempenho de bancos de dados SQLite
Comparados com computadores, os aparelhos móveis fornecem um ambiente muito restrito para os bancos de dados SQLite. Para obter o desempenho ideal em um smartphone BlackBerry, considere as seguintes restrições:
O limite de memória dinâmica é 16 MB. Este limite refere-se à quantidade de RAM disponível para um banco de dados SQLite armazenar as estruturas de dados internas para esquemas e transações. O esquema de banco de dados inteiro é carregado na memória quando um banco de dados SQLite é aberto e persiste até que o banco de dados seja fechado.
O tamanho máximo de consulta SQL é 1 MB.
O número máximo de bancos de dados que podem estar abertos ao mesmo tempo é aproximadamente 50. A API Database usa um modo de cache compartilhado para fazer uso eficiente de memória para várias conexões ao mesmo banco de dados.
Somente uma conexão de leitura-gravação de banco de dados a um banco de dados SQLite pode ser feita por vez. Outras conexões de banco de dados são somente leitura.
Quando você usa os métodos Database.createBlobOutputStream e Database.createBlobInputStream para ler e gravar blobs, a memória disponível para o SQLite não restringe o tamanho do blob que você pode usar: seu conjunto de resultados não está limitado a 1 MB.
Você pode controlar uso de memória para cada conexão de banco de dados usando os seguintes métodos: Database.getCacheUsed, Database.getSchemaUsed e Database.getStatementUsed.
Entender o SQLite como um serviço
Em um smartphone BlackBerry, o SQLite é executado como um serviço. As operações de banco de dados usam uma ponte de tempo de execução para transferir dados entre Java e código nativo. Há um limite de 1 MB na quantidade de dados que pode ser transferida sobre a ponte de tempo de execução. A ponte de tempo de execução pode fornecer um excelente desempenho de operações de banco de dados, mas pode reduzir o desempenho caso não seja usada adequadamente.
Quando você cria uma instrução preparada para inserir ou atualizar dados, você deve usar os métodos Statement.executeInsert ou Statement.executeUpdate para reduzir o número de chamadas sobre a ponte de tempo de execução. Estes métodos executam as seguintes operações em código nativo:
- Unir parâmetros SQL
- Executar a instrução
- Redefinir a instrução
- Apagar as uniões
- Retornar o último ID de linha inserido (somente executeInsert)
Quando você executa uma consulta que não retorna um conjunto de resultados e você não une parâmetros, você deve usar o método Database.executeStatement, que reduz o número de chamadas sobre a ponte de tempo de execução realizando as seguintes operações em código nativo:
Quando você executa uma consulta que retorna conjuntos de resultados usando o método Statement.getCursor, você pode fazer uma busca prévia de um determinado número de linhas usando o método Statement.setCursorBufferSize. Esta abordagem reduz a utilização da ponte de tempo de execução. Quando o cursor passa pelo conjunto em buffer, um novo grupo de linhas é buscado automaticamente. Você pode recuperar um número de filas que um cursor realiza buffer com o método Statement.getCursorBufferSize.
Quando você recupera valores de número inteiro para usar como chaves em outra consulta, você pode usar os métodos Statement.getIntegers e Statement.getLongs. Estes métodos simplificam e otimizam a recuperação de colunas de números inteiros.
Prática recomendada: Otimizando o desempenho do banco de dados SQLite
Considere as seguintes diretrizes:
| Prática recomendada | Descrição |
|---|---|
Usar o modo apropriado de diário |
Por padrão, um diário de reversão é usado para registrar transações, mas você poder optar por usar um log de write-ahead em vez disso. O log de write-ahead fornece maior concorrência e é mais rápido que o diário de reversão na maioria dos casos porque a gravação para o banco de dados não bloqueia a leitura. No entanto, o log de write-ahead usa mais manipuladores de arquivo. O log de write-ahead usa até três manipuladores de arquivo que podem persistir por longos períodos, enquanto o diário de reversão usa um, ou de vez em quando dois, durante as transações de gravação. Para alterar o modo de geração de diário para um log de write-ahead, execute a instrução SQL "PRAGMA journal_mode = WAL;". |
Considere reduzir as restrições de segurança para tabelas |
Quando a integridade de dados não for uma preocupação, considere usar comandos Pragma para não especificar um diário e desativar o modo síncrono. Por exemplo, PRAGMA journal_mode=OFF e PRAGMA synchronous=OFF. |
Teste |
Se planeja criar um banco de dados com um esquema grande ou inserir blobs grandes, você deve testar o banco de dados em seus smartphones BlackBerry de destino para certificar-se de que os smartphones tenham memória adequada. |
Armazenar o mínimo de dados possível |
A maioria do tempo de processamento dos bancos de dados SQLite é dedicada a ler e gravar no armazenamento. Menos dados geralmente significa menos leitura e gravação. O mecanismo de banco de dados SQLite armazena em cache as páginas de banco de dados freqüentemente acessadas. Ao armazenar menos dados, você pode aumentar a probabilidade de o mecanismo de banco de dados SQLite recuperar dados solicitados mais rapidamente do cache, em vez de recuperar do acesso de armazenamento, que é relativamente lento. |
Agrupar instruções com transações explícitas |
Se você não usar transações explícitas, uma transação será criada para cada instrução que você executar. Este comportamento padrão é ineficiente. Requer que o arquivo de diário seja aberto, reaberto, editado e fechado para cada instrução. Com transações explícitas, você pode agrupar instruções para obter maior eficiência. Você pode executar várias instruções em uma transação colocando Database.beginTransaction e Database.commitTransaction ao redor de um grupo de instruções. |
Criar índices eficientes |
Os índices podem reduzir bastante o tempo requerido para varrer uma tabela. Aqui estão algumas observações sobre a criação de índices eficientes:
|
Minimizar o tamanho das linhas |
Se houver uma coluna muito larga, coloque-a em uma tabela separada. |
Armazenar blobs adequadamente |
Se seus dados incluem blobs, você deve ler e gravá-los usando os objetos InputStream e OutputStream usando createBlobInputStream e createBlobOutputStream. Você também pode considerar armazenar cada blob em uma tabela separada. Se os blobs forem muito grandes, você poderá armazená-los como arquivos fora do banco de dados (e armazenar o caminho para cada arquivo no banco de dados), mas esta prática introduz códigos extras para a busca de nomes de arquivos. |
Usar tabelas temporárias |
Se não precisar que os dados estejam disponíveis após reiniciar um smartphone, use a instrução CREATE TEMP TABLE em vez de CREATE TABLE. |
Usar parâmetros SQL |
Para executar um conjunto de instruções do mesmo formato, primeiro prepare uma instrução genérica que use parâmetros SQL. Você pode executar a instrução através da iteração pelos valores variáveis e vincular os valores às variáveis nomeadas em cada iteração. |
Evitar subconsultas |
Em alguns casos, o mecanismo de banco de dados SQLite armazena subconsultas que resultam em um arquivo temporário, o que pode retardar o processamento. |
Desfragmentar o banco de dados |
Use o comando VACUUM do SQLite para desfragmentar o banco de dados. Este processo também reduz o tamanho do arquivo do banco de dados. |
Considerar a ordem das colunas nas instruções em tabela |
A ordem das colunas em uma instrução em tabela afeta o desempenho, especialmente na ausência de um índice, porque o mecanismo de banco de dados SQLite varre as colunas na ordem definida na instrução em tabela. As colunas que contêm uma quantidade pequena de dados que são frequentemente acessados devem ser colocadas antes das colunas que contêm uma quantidade grande de dados que são acessados com menos frequência. |
Usar métodos de operação em massa |
Os seguintes métodos de operação em massa fazem uso eficiente da ponte de tempo de execução:
Para obter mais informações, consulte Entender o SQLite como um serviço. |
Estas informações foram úteis? Gostaríamos de receber seus comentários.