Estamos tentando fazer com que o Service Broker funcione em nosso ambiente para resolver um caso de negócios. Não sei se o título da mensagem é bom, mas minha pergunta está abaixo. Mas pode não ser uma boa pergunta, então, depois disso, é o que estamos fazendo e por que acho que é a pergunta certa.
Quantas mensagens devem ser enviadas em uma conversa antes de terminar a conversa?
Queremos usar o Service Broker para atualizar de forma assíncrona uma tabela de resultados. A tabela de resultados é achatada e rápida. Temos gatilhos nas tabelas base que enviam uma mensagem com sua tabela e chave primária. Temos três filas:
- Baixa latência - o objetivo é de 15 segundos para processar. Ele lida com itens que mudam relacionados a um item específico.
- Fila em massa - o objetivo é de 5 minutos para processar. Ele lida com quando algo muda que afeta muitas centenas (ou milhares) de itens. Ele divide a lista de itens que foram afetados e os alimenta na Fila de Baixa Latência Adiada
- Baixa Latência Adiada - o objetivo é 30 minutos para processar. Isso processa itens, mas apenas da fila em massa.
Basicamente, se as informações de um cliente forem atualizadas; isso afeta muitos produtos e é enviado para a fila em massa para um processamento mais lento. No entanto, se um produto é atualizado, ele é enviado para a fila de baixa latência.
Reutilizamos conversas semelhantes ao blog de Remus Rusanu http://rusanu.com/2007/04/25/reusing-conversations/ , com a exceção de que o fazemos com base no módulo da chave primária. Isso tem o benefício de ajudar na desduplicação de chaves primárias.
Portanto, estamos reutilizando as conversas e estamos dentro das nossas diretrizes. Com dois threads, consegui gravar 125 mensagens / segundo (queda artificial de vários milhares de mensagens), o que é mais do que capaz de acompanhar a produção (est. 15 mensagens / s).
No entanto, o problema que estamos enfrentando é que, após um período de tempo, ~ 4 horas ou 120 mil mensagens, começamos a ver blocos e alta contenção no sysdesend e na tabela de filas. Os bloqueios são LCK_M_U e são bloqueios KEY. Às vezes, o hobt resolve sysdesend e outras vezes para a tabela de filas específica (fila_).
Temos um processo em andamento que encerra as conversas após 24 horas ou 30 minutos de inatividade, para podermos aumentar o tempo antes de alternar as conversas.
Estamos usando o SQL 2016 Enterprise (13.0.4001.0)
- Disparar disparos (enviar para baixa latência ou volume)
- Procure ou crie um identificador de conversa.
- enviar mensagem
- Procedimento ativado da fila
- Atualizar tabela de resultados
O processo de limpeza é executado a cada 10 minutos para verificar se há alguma conversa ociosa. se os encontrar mais de três vezes seguidas, marcará como inativo e encerrará as conversas.
Informe-me se houver algum detalhe adicional que possa ser benéfico. Como não tenho muita experiência com o Service Broker, não sei se nossas mensagens / s são baixas, altas ou indiferentes.
ATUALIZAR
Por isso, tentamos novamente hoje e encontramos o mesmo problema. Alteramos o tempo de vida da conversa para 2 horas e isso não teve efeito. Então, implementamos o truque 150; que teve o mesmo problema.
Toneladas de esperas em SEND CONVERSATION, aguardando sysdesend. Alguém tem mais alguma idéia?
ATUALIZAÇÃO 2
Realizamos o teste por mais tempo hoje e, durante um período de amostra de 17 minutos, processamos 41 mil mensagens em quatro identificadores de conversa. Conseguimos acompanhar, exceto no final, quando os bloqueios no sysdesend e a tabela da fila ficaram muito altos e começamos a nos afastar antes de pará-lo. Parece que não temos problemas ao processar mensagens, sem que as coisas entrem na fila, podemos retirá-las e processá-las pelo menos 5x dessa velocidade. Nossa velocidade parece ser limitada com base na adição de mensagens.
Em um teste posterior, removemos um dos gatilhos que representavam 80% das mensagens. Mesmo com essa carga muito reduzida, começamos a ver as mesmas esperas.
ATUALIZAÇÃO 3
Obrigado, Remus pelo seu conselho (e obrigado por postar artigos excelentes no blog sobre o assunto, eles foram fundamentais para chegar a esse ponto).
Corremos novamente hoje e fizemos melhor (como demoramos mais para ver as esperas e ainda mais antes que isso nos paralisasse). Então, os detalhes.
Alteramos: * Aumentamos o número de conversas mantidas por thread de 1: 1 para 2: 1. Basicamente, tivemos 8 identificadores de conversa para 4 threads.
- consolidou a fila em massa (porque uma mensagem recebida pode significar centenas de mensagens enviadas) para consolidar em menos mensagens maiores.
Notas sobre esta tentativa:
desativando o procedimento de ativação da fila de destino. nenhuma alteração no bloqueio (esperamos 5 minutos) e as mensagens foram enviadas para sys.transmission_queues.
monitorando sys.conversation_endpoints. Esse número passou de 0 13K muito rapidamente e aumentou mais lentamente ao longo do dia, terminando em torno de 25K após ~ 5 horas. O bloqueio não começou a ocorrer até atingir 16K +/-
Entrei no DAC e executei os comandos DBREINDEX para as filas, embora, a partir de uma consulta, os registros fantasmas nunca chegassem a mais ou menos 200 antes da limpeza, e a contagem caiu para 0.
sysdesend e sysdercv tinham contagens idênticas de 24.932 quando terminei o teste.
processamos ~ 310K mensagens em 5 horas.
Nós demoramos tanto para as coisas desmoronarem que eu realmente pensei que conseguiríamos dessa vez. Amanhã tentaremos forçar as mensagens a passar pelo fio.
fonte
we started seeing blocks and high contention on sysdesend and the queue table.
-> Qual é o tipo de espera -PAGELATCH_EX/SH and WRITELOG
? Você já usou o truque 150 ? Se as tabelas do sistema são o seu ponto de discórdia, o truque 150 será altamente útil.sys.conversation_endpoints
durante o teste (constante ou está aumentando e qual o tamanho quando o bloqueio ocorre). 2) Quando o bloqueio ocorre, a desativação da fila de destino faz diferença no bloqueio SEND (a desativação da fila deve rotear SEND para sys.transmission_queue). e 3) Forçar as mensagens a serem transmitidas, mesmo localmente (configure o ponto de extremidade SSB, adicione rotas) altera o comportamento a longo prazoALTER QUEUE ... REBUILD
faz diferença quando o bloqueio é iniciado?Respostas:
Sei que é uma má forma responder à sua própria pergunta, mas queria encerrar isso para qualquer pessoa interessada. Finalmente conseguimos resolver o problema ou, pelo menos, resolvê-lo o suficiente para atender aos nossos requisitos. Quero agradecer a todos que contribuíram com comentários; Remus Rusanu e Kin como eles foram muito úteis.
Nosso banco de dados está bastante ocupado e está no modo RCSI. Temos vários (milhares) de dispositivos móveis que atualizam suas informações de localização a cada 45 segundos. Por meio dessas atualizações, várias tabelas atualizam suas informações (design inadequado, pois eu limitaria as informações voláteis a uma única tabela e as juntaria para obter os resultados). Essas tabelas são as mesmas para as quais estávamos tentando gerar informações de relatório de forma assíncrona, em vez de fazer com que os usuários finais fossem diretamente contra as tabelas base.
Inicialmente, tínhamos os gatilhos fazendo um cursor sobre os registros modificados em todas as instruções de atualização / inserção (deveria ter sido uma linha na maioria dos casos) e enviando cada chave primária em uma mensagem para o service broker. No broker de serviço interno, especialmente na fila em massa, havia outros cursores que executavam o procedimento de upsert para o relatório (uma execução por chave primária).
O que finalmente nos fez trabalhar:
Removemos os cursores e decidimos enviar mensagens maiores. Ainda uma mensagem por transação de usuário por tabela, mas agora enviamos mensagens com mais de uma chave primária.
O processador em massa também envia várias chaves por mensagem, o que reduziu o número de CONVERSAS DE ENVIO em andamento, ao embaralhar as mensagens para a outra fila, conforme apropriado.
A tabela mais volátil (nossa tabela de dados do dispositivo móvel) teve seus gatilhos removidos. Atualizamos o procedimento de upsert para incluir as chaves estrangeiras apropriadas e agora nos juntamos novamente a essa tabela ao buscar resultados para os usuários. Essa tabela contribuiu facilmente com 80% das mensagens que tivemos que processar em um dia.
Processamos ~ 1 milhão de mensagens por dia (sem a tabela Mobile) e a grande maioria (99% +) de nossas mensagens é processada dentro do nosso objetivo. Ainda temos o ocasional outlier, mas dada a natureza rara do que é considerado aceitável.
Fatores contribuintes:
Encontrei um bug no procedimento de limpeza de conversação mencionado anteriormente que não estava limpando as conversas adequadamente e finalizando-as prematuramente. Agora, isso resultou em que nossa contagem de sysdesend nunca será superior a alguns milhares (a maior parte vem do uso do truque 150).
Os cursores nos gatilhos pareciam conter mais travamentos do que o esperado (mesmo com estático, forward_only). removê-los parece ter tornado os bloqueios que vemos em SEND CONVERSATION mais transitórios por natureza (ou pelo menos os momentos que vemos são muito mais baixos).
Basicamente, estávamos executando duas soluções lado a lado (o back-end da solução Service Broker (para teste sob carga de produção)) e a solução atual (consulta terrível que abrange muitas tabelas).
Como um benefício colateral, isso descobriu um problema de limpeza de registros fantasmas e, embora não estivesse nas tabelas do Service Broker (sistema ou fila), é bastante comum em nosso sistema e os sintomas se alinham muito bem com a nossa "causa clara". problemas que experimentamos às vezes. A investigação está em andamento, estamos tentando encontrar as tabelas que estão contribuindo para isso e provavelmente provavelmente apenas reconstruiremos seus índices rotineiramente.
Agradeço novamente.
fonte