Fórum Consulta de Questionario WEB #7426

08/07/2009

0

Boa Tarde,
Essa é minha primeira vez aqui no suporte. Nao sei se estou na sala correta para esta duvida.

Uso Java 5 + Jboss 4.23 + JSF.

Essa é minha aplicação.
http://150.162.1.32:8080/resultadosead

login: saad
senha: avaliacao

A aplicação é a consulta de respostas de um questionário web.

O arquivo .war e o código workspace do sistema estao aqui: http://files.getdropbox.com/u/745338/consultaead.rar

Estou com um gargalo de desempenho no sistema que não consigo resolver, é em apenas um método. Não existe análise de código de grande complexidade. A classe é DBData, método getAlternativa.

O problema está nas consultas que usam os parametros "Todos os Pólos" ou " Todas as Disciplinas"
O problema é que preciso fazer várias varreduras no BD para ir buscando os dados.


Nesta consulta o que estou fazendo é montar SQLs dentro do método e ir buscando as coisas que preciso.

No exemplo desta imagem para a alternativa "Utilizei bastante" busco os respondentes totais em todos os pólos(50), busco os respondentes de cada pólo, Pr-Ibati(13), e um grande problema é que também preciso saber o total de respondentes da questão em cada coluna, para fazer a porcentagem e as colunas fecharem em 100%.

Bom, estou disponível para fazer qualquer tipo de modificação. Esse sistema está no ar. Então estou tendo muitos problemas.

Aguardo ansiosamente e estou disponível para qualquer dúvida.
Lucas

lucaszago@gmail.com, lucaszag@hotmail.com
48 96176763

Lucas Zago

Lucas Zago

Responder

Posts

08/07/2009

Henrique Weissmann

Lucas,

ainda não acessei o seu código fonte, mas com base na sua descrição já é possível dar algumas idéias:

Você diz: "O problema é que preciso fazer várias varreduras no BD para ir buscando os dados."

Neste caso, será que não seria interessante a utilização de uma estratégia de cache?

Explicando melhor: há algumas informações no seu banco de dados que não serão atualizadas constantemente. Neste caso, é mais interessante ao invés de você as consultar diretamente no banco de dados, as consultar primeiro em um cache interno da sua aplicação e, somente caso as mesmas não se encontrem presentes no mesmo, fazer a consulta no banco de dados e, posteriormente, armazená-las no cache para que em uma busca posterior você não precise repetir a consulta no BD.

Esta técnica melhora ao extremo a performance do sistema, pois todas as informações que você irá necessitar já se encontrarão em memória para você.

Há atualmente diversas bibliotecas de cacheamento que você pode usar. Lhe recomendo duas alternativas:

OSCache: http://www.opensymphony.com/oscache/

EHCache: http://ehcache.sourceforge.net/

Ambas são igualmente boas e permitem inclusive o cacheamento de páginas JSP também. Como consequência, a sua camada de visualização em alguns casos nem sequer precisará acessar a camada de modelo da sua aplicação, melhorando significativamente a sua performance.

No caso de ambas as bibliotecas, é possível definir também o tempo de expiração do cache, assim como a política de armazenamento (LRU, MRU ou outras) garantindo que assim as informaçãoes presentes no mesmo sejam o mais atualizadas possível.

Qualquer coisa, estou a sua disposição.
Responder

Gostei + 0

08/07/2009

Lucas Zago

Oi Henrique,


Por um momento achei bem viável, visto que esses dados não são tão dinâmicos, mas se for para pra pensar no número de consultas possíveis isso gera alguma duvidas por eu nao conhecer como funciona essas bibliotecas.

Mas pensando em consultas, os parametros sao:
Semestre (n) * Curso * (n) * Pólos (n) * Disciplinas(n) * Dimensao(n)

A combinação é grande e nao sei como ficaria armazenada todas essas consultas.
Elas estao demorando bastante apenas quando se trata dos parametros "todos os polos" ou "todas as disciplinas". Mas mesmo assim a combinacao de consultas usando esses parametros ainda nao é pequena.

Portanto, qual caminho seguir?

Tentar otimizar isso de alguma forma ou estratégia de cache?

Muito Obrigado Henrique,

Lucas
Responder

Gostei + 0

08/07/2009

Henrique Weissmann

Olá Lucas,

provávelmente as suas consultas ficam mais lentas quando se seleciona todos os polos ou disciplinas porque uma destas duas variáveis (ou as duas) apresenta um maior número de possibilidades.

Com relação a qual das abordagens adotar: alguma otimização no banco de dados ou biblioteca de cache?

As duas possibilidades:

no seu banco de dados, verifique se há índices para as colunas chave da sua tabela. Caso não exista, crie-os. Só isto já irá otimizar em muito o acesso às informações.

Outra dica importante com relação a bancos de dados diz respeito ao tipo das colunas chave (como chaves primárias ou estrangeiras, ou mesmo consultas que sejam fundamentais para a consulta): já vi diversas vezes utilizarem o tipo varchar neste tipo de campo. No caso, mude para char mesmo, pois o varchar sempre terá um certo overhead relativo à descoberta do número de caracteres armazenados no campo. É um tempo desprezível para poucos registros, mas que pode se tornar significativo para consultas maiores.

Com relação à utilização de caches, sempre é uma boa alternativa. O raciocínio básico aqui é: se já foi computado uma vez (no caso, se você já buscou um registro no bd (e consutlas no bd são caras), e o registro não sofre tantas alterações), porque ficar computando diversas outras vezes quando você pode possuir o resultado pronto uma única vez? Esta é a idéia básica por trás do cache.


Responder

Gostei + 0

08/07/2009

Lucas Zago

Pois é Henrique, você disse uma coisa agora que vai me ajudar a explicar o que realmente está acontecendo.

Você disse: "provavelmente as suas consultas ficam mais lentas quando se seleciona todos os polos ou disciplinas porque uma destas duas variáveis (ou as duas) apresenta um maior número de possibilidades."

A consulta está lenta nao somente pela busca de possiblidades, mas pela maneira que eu tenho de "monta-las" para exibir na tela.

Quando eu busco "Todos os Polos" E "Todas as Disciplinas" a consulta é rapida! Pois eu só exibo uma coluna, que mostra o total de respostas de todos os pólos e todas as disciplinas.

Olhe a imagem no primeiro post.
Usando um exemplo de Todos os Polos e uma disciplina qualquer, que foi o exemplo do primeiro Post, no sistema é preciso montar uma coluna para cada pólo. E nessa montagem está o baixo desempenho pois além de eu achar os 25 respondentes da primeira alternativa em Pato Branco, eu tenho de saber que nessa questao existem 27 respondentes no total, para poder calcular a porcentagem de 92,59%.

Por isso acho que o desempenho nao está de todo mal, mas nessas consultas de várias colunas talvez eu esteja pecando em alguma coisa.

Nao sou eu quem toma conta do BD, mas vou conversar com o analista hoje e sugerir alguma consulta que possa exibir isso de maneira que possa trazer pra aplicacao com uma varredura simples.

Me disseste "ainda não acessei o seu código fonte". Poderia dar uma olhada nesse método getAlternativa dentro da classe DBData quando puderes? Temo estar pecando em algo simples.

Obrigado Henrique

Lucas
Responder

Gostei + 0

13/07/2009

Henrique Weissmann

Lucas, verifiquei o seu código e não encontrei nenhuma anomalia.

Tente a dobradinha cacheamento + otimização do banco e me dê seu retorno ok?
Responder

Gostei + 0

14/07/2009

Devmedia

Lucas,
precisamos de um retorno seu para que possamos resolver suas dúvidas.
Responder

Gostei + 0

16/07/2009

Devmedia

Lucas,
precisamos de um retorno seu, a fim de solucionar o chamado. No aguardo.
Responder

Gostei + 0

17/07/2009

Lucas Zago

Ola,

Bom... a realidade eh que o cacheamento vai melhorar, mas eu nunca trabalhei com isso e teria de aprender uma coisa que talvez nao seja necessaria, pois tenho certeza que melhorando a maneira como foi implementado aquele metodo, concerteza vai resolver o problema, por isso pedi pra dar uma olhada no codigo. Anomalias nao tem mesmo porque esta funcionando, mas de uma maneira ruim, o ideal seria otimizar o codigo acredito.
E, pra algo estar na cache a consulta precisa ser feita, e como a combinacao de consultas nao eh pequena, teria de ser feita cada consulta daquela, demorando uns 10 minutos cada uma, para depois sim ficar em cache. Vou procurar como fazer isso usando ferramentas pra cache, mas..

Eu coloquei um problema em discussao com varios detalhes, mas recebi uma resposta bem generalizada que serviria pra qualquer aplicacao.
Responder

Gostei + 0

17/07/2009

Henrique Weissmann

Lucas,

neste caso, as soluções são realmente genéricas, mas é que são as normalmente aplicadas em casos como o seu.

Se quiser, podemos a partir daqui ir trabalhando de duas maneiras:

* Vendo como funcionam as principais ferramentas de cache utilizadas atualmente

* Procurar alternativas para a execução de suas consultas. Percebi por exemplo que há mais de um caso de pesquisa, a partir das quais você vai construindo o sql a ser gerado.

No caso, poderiamos discutir uma abordagem alternativa para este problema. Talvez remodelando o código para cada caso, o que me diz?


Estou a sua disposição.


Responder

Gostei + 0

20/07/2009

Devmedia

Lucas,
precisamos de um retorno seu para podermos resolver o seu problema. O suporte DevMedia tira suas dúvidas, é só expor que a gent tenta te ajudar da maneira mais fácil e rápida possível.
Responder

Gostei + 0

23/07/2009

Lucas Zago

Oi Henrique,

Estava viajando, por isso a demora na resposta.

Voce disse duas coisas que a gente pode trabalhar.
Eu digo que se me ajudar a resolver tirando algumas duvidas no que diz respeito a implementacao, eu poderia abrir outro chamado só para nos dedicarmos a caches. O que me diz?

O problema dos loops é o seguinte.
Quando ele entra nesse Método ele trabalha da seguinte forma

Para cada alternativa
  - Faz muita coisa para achar resultados de somente uma alternativa

Voce disse que existe muitos casos, onde posso separar cada um deles, se eu resolver esse o resto é batata.
Montei um exemplo de cosulta sql onde isso esta tudo junto.

Agora a solucao caiu em como exibir isso na tela.
Porém, dois problemas que preciso de sua ajuda.

1. Antes eu usava um HASHMAP, onde o código do Pólo era a chave e um Objeto Respostas continha o N e a Porcentagem. E o porque de eu usar HASHMAP é que como pode observar os dados nao estao certinhos para simplemente botar na tela, tenho de trabalhar com eles, por exemplo no alternativa 101, teve apenas 1 respondente no Polo 'SC016' e nenhum dos outros respondeu essa alternativa nos outros Polos. Teria de estar como zero, mas eu nem posso usar o comando isNull no sql pois se a questao nao foi respondida nem sequer existe uma entrada com o campo null.
Resumindo, como nao da pra montar uma sql perfeita, preciso de uma chave de hash que utilize dois parametros.
O HashMap atual é:
HashMap<String, Respostas> v = new HashMap<String, Respostas>();
onde String é o código do Polo.
Ou qualquer outra alternativa.


2. O que gerou essa confusao, é que estou gerando a tabela linha a linha.
E o interssante se eu conseguir fazer o que disse acima, seria gerar colunas de respostas por questao
Olhe como esta feito o trecho das alternativas, para cada questao ele executa isso no jsp.

<rich:subTable var="alternativa" value="#{questao.alternativaQuestao}"
                                <rich:column>
                                    <h:outputText value="#{alternativa.descricao}"/>
                                </rich:column>
                                <rich:columns value="#{listagem.cabecalho}" var="col" index="index">
                                    <h:panelGrid columns="2" width="100%">
                                      <h:outputText value="#{alternativa.respostas[index].NRespondentes}" />
                                        <h:outputText value="#{alternativa.respostas[index].porcentagem}" />
                                    </h:panelGrid>
                                </rich:columns>
</rich:subTable>

Estarei sempre por perto agora.
Grato.




Responder

Gostei + 0

23/07/2009

Henrique Weissmann

Olá Lucas,

bem: vamos por partes ok?

> Voce disse duas coisas que a gente pode trabalhar.
> Eu digo que se me ajudar a resolver tirando algumas duvidas no que diz respeito a implementacao, eu poderia > abrir outro chamado só para nos dedicarmos a caches. O que me diz?

O que for melhor pra você Lucas. :)

Antes de trabalharmos com as views, dei mais uma revisada no código fonte. No caso, o método que você havia mencionado anteriormente: função getAlternativas da classe DBData.

Tenho algumas observações com relação a este método. Para começar, o fato dele ser estático não é uma boa prática. Lembre-se de que você está trabalhando com uma aplicação web que será acessada por diversas pessoas ao mesmo tempo. Ao definir este método desta forma, você estará criando um gargalo considerável na sua aplicação, pois o método ficará "preso" para cada requisição feita.

O ideal consiste em você criar novas instâncias da classe DBData conforme se faça necessário.

Bom: agora, com relação ao conteúdo do método:

Observei algumas condições que talvez sejam perigosas com relação ao cálculo dos "N respondentes".

Veja este trecho:

                String sql2 = "select count(*) as nRespondentes ";
                String sql2test = "";

                if (isTodosPolos() && !isTodosDisciplinas()) {
                    sql2 += ", cd_polo_pol ";
                }
                if (!isTodosPolos() && isTodosDisciplinas()) {
                    sql2 += ", cd_disciplina_dis ";
                }

Pergunta: você está criando o seu código SQL em tempo de execução. O que ocorreria se a função isTodosPolos() retornar verdadeiro E isTodosDisciplinas() também? Outra situação: e se ambas as funções retornarem falso? Isto pode ocorrer? Os campos cd_polo_pol e cd_disciplina_dis não são utilizados no transcorrer do seu método, reparou isto? Dependendo do SGBD utilizado, isto poderia tornar a sua consulta mais lenta.

(Saindo um pouco deste método e caminhando em direção ao banco de dados, tenho uma sugestão para você que aprendi analisando o código do phpBB. Neste sistema há uma situação similar à sua: lá é preciso expor quantos replies foram dados a determinado post no fórum. Há dois caminhos aqui: eles poderiam simplesmente a cada execução chamar uma consulta do tipo "select count(*) from..." ou poderiam criar um campo a mais no post indicando qual o número de replies, que seria atualizado conforme novas mensagens foram postadas. Optaram pela segunda alternativa. Talvez, você possa utilizar abordagem semelhante na sua base de dados, o que tornaria esta busca por número de correspondentes desnecessária (pense nisto).


um trigger simples no banco de dados faria isto para você)


Outra sugestão que fiz anteriormente: consultas SQL que são compostas por concatenação de strings normalmente são sinal de problema a curto prazo. Pense novamente na utilização de uma ferramenta de ORM para fazer suas consultas. O uso deste tipo de ferramenta diminui significativamente a complexidade do seu código.

Vamos agora para a sua camada de visualização:

Você menciona no seu último post que está gerando as tabelas contendo o número de respostas linha a linha. como consequência, teremos uma série de tags <table> aninhadas. Pode ser (não garanto) que o seu servidor esteja inclusive gerando a resposta rapidamente para você, porém o navegador esteja demorando na renderização da sua página. Já vi isto acontecer inúmeras vezes. Minha sugestão para este tipo de situação consiste em, visto que o aninhamento de tabelas se mostra necessário, optar por renderizar as tabelas aninhadas gerando a menor quantidade possível de código HTML ou, com o mínimo possível de estilos.

Talvez uma alternativa interessante seja a exposição dos seus dados não como tabelas, mas sim como listas. O que me diz? Além de ser gerado menos código html, a renderização seria mais rápida (supondo que seja um problema de demora na renderização).

Agora, com relação ao seu post:

>> Voce disse que existe muitos casos, onde posso separar cada um deles, se eu resolver esse o resto é batata.
>> Montei um exemplo de cosulta sql onde isso esta tudo junto.

Que caso você está se referindo especificamente? Realmente não ficou claro para mim.

Aguardo seu retorno.

PS:

Uma dica interessante: para avaliar o desempenho dos seus métodos, crie testes unitários para eles. Assim você poderá comparar o tempo de execução dos testes para ter uma noção de onde seu código está ficando preso ou não.

Outra dica de ferramenta interessante é o profilador do Netbeans (o melhor que conheço). Pelo que vi, você está trabalhando com o Eclipse. No entanto, você pode importar o seu projeto para o Netbeans e executá-lo pelo Netbeans só para usar seu profilador, que costuma ser uma mão na roda em situações como as suas.

Responder

Gostei + 0

24/07/2009

Lucas Zago

>Pergunta: você está criando o seu código SQL em tempo de execução. O que ocorreria se a função >isTodosPolos() retornar verdadeiro E isTodosDisciplinas() também? Outra situação: e se ambas as funções >retornarem falso?

Tipos de Consulta:
    if (!isTodosPolos() && !isTodosDisciplinas())
Eficiente!

    if (isTodosPolos() && isTodosDisciplinas())
Eficiente, pois so precisa gerar um total(uma coluna)

Os outros dois casos sao:

    if (isTodosPolos() && !isTodosDisciplinas())
    if (!isTodosPolos() && isTodosDisciplinas())

Ou seja se sao "todos os polos" OU "todas as disciplinas". Estes sao demorados porque geram uma tabela com varias colunas.

---------------------------------------

>Os campos cd_polo_pol e cd_disciplina_dis não são utilizados no transcorrer do seu método, reparou isto? >Dependendo do SGBD utilizado, isto poderia tornar a sua consulta mais lenta.

Os campos cd_polo_pol e cd_disciplina_dis sao seguidos no final por um group by, mas somente nesses casos. As outras situacoes so e retornado um N pelo count.

>Pode ser (não garanto) que o seu servidor esteja inclusive gerando a resposta rapidamente para você, porém o >navegador esteja demorando na renderização da sua página

Tambem  nao garanto, mas apostaria 90% que nao, pelo que ja vi anteriormente.
---------

Agora sobre os casos que especifiquei, é um dos dois casos que geram problemas, me referi quando é todos os polos e !todas as disciplinas.
Por favor, de uma olhada no meu primeiro post, la eu explico exatamente o que e o "Meu grande problema"

---------

PS: Pode ter certeza que todas as dicas que deu sao validas e pretendo analisar e ver todas elas, mas preciso que isso tenha ao menos uma melhora significativa para depois poder trablhar nas outras coisas. Tenho certeza que o problema e o desempenho neste metodo. E e nisso que estou trabalhando no momento.

Obrigado Henrique.
Responder

Gostei + 0

24/07/2009

Lucas Zago

Oi Henrique,

Estou tentando montar uma consulta que vai ter todos os dados que eu preciso. Se eu conseguir monta-la.
Tudo é uma questao de manipular os dados depois, mas o importante é que ele irá fazer muito menos varreduras no BD.

Esse é um exemplo de consulta:

select distinct cd_polo_pol, count(*) as nRespondentes, nu_alternativa_tpa
from capa..respostaQuestaoAcad_rqa
where cd_pesquisa_pes=101
and nu_sem_trm=20081
and cd_dimensao_dim=1
and cd_grupoQuestao_grq=1
and cd_questao_qto=1
and cd_curso_rqa=709
and cd_disciplina_dis='FIL9600'
and nu_alternativa_tpa In(101,102,103,104)
group by cd_polo_pol, nu_alternativa_tpa

Esses numeros dentro de nu_alternativa_tpa precisam ser genéricos.
Eu pego eles por uma outra consulta que esta abaixo e é executada primeiro

            while (rs.next()) {
                alt = new AlternativaQuestao();
                alt.setNumero(rs.getInt("nu_alternativa_tpa"));
                alt.setDescricao(rs.getString("dc_alternativa_tpa"));
                alt.setSequencia(rs.getInt("nu_seqAlternativa_que"));
            }

Tentei fazer da seguinte maneira
Concatenei todos os alt.getNumero() separados por viruglas e coloquei no statement. Mas deu que nao podia fazer a consulta no BD pois esse numeros precisam ser Inteiros separados por virgulas, e nao um "stringao" como eu fiz.

Tentei coloca-los num array, mas eu nao sei quantos vao ter.

Tentei em um arrayList mas acabei caindo na mesma ideia, nao sei quantos numeros vao ter, entao nao sei como coloca-los separados por virgulas para colocar na sql.
Essa é a idéia
String sql2 = "select distinct cd_polo_pol, count(*) as nRespondentes, nu_alternativa_tpa "
                    + "from capa..respostaQuestaoAcad_rqa "
                    + "where cd_pesquisa_pes=? "
                    + "and nu_sem_trm=? "
                    + "and cd_dimensao_dim=? and cd_grupoQuestao_grq=? and cd_questao_qto=? "
                    + "and cd_curso_rqa=? and cd_disciplina_dis=? "
                    + "and nu_alternativa_tpa In(alts.get(0) + "," + alts.get(1) ... + ")"
                    + "group by cd_polo_pol, nu_alternativa_tpa";

Ou seja, como posso montar essa SQL. Meu problema caiu em como montar ela.

=p
Responder

Gostei + 0

24/07/2009

Lucas Zago

Opa, sobre o post da consulta SQL já consegui

select distinct cd_polo_pol, count(*) as nRespondentes, nu_alternativa_tpa
from capa..respostaQuestaoAcad_rqa
where cd_pesquisa_pes=101
and nu_sem_trm=20081
and cd_dimensao_dim=1
and cd_grupoQuestao_grq=1
and cd_questao_qto=1
and cd_curso_rqa=709
and cd_disciplina_dis='FIL9600'
and nu_alternativa_tpa In(select nu_alternativa_tpa from capa..vi_alternativaQuestao_que  where cd_pesquisa_pes=101 and cd_dimensao_dim=1
and cd_grupoQuestao_grq=1
and cd_questao_qto=1)
group by cd_polo_pol, nu_alternativa_tpa
Responder

Gostei + 0

Utilizamos cookies para fornecer uma melhor experiência para nossos usuários, consulte nossa política de privacidade.

Aceitar