Estou otimizando um banco de dados Firebird 2.5 de tickets de trabalho. Eles são armazenados em uma tabela declarada como tal:
CREATE TABLE TICKETS (
TICKET_ID id PRIMARY KEY,
JOB_ID id,
ACTION_ID id,
STATUS str256 DEFAULT 'Pending'
);
Geralmente, quero encontrar o primeiro ticket que não foi processado e está no Pending
status.
Meu loop de processamento seria:
- Recupere o 1º tíquete onde
Pending
- Trabalhe com o Ticket.
- Atualizar status do ticket =>
Complete
- Repetir.
Nada muito chique. Se eu estiver assistindo o banco de dados enquanto esse loop é executado, vejo o número de leituras indexadas aumentar para cada iteração. O desempenho não parece degradar terrivelmente o que posso dizer, mas a máquina em que estou testando é bastante rápida. No entanto, recebi relatórios de degradação do desempenho ao longo do tempo de alguns dos meus usuários.
Eu tenho um índice Status
, mas ainda parece que ele varre a Ticket_Id
coluna a cada iteração. Parece que estou ignorando algo, mas não sei ao certo o que. O número crescente de leituras indexadas é algo como isso esperado ou o índice está se comportando de alguma maneira?
- Edições para comentários -
No Firebird você limita a recuperação de linhas como:
Select First 1
Job_ID, Ticket_Id
From
Tickets
Where
Status = 'Pending'
Então, quando digo "primeiro", estou apenas pedindo um conjunto limitado de registros Status = 'Pending'
.
fonte
ticket_id
, você probbaly precisa um índice em(status, ticket_id)
ticket_id
realmente teve um desempenho pior do que apenas ter o Status indexado.id
(o tipo de dados) um domínio que você definiu?Respostas:
A degradação ao longo do tempo ocorre devido ao aumento do número de itens que estão no status "Concluído". Pense nisso por um segundo - você não terá nenhuma degradação no desempenho ao testar, pois provavelmente possui um pequeno número de linhas com o status "Concluído". Mas na produção, eles podem ter milhões de linhas com o status "Concluído" e esse número aumentará com o tempo. Isso essencialmente torna seu índice no Status cada vez menos útil ao longo do tempo. Como tal, o banco de dados provavelmente decide que, como o Status quase sempre tem o valor 'Concluído', ele apenas varrerá a tabela em vez de usar o índice.
No SQL Server (e talvez em outros RDBMSs?), Isso pode ser resolvido usando índices filtrados. No SQL Server, você adicionaria uma condição WHERE ao final da sua definição de índice para dizer "aplicar este índice apenas a registros com um Status <> 'Concluído'". Portanto, qualquer consulta usando esse predicado provavelmente utilizará o índice na pequena quantidade de registros não configurados como 'Concluído'. No entanto, com base na documentação aqui: http://www.firebirdsql.org/refdocs/langrefupd25-ddl-index.html , não parece que o Firebird suporte índices indexados.
Uma solução alternativa é colocar registros 'Concluídos' em uma tabela ArchiveTickets. Crie uma tabela com a mesma definição exata (embora sem qualquer ID gerado automaticamente) que a tabela Tickets e mantenha linhas entre elas pressionando registros 'Concluídos' para a tabela ArchiveTickets. O índice na tabela de tickets terá um número muito menor de registros e um desempenho muito maior. Isso provavelmente significa que você precisará alterar quaisquer relatórios etc. que façam referência a tickets 'Concluídos' para apontar para a tabela Archive ou executar um UNION nos tickets e ArchiveTickets. Isso terá a vantagem de não apenas ser rápido, mas também significará que você pode criar índices específicos para a tabela ArchiveTickets para melhorar o desempenho de outras consultas (por exemplo:
Você deve se preocupar com isso se a sua produção for para as milhares de linhas. O desempenho diminuirá com o tempo e afetará negativamente a experiência do usuário.
fonte
Se o desempenho é afetado ou não será uma função do volume de dados e da capacidade da máquina. Dada a capacidade do hardware moderno, é difícil imaginar o volume de vendas de ingressos que não pudesse ser tratado pelo design que você descreve. No entanto, existem mudanças que eu recomendaria para correção e podem melhorar o desempenho como um benefício secundário.
Sua primeira consulta pendente de obtenção não é determinística. Primeiro de acordo com que ordem? Uma tabela SQL não tem ordem intrínseca; o
First 1
hack está apenas dando a você um primeiro arbitrário. Para torná-lo determinístico, por que não processar trabalhos pendentes na ordem Job_ID?Se você tiver dois índices {Job_ID} e {Status, Job_ID}, essa consulta retornará uma linha de maneira previsível e eficiente:
Como eu não sou usuário do Firebird, você precisará verificar o plano de consulta, mas deve ser eficiente, porque a subconsulta faz referência apenas ao segundo índice, produz um valor para o primeiro. (Pode haver outros truques de eficiência disponíveis para você. Você pode organizar a tabela física como uma árvore B + ou ter acesso a um row_id oculto, por exemplo.)
A outra alteração que eu faria para corrigir é criar
Status
um byte restrito e permitir que o aplicativo forneça a string "Pendente". Isso protegerá contraStatus
valores errados e provavelmente diminuirá o índice na barganha. Algo como:Obviamente, você pode usar uma exibição (ou talvez uma coluna derivada) para fornecer as seqüências canônicas para Status.
fonte