Este artigo descreve o método de processamento em lote de hibernato de dados maciços. Compartilhe -o para sua referência, como segue:
Processamento de lote de hibernação quantidades maciças são realmente indesejáveis da perspectiva do desempenho e são desperdiçadas muita memória. A partir de seu mecanismo, o Hibernate primeiro verifica os dados que atende às condições, o coloca na memória e depois executa operações. O desempenho é muito insatisfatório no uso real. No meu uso real, os dados da terceira solução de otimização a seguir são: 100.000 dados são inseridos no banco de dados, que leva cerca de 30 minutos. Haha, fraco. (Inseri 1000.000 dados em 10 minutos (os campos são relativamente pequenos))
Existem três maneiras de lidar com isso para resolver problemas de desempenho:
1: Ignorar a API do hibernato e use diretamente a API JDBC. Este método tem melhor desempenho. É também o mais rápido.
2: Use procedimentos armazenados.
3: Use a API do Hibernate para realizar processamento regular em lote. Pode haver alterações e a mudança mudará. Quando encontramos um certo valor, podemos excluir os dados a tempo após a conclusão da operação, session.flush (); session.evict (XX Objetos Conjunto); Isso também pode salvar algumas perdas de desempenho. Essa "quantidade certa" precisa ser usada como uma referência quantitativa com base nas condições reais. Geralmente em torno de 30-60, mas o efeito ainda não é o ideal.
1: Ignore a API de hibernato e faça -o diretamente através da API JDBC. Este método tem melhor desempenho e o mais rápido. (O exemplo é uma operação de atualização)
Transação tx = session.begIntransaction (); // Observe que você está usando a conexão de limite de transação do hibernato Conn = session.Connection (); Preparado STMTENT STMT = Conn.PreparedStatement ("Atualize o cliente como C Set C.Sarlary = C.Sarlary+1 onde C.Sarlary> 1000"); stmt.excuteUpdate (); tx.Commit (); // Observe que você está usando o limite de transação de hibernato Neste applet, ele usa a API que chama diretamente o JDBC para acessar o banco de dados, o que é muito eficiente. Evite problemas de desempenho causados pelo hibernato primeiro consulta e carregamento na memória e depois executando operações.
2: Use procedimentos armazenados. No entanto, esse método não é recomendado para usar devido à conveniência da portabilidade e da implantação do programa. (O exemplo é uma operação de atualização)
Se o banco de dados subjacente (como o Oracle) suportar procedimentos armazenados, as atualizações em lote também poderão ser executadas por meio de procedimentos armazenados. Os procedimentos armazenados são executados diretamente no banco de dados, mais rápido. No banco de dados Oracle, um procedimento armazenado chamado BatchUpDateCustomer () pode ser definido, o código é o seguinte:
A cópia do código é a seguinte: Crie ou substitua o procedimento BatchUpDatEcustomer (P_AGE em número) como BEGIN ATUALIZAÇÃO Os clientes definem idade = idade+1 onde idade> p_age; final;
O procedimento armazenado acima possui um parâmetro p_age, que representa a idade do cliente. O aplicativo pode chamar o procedimento armazenado das seguintes maneiras:
tx = session.BegIntransaction (); conexão con = session.Connection (); String procedure = "{Call BatchUpDateCustomer (?)}"; CallableStatement cstmt = con.preparecall (procedimento); cstmt.setInt (1, 0); // Defina o parâmetro de idade como 0cstmt.executeUpdate (); tx.Commit ();Como pode ser visto no programa acima, o aplicativo também deve ignorar a API do Hibernate e chamar diretamente os procedimentos armazenados por meio da API JDBC.
3: Use a API do Hibernate para realizar processamento regular em lote. Pode haver alterações e a mudança mudará. Quando encontramos um certo valor, podemos excluir os dados a tempo após a conclusão da operação, session.flush (); session.evict (XX Objetos Conjunto); Isso também pode salvar algumas perdas de desempenho. Essa "quantidade certa" precisa ser uma referência quantitativa com base nas condições reais ...
(O exemplo é uma operação de salvamento)
A lógica de negócios é: queremos inserir 10 0000 dados no banco de dados
tx = session.begIntransaction (); for (int i = 0; i <100000; i ++) {cliente personalizado = new client (); custom.setName ("user"+i); session.save (personalizado); se (i%50 == 0) // use todos os 50 dados como uma unidade de processamento, que mencionou acima.Isso manterá o sistema em um intervalo estável ...
Durante o processo de desenvolvimento do projeto, devido aos requisitos do projeto, geralmente precisamos inserir grandes quantidades de dados no banco de dados. Existem dezenas de milhares, dezenas de milhares, dezenas de milhões e até dezenas de milhões delas. Se você usar o Hibernate para inserir dados desse nível de magnitude, poderá ocorrer uma exceção. A exceção comum é OSTOfMemoryError (exceção de transbordamento de memória).
Primeiro, vamos revisar brevemente o mecanismo de operação de inserção de hibernato. O Hibernate precisa manter seu cache interno. Quando executarmos a operação de inserção, colocaremos todos os objetos para operar em nosso cache interno para gerenciamento.
Quando se trata do cache de Hibernate, o Hibernate tem teorias de cache interno e cache secundário. Como o hibernato possui diferentes mecanismos de gerenciamento para esses dois caches, podemos configurar seu tamanho em relação ao cache secundário, enquanto para caches internos, o Hibernate adota uma atitude de "fluxo de trela" e não há limite de sua capacidade. Agora o cerne do problema é encontrado. Quando inserimos dados maciços, muitos objetos serão incluídos no cache interno (o cache interno é armazenado em cache na memória), para que a memória do sistema seja comida pouco a pouco. Se o sistema for finalmente "frito", é razoável.
Vamos pensar em como lidar melhor com esse problema? Algumas condições de desenvolvimento devem ser tratadas usando o Hibernate e, é claro, alguns projetos são mais flexíveis e você pode encontrar outros métodos.
Aqui eu recomendo dois métodos:
(1): Otimize o hibernato e use o método de inserção segmentada para limpar o cache no tempo no programa.
(2): ignore a API de hibernato e faça a inserção em lote diretamente através da API JDBC. Este método tem o melhor desempenho e o mais rápido.
Para o método 1 acima, a idéia básica é: otimize o hibernato, defina o parâmetro hibernate.jdbc.batch_size no arquivo de configuração para especificar o número de SQL enviado a cada vez; O programa usa o método de limpeza do cache no tempo na inserção segmentada (a sessão implementa a escrita assíncrona, o que permite que o hibernato escreva operações explicitamente), ou seja, limpe-as do cache interno a tempo depois de inserir uma certa quantidade de dados e libera a memória ocupada.
Para definir o parâmetro hibernate.jdbc.batch_size, você pode consultar a seguinte configuração.
<Factory> <factory> ...
O motivo para configurar o parâmetro hibernate.jdbc.batch_size é ler o banco de dados o mínimo possível. Quanto maior o valor do parâmetro hibernate.jdbc.batch_size, quanto menos os tempos você lê o banco de dados e mais rápido a velocidade. A partir da configuração acima, pode -se observar que o Hibernate espera até que o programa acumule 50 mql antes de enviá -lo em lotes.
O autor também está pensando que o valor do parâmetro hibernate.jdbc.batch_size não pode ser definido o maior possível, e resta a ser discutido a partir de uma perspectiva de desempenho. Isso requer consideração da situação real e defini -la conforme apropriado. Geralmente, a definição de 30 ou 50 pode atender às necessidades.
Em termos de implementação do programa, o autor leva a inserção de 10.000 dados como exemplo,
Sessão session = hibernateUtil.currentSession (); transatcion tx = session.begIntransaction (); for (int i = 0; i <10000; i ++) {student st = new Student (); s.SetName ("FeIFEI"); Session.Save (ST); if (i%50 == 0) // Use 50; // Mantenha -se síncrono com os dados do banco de dados session.clear (); // Limpe todos os dados em cache internamente e libere a memória ocupada no tempo}} tx.Commit (); ...Em uma certa escala de dados, essa abordagem pode manter os recursos de memória do sistema em um intervalo relativamente estável.
NOTA: O cache de segundo nível mencionado anteriormente é necessário para mim mencioná-lo aqui. Se o cache secundário estiver ativado, para manter o cache secundário, o Hibernate cobrará os dados correspondentes no cache secundário quando inserirmos, atualizaremos e excluirmos operações. Haverá uma enorme perda de desempenho, então o autor recomenda desativar o cache do nível 2 no processamento em lote.
Para o método 2, o processamento tradicional do lote JDBC é usado e a API JDBC é usada para processá -lo.
Consulte o processamento em lote Java e o SQL de auto-execução.
Olhando para o código acima, você sempre acha que algo é inapropriado? Sim, você não percebeu! Esta ainda é a programação tradicional do JDBC, sem um sabor de hibernato.
O código acima pode ser modificado para o seguinte:
Transação tx = session.begIntransaction (); // Use a conexão de processamento de transação de hibernato Conn = session.Connection (); PreparEstatement sTmt = Conn.Preparestatement ("Inserir em T_student (nome) valores (?)"); para (int j = 0; j ++; j <200) {for (int i = 0; i ++; j <50) {stmt.SetString (1, "feiFei");}} stmt.executeUpdate (); tx.Commit (); // Use o limite de processamento de transações de hibernato ...Essa mudança terá um sabor de hibernato. Após o teste, o autor usa a API JDBC para processamento em lote, que é quase 10 vezes maior no desempenho do que o uso da API de Hibernate. Este é sem dúvida o desempenho dominante do JDBC.
Na atualização e exclusão de lote do Hibernate2, para operações de atualização em lote, o Hibernate descobre os dados que atendem aos requisitos e executa a operação de atualização. O mesmo vale para a exclusão do lote. Primeiro descubra os dados que atendem às condições e depois faça a operação de exclusão.
Isso tem duas grandes desvantagens:
(1): ocupa muita memória.
(2): Ao processar dados maciços, a execução da instrução Atualizar/excluir é uma quantidade enorme e uma instrução ATUALIZAÇÃO/DELETE pode operar apenas um objeto. É concebível que o desempenho do banco de dados seja baixo se for operado com frequência.
Depois que o Hibernate3 foi lançado, a atualização/exclusão em massa foi introduzida para operações de atualização/exclusão de lote. O princípio é preencher operações de atualização/exclusão em lote por meio de uma instrução HQL, que é muito semelhante às operações de atualização/exclusão do JDBC. Em termos de desempenho, há uma grande melhoria sobre as atualizações de lote/exclusão do Hibernate2.
Transação tx = session.beginsession (); string hql = "excluir aluno"; query query = session.createQuery (hql); int size = query.executeUpdate (); tx.commit (); ...
O console produz apenas uma declaração de exclusão hibernate: exclua do t_student. A execução da declaração é menor e o desempenho é quase o mesmo que o uso do JDBC. É uma boa maneira de melhorar o desempenho. Obviamente, para ter melhor desempenho, o autor recomenda que as atualizações de lote e as operações de exclusão ainda usem o JDBC. Os métodos e pontos de conhecimento básicos são basicamente os mesmos que o método de inserção de lote acima, por isso não o descrevo de forma redundante aqui.
Aqui, eu forneço outro método, que é considerar melhorar o desempenho do lado do banco de dados e de chamadas de procedimentos armazenados no lado do programa Hibernate. Os procedimentos armazenados são executados no lado do banco de dados, mais rápido. Tomando atualizações em lote como exemplo, o código de referência é fornecido.
Primeiro, crie um procedimento armazenado chamado BatchupDatestudent no lado do banco de dados:
Crie ou substitua Produzir BatchUpDateStudent (um número) ASBEGINUPDATE ADENTE DE ANENTE AGE = Idade+1 onde a idade> a; fim;
O código de chamada é o seguinte:
Transação tx = session.beginsession (); conexão conn = session.connection (); string pd = "… {chamada BatchUpDatestudent (?)}"; Callablestatement cstmt = Conn.Preparecall (pd); cstmt.setInt (1, 20); // Defina o parâmetro de idade como 20tx.Commit ();Observando o código acima, ele também ignora a API do Hibernate e usa a API JDBC para chamar procedimentos armazenados e usa os limites da transação do Hibernate. Os procedimentos armazenados são, sem dúvida, uma boa maneira de melhorar o desempenho do processamento de lote. Eles são executados diretamente com o lado do banco de dados e, em certa medida, transferem a pressão do processamento em lote para o banco de dados.
PostScript
Este artigo discute as operações de processamento em lote da Hibernate, e o ponto de partida é considerar melhorar o desempenho e fornece apenas um pequeno aspecto para melhorar o desempenho.
Não importa qual método seja adotado, ele deve ser considerado com base nas condições reais. Fornecer aos usuários um sistema eficiente e estável que atenda às suas necessidades é a principal prioridade.
Espero que este artigo seja útil para a programação de hibernação de todos.