SQLite
 sql >> Base de Dados >  >> RDS >> SQLite

Ajuste de desempenho do SQLite


SQLite é um banco de dados relacional popular que você incorpora em seu aplicativo. Com uma quantidade crescente de dados em seu banco de dados, você precisa aplicar o ajuste de desempenho do SQLite. Este artigo discute índices e suas armadilhas, o uso do planejador de consulta, o modo de diário Write-Ahead-Logging (WAL) e o aumento do tamanho do cache. Ele também explica a importância de medir o impacto de seus ajustes, usando testes automatizados.

Introdução


SQLite é um sistema de banco de dados relacional (DB) popular . Ao contrário de seus irmãos maiores, baseados em cliente-servidor, como o MySQL, o SQLite pode ser incorporado ao seu aplicativo como uma biblioteca . O SQLite tem um conjunto de recursos muito semelhante e também pode lidar com milhões de linhas, desde que você conheça algumas dicas e truques sobre ajuste de desempenho. Como as seções a seguir mostrarão, há mais para saber sobre o ajuste de desempenho do SQLite do que apenas criar índices.

Crie índices, mas com cautela


A ideia básica de um índice é acelerar a leitura de dados específicos , ou seja, SELECT declarações com um WHERE cláusula. Os índices também aceleram a classificação dados (ORDER BY ), ou JOIN tabelas. Infelizmente, os índices são uma faca de dois gumes, pois consomem espaço adicional em disco e retardam a manipulação de dados (INSERT , UPDATE , DELETE ).

O conselho geral é criar o menor número possível de índices, mas quantos forem necessários . Além disso, os índices só fazem sentido para maiores bancos de dados, com milhares ou milhões de linhas.

Use o planejador de consultas para analisar suas consultas


A forma como os índices são usados ​​internamente pelo SQLite são documentados, mas não muito fáceis de entender. Conforme detalhado neste artigo, é uma boa ideia analisar uma consulta prefixando-a com EXPLAIN QUERY PLAN . Dê uma olhada em cada linha de saída, das quais existem três variantes básicas:
  • SEARCH table ... linhas são um bom sinal – o SQLite usa um de seus índices!
  • SCAN table ... USING INDEX é um mau sinal,
  • SCAN table ... é ainda pior!

Tente evitar SCAN table [using index] entradas na saída de EXPLAIN QUERY PLAN sempre que possível, porque você terá problemas de desempenho em bancos de dados maiores. Use EXPLAIN QUERY PLAN para iterativo adicionar ou modificar seus índices até que não haja mais SCAN table entradas aparecem.

Otimizar consultas que envolvem IS NOT


Verificando IS NOT ... é caro porque o SQLite terá que digitalizar todas as linhas da tabela, mesmo que a coluna afetada tenha um índice . Os índices só são úteis se você procurar valores específicos, ou seja, comparações envolvendo < (menor), > (maior) ou = (igual), mas eles não se aplicam a !=(desigual).

Um pequeno truque legal é que você pode substituir a coluna WHERE column != value com WHERE column > value OR column < value . Isso usará o índice da coluna e afetará efetivamente todas as linhas cujo valor não seja igual a value . Da mesma forma, um WHERE stringColumn != '' pode ser substituído por WHERE stringColumn > '' , porque as strings são classificáveis. No entanto, ao aplicar esse truque, certifique-se de saber como o SQLite lida com NULL comparações. Por exemplo, o SQLite avalia NULL > '' como FALSE .

Se você usar esse truque de comparação, há outra advertência caso sua consulta contenha WHERE e ORDER BY , cada um com uma coluna diferente:isso tornará a consulta ineficiente novamente. Se possível, use o mesmo coluna em WHERE e ORDER BY , ou crie um índice de cobertura que envolve tanto o WHERE e ORDER BY coluna.

Melhore a velocidade de gravação com o Write-Ahead-Log


O Write-Ahead-Logging (WAL) o modo diário melhora significativamente o desempenho de gravação/atualização , em comparação com o padrão reversão modo diário. No entanto, conforme documentado aqui, há algumas ressalvas . Por exemplo, o modo WAL não está disponível em determinados sistemas operacionais. Além disso, há garantias de consistência de dados reduzidas em caso de falha de hardware . Certifique-se de ler as primeiras páginas para entender o que você está fazendo.

Descobri que o comando PRAGMA synchronous = NORMAL fornece uma aceleração de 3-4x. Configurando journal_mode para WAL em seguida, melhora o desempenho novamente significativamente (aproximadamente 10x ou mais, dependendo do sistema operacional).

Além das advertências que já mencionei, você também deve estar ciente do seguinte:
  • Usando o modo de diário WAL, haverá dois arquivos adicionais ao lado do arquivo de banco de dados em seu sistema de arquivos, que têm o mesmo nome do banco de dados, mas com o sufixo “-shm” e “-wal”. Normalmente você não precisa se preocupar, mas se você for enviar o banco de dados para outra máquina enquanto seu aplicativo estiver em execução, não se esqueça de incluir esses dois arquivos. O SQLite compactará esses dois arquivos no arquivo principal sempre que você normalmente fechar todas as conexões de banco de dados abertas.
  • O desempenho de inserção ou atualização cairá ocasionalmente, sempre que a consulta acionar a mesclagem do conteúdo do arquivo de log do WAL no arquivo de banco de dados principal. Isso é chamado de ponto de verificação , veja aqui.
  • Descobri que PRAGMA s que alteram journal_mode e synchronous não parecem ser armazenados persistentemente no banco de dados. Assim, eu sempre execute-os novamente sempre que eu abrir uma nova conexão de banco de dados, em vez de apenas executá-los ao criar as tabelas pela primeira vez.

Meça tudo


Sempre que você adicionar ajustes de desempenho, certifique-se de medir o impacto. Os testes automatizados (unitários) são uma ótima abordagem para isso. Eles ajudam a documentar suas descobertas para sua equipe, e eles descobrirão comportamentos desviantes ao longo do tempo , por exemplo. quando você atualiza para uma versão mais recente do SQLite. Exemplos do que você pode medir:
  • Qual ​​é o efeito de usar o WAL modo diário sobre o reversão modo? Qual é o efeito de outros (supostamente) PRAGMA que melhoram o desempenho s?
  • Depois de adicionar/alterar/remover um índice, quanto mais rápido SELECT declarações se tornam? Quanto mais lento INSERT/DELETE/UPDATE declarações se tornam?
  • Quanto espaço em disco adicional os índices consomem?

Para qualquer um desses testes, considere repeti-los com tamanhos de banco de dados variados. Por exemplo. execute-os em um banco de dados vazio e também em um banco de dados que já contém milhares (ou milhões) de entradas. Você também deve executar os testes em diferentes dispositivos e sistemas operacionais, especialmente se seu ambiente de desenvolvimento e produção for substancialmente diferente.

Ajuste o tamanho do cache


SQLite armazena informações temporárias em um cache (na RAM), e. ao construir os resultados de um SELECT consulta ou ao manipular dados que ainda não foram confirmados. Por padrão, esse tamanho é de apenas 2 MB . As máquinas desktop modernas podem poupar muito mais. Execute PRAGMA cache_size = -kibibytes para aumentar esse valor (cuidado com o menos assine na frente do valor!). Veja aqui para mais informações. Novamente, meça que impacto esta configuração tem no desempenho!

Use REPLACE INTO para criar ou atualizar uma linha


Isso pode não ser tanto um ajuste de desempenho, pois é um pequeno truque. Suponha que você precise atualizar uma linha na tabela t , ou criar uma linha se ela ainda não existir. Em vez de usar duas consultas (SELECT seguido por INSERT ou UPDATE ), use o comando REPLACE INTO (documentos oficiais).

Para que isso funcione, adicione uma coluna fictícia adicional (por exemplo, replacer ) para a tabela t , que tem um UNIQUE restringir. A declaração da coluna pode, por exemplo, seja ... replacer INTEGER UNIQUE ... que faz parte do seu CREATE TABLE demonstração. Em seguida, use uma consulta como
REPLACE INTO t (col1, col2, ..., replacer) VALUES (?,?,...,1)Code language: SQL (Structured Query Language) (sql)

Quando esta consulta for executada pela primeira vez, ela simplesmente executará um INSERT . Quando é executado pela segunda vez, o UNIQUE restrição do replacer será acionada e o comportamento de resolução de conflitos fará com que a linha antiga seja descartada, criando uma nova automaticamente. Você também pode achar útil o comando UPSERT relacionado.

Conclusão


Quando o número de linhas em seu banco de dados aumenta, os ajustes de desempenho se tornam uma necessidade. Os índices são a solução mais comum. Eles trocam a complexidade de tempo aprimorada por complexidade de espaço reduzida, melhorando as velocidades de leitura, enquanto afetam negativamente o desempenho da modificação de dados. A que demonstrei, você precisa ter muito cuidado ao comparar desigualdade em SELECT instruções, porque o SQLite não pode usar índices para esse tipo de comparação. Eu geralmente recomendo usar o planejador de consultas que explica o que acontece internamente para cada consulta SQL. Sempre que você ajustar algo, meça o impacto!