none
Lentidão ao chamar dataReader.Close() utilizando CommandBehavior.SingleRow RRS feed

  • Pergunta

  • Olá,

    Estou tendo o seguinte problema de performance ao utilizar o método Close() do IDataReader (DbDataReader, SqlDataReader, MySqlDataReader).

    O que estou tentando fazer é recuperar somente a primeira linha de qualquer consulta. No caso de uma consulta que retorna 100mil registros, eu consigo efetuar a leitura do primeiro registro rapidamente. Mas quando chamo o método dataReader.Close() ocorre uma lentidão inexplicável, coisa de 30seg. Métodos para recuperação de metaDados(GetSchema("Columns")) para esta mesma consulta também costumam ser lentas.

    Abaixo segue o código:

    IDbConnection connection = new MySqlConnection(connectionString);
    IDbCommand command = connection.CreateCommand();
    command.CommandText = "SELECT lavoura.* FROM lavoura, fazenda limit 100000" ;
    IDataReader reader;
    reader = command.ExecuteReader(CommandBehavior.SingleRow);
    // leitura do primeiro registro...
    reader.Close(); // aqui ocorre uma lentidão excessiva

    Existe alguma outra forma de recuperar apenas a primeira linha de consultas que geram grande volume de dados, com uma boa performance? Gostaria de fazer isto somente utilizando os componentes do ADO.NET, não tendo a necessidade de incluir cláusulas nas consultas para limitar o resultado.

    No exemplo que passei, a consulta está sendo feita em um banco de dados MySql, mas este mesmo cenário irá se repetir em qualquer outro banco.


    Obrigado

    quarta-feira, 14 de outubro de 2009 22:23

Respostas

  • Tenta colocar a execução do DataReader dentro de um bloco using, chamando o cancel (obviamente dentro do bloco using), vê o que acontece. Não estou com o VS aqui para testar.

    A sua pergunta sobre o ReadOnly eu não entendi, já que o DataReader por essência é somente leitura, certo?

    Abraços

    EDIT:

    O código seria + ou - isso:

    IDbConnection connection = new MySqlConnection(connectionString);
                IDbCommand command = connection.CreateCommand();
                command.CommandText = "SELECT * FROM Tabela";
                connection.Open();
                IDataReader reader = command.ExecuteReader(CommandBehavior.SingleRow);
                using (reader)
                {
                    if (reader.Read())
                    {
                        object o = reader[0];
                    }
                    command.Cancel();
                }
                connection.Close();



    quinta-feira, 5 de novembro de 2009 18:23

Todas as Respostas

  • Marciel e se você mudasse sua consulta usando limit 1?
    quarta-feira, 14 de outubro de 2009 22:43
  • Olá Murilo,

    Neste caso, o LIMIT foi imposto pelo cliente. Talvez o exemplo que eu passei não tenha sido o melhor. Em tabelas com 900mil registros por exemplo, tambem acontece o mesmo problema. 


    SELECT * FROM TABELA1 , sendo que TABELA1 tem 900mil registros.


    Marciel
    quarta-feira, 14 de outubro de 2009 23:00
  • Marciel faça esse teste e me fale, tente fechar a conexão e não o datareader só pra ver se demora também.
    Caso demore use o MySqlConnection, MySqlDataReader ao invés de IDBConnection, IDataReader, só por teste.
    quarta-feira, 14 de outubro de 2009 23:02
  • Murilo,

    Efetuei os seguintes testes:

    1) Troquei o reader.Close() por connection.Close() - Sem sucesso.
    2) Troquei a declaração das classes de acesso a dados que utilizavam as interfaces pelas classes do pacote MySql.Data.MySqlClient. - Sem sucesso.

    Tanto o close do connection como o do dataReader estão levando aprox. 30s.



    quinta-feira, 15 de outubro de 2009 14:03
  • Putz Marciel estou achando que tem a ver com a dll deles mesmo, porém não possuo experiência com essa quantidade imensa de dados, isso é, não sei se isso ocorre com o SqlConnection por exemplo, então não posso dar certeza. Desculpa por não poder ajudar.
    quinta-feira, 15 de outubro de 2009 14:08
  • Obrigado pela força Murilo. Realmente está dificil de entender esta lentidão ao fechar. Isso tambem acontece quando se usa um dataTable.

    quinta-feira, 15 de outubro de 2009 15:04
  • Ainda estou com este problema. Alguem com alguma sugestão ou técnica para trabalhar com tantos dados assim?

    Obrigado
    quarta-feira, 4 de novembro de 2009 19:40
  • Perguntinha besta: voce está fazendo a leitura do registro com um IF ao inves de While certo?

    // leitura do primeiro registro
    if (reader.Read())
    {

    }

    2º e mais importante: se sua intencao é trazer um UNICO registro nesta consulta porque o Limit 100000 ? Por que não limitar a "1" como já dito mais acima? Essa é a coisa mais logica a ser feita.

    Outro detalhe: qual é este unico registro? O primeiro incluido na tabela? O ultimo? Deve haver um criterio e, havendo, é so fazer o filtro WHERE......

    Há um erro de conceito nisso ai.. do jeito que está, está "estranho".

    []s

    Robson Castilho - MCTS .Net 2.0 Windows/Web Applications [Se o post foi útil, não esqueça de marcá-lo. Obrigado]
    quinta-feira, 5 de novembro de 2009 02:53
  • Olá Robson,

    Estas consultas são feitas pelo usuário e podem ter origem em qualquer banco de dados (MySql, SQL Server, ODBC, etc...). 
    No exemplo que passei, sugere que o usuário implantou este script em um banco MySQL usando LIMIT 100000, sendo isso fora do meu controle. Poderia ser qualquer consulta, usando ou não limit.

    Resumindo: estou com dificuldades de performance em descobrir qual a estrutura de colunas que uma consulta que resulta muitos registros (100mil ou mais) pode gerar, pois mesmo efetuando a leitura do primeiro registro (usando IF como voce mencionou), a chamada ao método Close() está muito lenta (coisa de 30s).


    Obrigado
    Marciel

    quinta-feira, 5 de novembro de 2009 12:09
  • Amigão, o método Close passa por todos os registros de qualquer maneira, pois é assim que o DataReader funciona. Ele precisa disso para, por exemplo, atualizar a propriedade que diz quantos registro foram afetados. Por isso que fica lento desse jeito. Um workaround é chamar o método Cancel() do seu Command e depois o Close() do DataReader. Tenta aí e sinaliza se deu certo.

    Abraços
    quinta-feira, 5 de novembro de 2009 12:21
  • Olá Pedro,

    Obrigado pela explicação do método Close.

    Tentei utilizar o método Cancel. Só Mas não tive sucesso.

    command.Cancel();
    reader.Close();
    Na chamada ao método reader.Close(), a aplicação disparou a seguinte excessão: Query execution was interrupted


    Sobre esse processo que o Close faz qndo é invocado, isso também acontece quando eu efetuo uma consulta em modo ReadOnly? 
    quinta-feira, 5 de novembro de 2009 18:10
  • Tenta colocar a execução do DataReader dentro de um bloco using, chamando o cancel (obviamente dentro do bloco using), vê o que acontece. Não estou com o VS aqui para testar.

    A sua pergunta sobre o ReadOnly eu não entendi, já que o DataReader por essência é somente leitura, certo?

    Abraços

    EDIT:

    O código seria + ou - isso:

    IDbConnection connection = new MySqlConnection(connectionString);
                IDbCommand command = connection.CreateCommand();
                command.CommandText = "SELECT * FROM Tabela";
                connection.Open();
                IDataReader reader = command.ExecuteReader(CommandBehavior.SingleRow);
                using (reader)
                {
                    if (reader.Read())
                    {
                        object o = reader[0];
                    }
                    command.Cancel();
                }
                connection.Close();



    quinta-feira, 5 de novembro de 2009 18:23
  • Pedro,

    Sobre minha pergunta do ReadOnly, desconsidere. Realmente é somente leitura.

    Testei o código que voce passou. Ele acabou gerando a mesma exceção na chave que fecha o bloco using:  Query execution was interrupted.
    quinta-feira, 5 de novembro de 2009 18:52
  • Trabalhei com a chamada do método Cancel em consultas utilizando ODBC e SQL Server e em ambas as situações o Cancel e o Close executaram sem problemas. No ODBC, utilizei uma consulta a um banco Oracle que retornava 49 milhoes de registros. Já no SQL Server, a consulta retornava 2 milhões.

    Me parece então que isto é um problema com o driver do MySql.Data. Mesmo utilizando a versão mais recente do site, o Close do dataReader, após o Cancel do command, gera a exceção Query execution was interrupted.

    Alguma sugestão?
    quinta-feira, 5 de novembro de 2009 21:00
  • Existe uma comunidade que dá manutenção nesse driver? Se sim, entre em contato como bug report, afinal, é um bug do provider. Ou ainda você perguntar por uma solução dentro do próprio fórum do MySql. Poste o link do seu post aqui, se vc chegar a criar, para que possamos acompanhar.

    Abraços e boa sorte.

    EDIT:

    http://bugs.mysql.com/
    sexta-feira, 6 de novembro de 2009 11:18
  • Bom dia,

    Efetuei um post hoje no forum do MySql para verificar esta exceção que está ocorrendo. 


    Vamos ver o que eles vão retornar.

    Obrigado mais uma vez.
    segunda-feira, 9 de novembro de 2009 14:08