O SQLite3 lida com segurança com acesso simultâneo por vários processos de leitura / gravação do mesmo banco de dados? Existem exceções de plataforma para isso?
O SQLite3 lida com segurança com acesso simultâneo por vários processos de leitura / gravação do mesmo banco de dados? Existem exceções de plataforma para isso?
Respostas:
Se a maioria desses acessos simultâneos forem leituras (por exemplo, SELECT), o SQLite poderá lidar com eles muito bem. Mas se você começar a escrever simultaneamente, a contenção de bloqueio pode se tornar um problema. Muito dependeria da rapidez com que seu sistema de arquivos é, já que o mecanismo SQLite é extremamente rápido e possui muitas otimizações inteligentes para minimizar a contenção. Especialmente SQLite 3.
Para a maioria dos aplicativos de desktop / laptop / tablet / telefone, o SQLite é rápido o suficiente, pois não há simultaneidade suficiente. (O Firefox usa o SQLite extensivamente para favoritos, histórico etc.)
Para aplicativos de servidor, alguém disse há algum tempo que qualquer coisa menos do que 100 mil visualizações de páginas por dia poderia ser tratada perfeitamente por um banco de dados SQLite em cenários típicos (por exemplo, blogs, fóruns), e ainda não vi nenhuma evidência ao contrário. De fato, com discos e processadores modernos, 95% dos sites e serviços da Web funcionariam perfeitamente com o SQLite.
Se você deseja um acesso de leitura / gravação muito rápido, use um banco de dados SQLite na memória . A RAM é várias ordens de magnitude mais rápida que o disco.
Sim, o SQLite lida bem com a simultaneidade, mas não é o melhor do ponto de vista do desempenho. Pelo que posso dizer, não há exceções a isso. Os detalhes estão no site da SQLite: https://www.sqlite.org/lockingv3.html
Esta declaração é interessante: "O módulo pager garante que as alterações ocorram ao mesmo tempo, que todas as alterações ocorram ou nenhuma delas, que dois ou mais processos não tentem acessar o banco de dados de maneiras incompatíveis ao mesmo tempo"
Sim. Vamos descobrir por que
Todas as alterações em uma única transação no SQLite ocorrem completamente ou não são de todo
Esse suporte a ACID, bem como a leitura / gravação simultânea, são fornecidos de duas maneiras - usando o chamado registro no diário (vamos chamá-lo de “ maneira antiga ”) ou o registro write-ahead (vamos chamá-lo de “ novo caminho ”)
Nesse modo, o SQLite usa o bloqueio DATABASE-LEVEL . Este é o ponto crucial para entender.
Isso significa que sempre que precisar ler / gravar algo, primeiro adquira um bloqueio no arquivo de banco de dados INTEIRO . Vários leitores podem coexistir e ler algo em paralelo
Durante a gravação, garante que um bloqueio exclusivo seja adquirido e nenhum outro processo seja lido / gravado simultaneamente e, portanto, as gravações são seguras.
É por isso que aqui eles estão dizendo que o SQlite implementa transações serializáveis
Como ele precisa bloquear todo um banco de dados todas as vezes e todo mundo espera por um processo que lide com a simultaneidade de gravação, essas gravações / leituras simultâneas têm desempenho bastante baixo
Antes de escrever algo no arquivo de banco de dados, o SQLite primeiro salvaria o pedaço para ser alterado em um arquivo temporário. Se algo travar no meio da gravação no arquivo do banco de dados, ele pegará esse arquivo temporário e reverterá as alterações
Nesse caso, todas as gravações são anexadas a um arquivo temporário ( log de gravação antecipada) ) e esse arquivo é mesclado periodicamente com o banco de dados original. Quando o SQLite está procurando algo, ele primeiro verifica esse arquivo temporário e, se nada for encontrado, continue com o arquivo principal do banco de dados.
Como resultado, os leitores não competem com os escritores e o desempenho é muito melhor em comparação com o Old Way.
O SQlite depende muito da funcionalidade subjacente de bloqueio do sistema de arquivos, portanto, ele deve ser usado com cuidado, mais detalhes aqui
Também é provável que o erro de execução no banco de dados esteja bloqueado , especialmente no modo de diário, para que seu aplicativo precise ser projetado com esse erro em mente
Parece que ninguém mencionou o modo WAL (Write Ahead Log). Verifique se as transações estão organizadas corretamente e com o modo WAL ativado, não há necessidade de manter o banco de dados bloqueado enquanto as pessoas estão lendo coisas enquanto uma atualização está em andamento.
O único problema é que, em algum momento, o WAL precisa ser incorporado novamente no banco de dados principal e faz isso quando a última conexão com o banco de dados é fechada. Com um site muito ocupado, você pode levar alguns segundos para fechar todas as conexões, mas 100 mil acessos por dia não devem ser um problema.
database is locked
erro será raise pelo escritor
Em 2019, existem duas novas opções de gravação simultânea ainda não lançadas, mas disponíveis em ramificações separadas.
A vantagem desse modo de diário em relação ao modo "wal" regular é que os gravadores podem continuar gravando em um arquivo wal enquanto o outro está no ponto de verificação.
COMEÇAR CONCORRENTE - link para o documento detalhado
O aprimoramento BEGIN CONCURRENT permite que vários gravadores processem transações de gravação simultaneamente, se o banco de dados estiver no modo "wal" ou "wal2", embora o sistema ainda serialize comandos COMMIT.
Quando uma transação de gravação é aberta com "BEGIN CONCURRENT", o bloqueio do banco de dados é adiado até que um COMMIT seja executado. Isso significa que qualquer número de transações iniciadas com BEGIN CONCURRENT pode prosseguir simultaneamente. O sistema usa bloqueio otimizado no nível da página para impedir que transações simultâneas conflitantes sejam confirmadas.
Juntos, eles estão presentes no begin-simulturrent-wal2 ou em um ramo próprio separado .
begin concurrent
coisas.
O SQLite possui um bloqueio de leitores e gravadores no nível do banco de dados. Várias conexões (possivelmente pertencentes a processos diferentes) podem ler dados do mesmo banco de dados ao mesmo tempo, mas apenas um pode gravar no banco de dados.
O SQLite suporta um número ilimitado de leitores simultâneos, mas permitirá apenas um gravador a qualquer instante no tempo. Para muitas situações, isso não é um problema. Gravador na fila. Cada aplicativo faz seu trabalho de banco de dados rapidamente e segue em frente, e nenhum bloqueio dura mais de algumas dezenas de milissegundos. Mas existem alguns aplicativos que exigem mais simultaneidade e esses aplicativos podem precisar procurar uma solução diferente. - usos apropriados para SQLite @ SQLite.org
O bloqueio de leitores e gravadores permite o processamento de transações independentes e é implementado usando bloqueios exclusivos e compartilhados no nível do banco de dados.
Um bloqueio exclusivo deve ser obtido antes que uma conexão execute uma operação de gravação em um banco de dados. Após a obtenção do bloqueio exclusivo, as operações de leitura e gravação de outras conexões são bloqueadas até que o bloqueio seja liberado novamente.
O SQLite possui uma tabela de bloqueio que ajuda a bloquear o banco de dados o mais tarde possível durante uma operação de gravação para garantir a máxima simultaneidade.
O estado inicial é DESBLOQUEADO e, nesse estado, a conexão ainda não acessou o banco de dados. Quando um processo é conectado a um banco de dados e mesmo uma transação foi iniciada com BEGIN, a conexão ainda está no estado UNLOCKED.
Após o estado UNLOCKED, o próximo estado é o estado SHARED. Para poder ler (não gravar) dados do banco de dados, a conexão deve primeiro entrar no estado SHARED, obtendo um bloqueio SHARED. Várias conexões podem obter e manter bloqueios SHARED ao mesmo tempo, para que várias conexões possam ler dados do mesmo banco de dados ao mesmo tempo. Mas enquanto apenas um bloqueio SHARED permanecer inédito, nenhuma conexão poderá concluir com êxito uma gravação no banco de dados.
Se uma conexão deseja gravar no banco de dados, ela deve primeiro obter um bloqueio RESERVADO.
Somente um único bloqueio RESERVADO pode estar ativo ao mesmo tempo, embora vários bloqueios SHARED possam coexistir com um único bloqueio RESERVADO. RESERVADO difere de PENDENTE, pois novos bloqueios compartilhados podem ser adquiridos enquanto houver um bloqueio RESERVADO. - Bloqueio de arquivo e simultaneidade no SQLite versão 3 @ SQLite.org
Depois que uma conexão obtém um bloqueio RESERVADO, ela pode começar a processar operações de modificação do banco de dados, embora essas modificações possam ser feitas apenas no buffer, em vez de serem realmente gravadas no disco. As modificações feitas no conteúdo da leitura são salvas no buffer de memória. Quando uma conexão deseja enviar uma modificação (ou transação), é necessário atualizar o bloqueio RESERVADO para um bloqueio EXCLUSIVO. Para obter a trava, você deve primeiro levantar a trava para uma trava PENDENTE.
Um bloqueio PENDING significa que o processo que mantém o bloqueio deseja gravar no banco de dados o mais rápido possível e está aguardando que todos os bloqueios SHARED atuais sejam limpos, para que ele possa obter um bloqueio EXCLUSIVO. Nenhum novo bloqueio SHARED será permitido no banco de dados se um bloqueio PENDING estiver ativo, embora os bloqueios SHARED existentes possam continuar.
É necessário um bloqueio EXCLUSIVO para gravar no arquivo do banco de dados. Somente um bloqueio EXCLUSIVO é permitido no arquivo e nenhum outro tipo de bloqueio pode coexistir com um bloqueio EXCLUSIVO. Para maximizar a simultaneidade, o SQLite trabalha para minimizar a quantidade de tempo que os bloqueios EXCLUSIVOS são mantidos. - Bloqueio de arquivo e simultaneidade no SQLite versão 3 @ SQLite.org
Então, você pode dizer que o SQLite lida com segurança com acesso simultâneo por vários processos gravando no mesmo banco de dados, simplesmente porque não o suporta! Você receberá SQLITE_BUSY
ou receberá SQLITE_LOCKED
o segundo gravador quando ele atingir a limitação de novas tentativas.
Este thread é antigo, mas acho que seria bom compartilhar o resultado dos meus testes feitos no sqlite: executei 2 instâncias do programa python (processos diferentes, mesmo programa) executando instruções SELECT e UPDATE comandos sql dentro de transação com bloqueio EXCLUSIVO e tempo limite definidos como 10 segundos para obter um bloqueio, e o resultado foi frustrante. Cada instância fez no loop de 10000 etapas:
Mesmo que o sqlite tenha concedido bloqueio exclusivo na transação, o número total de ciclos realmente executados não era igual a 20.000, mas menor (o número total de iterações no contador único contado para os dois processos). O programa Python quase não emitiu nenhuma exceção (apenas uma vez durante a seleção para 20 execuções). A revisão do sqlite no momento do teste foi 3.6.20 e o python v3.3 CentOS 6.5. Na minha opinião, é melhor encontrar produtos mais confiáveis para esse tipo de trabalho ou restringir as gravações no sqlite para um único processo / thread único.
with con
é suficiente.