{"id":18057001,"url":"https://github.com/rafaelrok/spring-batch-performance","last_synced_at":"2026-01-29T20:03:44.245Z","repository":{"id":260121872,"uuid":"880319876","full_name":"rafaelrok/spring-batch-performance","owner":"rafaelrok","description":"Esse repositório é utilizado para demostrar os projetos que trabalhei na performance do spring batch em situações reais","archived":false,"fork":false,"pushed_at":"2024-11-28T18:40:54.000Z","size":4761,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-04-05T10:23:52.717Z","etag":null,"topics":["backend","java","java-8","mysql","performance","performance-analysis","performance-monitoring","spring-batch","spring-batch-jobs","spring-boot","spring-mvc","visualvm"],"latest_commit_sha":null,"homepage":"https://docs.spring.io/spring-batch/reference/index.html","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/rafaelrok.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-10-29T14:11:16.000Z","updated_at":"2024-12-29T22:29:10.000Z","dependencies_parsed_at":"2024-11-25T16:30:53.715Z","dependency_job_id":null,"html_url":"https://github.com/rafaelrok/spring-batch-performance","commit_stats":null,"previous_names":["rafaelrok/spring-batch-performance"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/rafaelrok/spring-batch-performance","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rafaelrok%2Fspring-batch-performance","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rafaelrok%2Fspring-batch-performance/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rafaelrok%2Fspring-batch-performance/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rafaelrok%2Fspring-batch-performance/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/rafaelrok","download_url":"https://codeload.github.com/rafaelrok/spring-batch-performance/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rafaelrok%2Fspring-batch-performance/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28884016,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-29T19:55:09.949Z","status":"ssl_error","status_checked_at":"2026-01-29T19:55:08.490Z","response_time":59,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["backend","java","java-8","mysql","performance","performance-analysis","performance-monitoring","spring-batch","spring-batch-jobs","spring-boot","spring-mvc","visualvm"],"created_at":"2024-10-31T02:06:16.298Z","updated_at":"2026-01-29T20:03:44.227Z","avatar_url":"https://github.com/rafaelrok.png","language":"Java","readme":"![Spring Batch Performance Optimization](resources/image.png)\r\n\u003cp align=\"center\"\u003e\r\n    \u003cimg alt=\"GitHub branch status\" src=\"https://img.shields.io/github/checks-status/rafaelrok/spring-batch-performance/main\"\u003e\r\n     \u003c/a\u003e  \r\n     \u003ca href=\"https://github.com/ms-sales/README.md/commits/master\"\u003e\r\n      \u003cimg alt=\"GitHub last commit\" src=\"https://img.shields.io/github/last-commit/rafaelrok/spring-batch-performance\"\u003e\r\n     \u003c/a\u003e\r\n     \u003ca href=\"https://github.com/rafaelrok/ms-sales/blob/main/LICENSE\"\u003e\r\n      \u003cimg alt=\"GitHub License\" src=\"https://img.shields.io/github/license/rafaelrok/spring-batch-performance?style=flat\"\u003e\r\n     \u003c/a\u003e  \r\n     \u003ca href=\"https://github.com/tgmarinho/README-ecoleta/stargazers\"\u003e\r\n      \u003cimg alt=\"Stargazers\" src=\"https://img.shields.io/github/stars/rafaelrok/spring-batch-performance?style=social\"\u003e\r\n     \u003c/a\u003e\r\n     \u003ca href=\"https://medium.com/@rafael\"\u003e\r\n      \u003cimg alt=\"medium\" src=\"https://img.shields.io/twitter/url?label=Medium\u0026logo=medium\u0026style=social\u0026url=https%3A%2F%2Fmedium.com%2F%40rafael.\"\u003e\r\n     \u003c/a\u003e\r\n  \u003c/p\u003e\r\n  \u003ch2 align=\"center\"\u003e Spring Batch Performance (NÃO FINALIZADO) \u003c/h2\u003e\r\n\r\n## Introdução\r\n\r\nEste repositório contém projetos focados na otimização de performance de jobs usando Spring Batch. O Spring Batch é um framework robusto para processamento em lote, essencial para lidar com grandes volumes de dados de forma eficiente. A otimização de performance no Spring Batch envolve diversas estratégias para garantir que os jobs sejam executados o mais rápido e eficientemente possível. Este README fornece um guia abrangente para entender e implementar essas técnicas de otimização de performance.\r\n\r\n## Tabela de Conteúdos\r\n\r\n- [Visão Geral do Spring Batch](#visão-geral-do-spring-batch)\r\n- [Estratégias de Otimização de Performance](#estratégias-de-otimização-de-performance)\r\n    - [Steps Paralelos](#steps-paralelos)\r\n    - [Remote Chunking](#remote-chunking)\r\n    - [Processamento Assíncrono](#processamento-assíncrono)\r\n    - [Multithreading](#multithreading)\r\n    - [Partitioning Local](#partitioning-local)\r\n- [Fluxograma de Decisão](#fluxograma-de-decisão)\r\n- [Exemplos](#exemplos)\r\n- [Conclusão](#conclusão)\r\n\r\n## Visão Geral do Spring Batch\r\n\r\nSpring Batch é um framework leve e abrangente projetado para habilitar o desenvolvimento de aplicações de lote robustas, vitais para as operações diárias de sistemas empresariais. Ele fornece funções reutilizáveis que são essenciais para o processamento de grandes volumes de registros, incluindo logging/tracing, gerenciamento de transações, estatísticas de processamento de jobs, reinício de jobs, skipping e gerenciamento de recursos.\r\n\r\n## Estratégias de Otimização de Performance\r\n\r\n### Steps Paralelos\r\n\r\nOs steps paralelos permitem que múltiplos steps sejam executados simultaneamente, o que pode reduzir significativamente o tempo total de execução de um job. Isto é particularmente útil quando os steps são independentes uns dos outros.\r\n\r\n**Implementação:**\r\n\r\n- Configure o job para usar um Split para executar steps em paralelo.\r\n- Assegure-se de que os steps não compartilhem recursos que possam causar contenção.\r\n\r\n### Remote Chunking\r\n\r\nO remote chunking envolve distribuir o processamento de chunks para workers remotos. Isso pode ser benéfico quando a lógica de processamento é intensiva em CPU e pode ser paralelizada em múltiplos nós.\r\n\r\n**Implementação:**\r\n\r\n- Utilize Spring Integration para configurar a comunicação entre os nós mestre e trabalhadores.\r\n- Configure o job para enviar chunks para workers remotos processarem.\r\n\r\n### Processamento Assíncrono\r\n\r\nO processamento assíncrono permite que os steps sejam executados de forma assíncrona, o que pode melhorar a performance ao não bloquear a thread principal enquanto espera por um step ser concluído.\r\n\r\n**Implementação:**\r\n\r\n- Use `TaskExecutor` para executar steps de forma assíncrona.\r\n- Assegure-se de tratar corretamente a conclusão do step e cenários de erro.\r\n\r\n### Multithreading\r\n\r\nO multithreading pode ser usado para processar múltiplos itens dentro de um step de forma simultânea. Isto é útil para steps que envolvem operações de I/O ou outras tarefas que podem ser paralelizadas.\r\n\r\n**Implementação:**\r\n\r\n- Configure o step para usar um `TaskExecutor` para multithreading.\r\n- Assegure a segurança das threads ao acessar recursos compartilhados.\r\n\r\n### Partitioning Local\r\n\r\nO partitioning local envolve dividir os dados em partições e processar cada partição em paralelo. Isso pode ser útil quando os dados podem ser facilmente particionados e processados de forma independente.\r\n\r\n**Implementação:**\r\n\r\n- Use um `Partitioner` para dividir os dados em partições.\r\n- Configure o job para processar cada partição em paralelo.\r\n\r\n## Fluxograma de Decisão\r\n\r\nProfiling é o processo de monitorar e analisar o desempenho de uma aplicação para identificar áreas que podem ser otimizadas. No contexto do Spring Batch, o profiling ajuda a identificar gargalos em jobs, que são etapas ou processos que estão causando atrasos ou ineficiências.\r\nO seguinte fluxograma fornece um processo Profiling de decisão para otimizar a performance de jobs no Spring Batch:\r\n\r\n![Fluxograma de Decisão](resources/fluxograma.png)\r\n\r\n1. **Steps Paralelos:** Verifique se o job tem steps independentes.\r\n2. **Gargalo são operações de I/O?:** Determine se o gargalo é devido a operações de I/O.\r\n3. **Gargalo é o processador?:** Determine se o gargalo é o processador.\r\n4. **Possui alta latência de rede?:** Verifique se há alta latência de rede.\r\n5. **Precisa de restart?:** Determine se o job precisa ser reiniciado.\r\n\r\nDependendo das respostas, o fluxograma sugere diferentes estratégias de otimização como steps paralelos, remote chunking, processamento assíncrono e multithreading.\r\n\r\n### **Passos para Profiling de Jobs Spring Batch**\r\n* **Monitoramento de Métricas**: Utilize ferramentas como Spring Boot Actuator para coletar métricas de desempenho. Isso pode incluir o tempo de execução de jobs, o número de itens processados, o número de erros, etc.\r\n* **Análise de Logs**: Examine os logs da aplicação para identificar quais etapas ou processos estão demorando mais. Isso pode ajudar a identificar gargalos.\r\n* **Uso de Ferramentas de Profiling**: Utilize ferramentas como VisualVM, JProfiler ou YourKit para analisar o uso de CPU, memória e I/O. Essas ferramentas podem fornecer insights detalhados sobre onde a aplicação está gastando mais tempo e recursos.\r\n* **Ajuste de Configurações**: Ajuste parâmetros de configuração, como o tamanho do chunk, o intervalo de commit e o número de threads, para otimizar o desempenho.\r\n* **Identificação de Gargalos**: Determine se os gargalos estão relacionados a operações de I/O, processamento ou latência de rede, e aplique as soluções sugeridas.\r\n\r\n## Exemplos\r\n\r\nEste repositório inclui vários projetos de exemplo demonstrando as diferentes estratégias de otimização de performance:\r\n\r\n### Exemplo de `Steps Paralelos`: \r\nDemonstra como configurar e executar steps paralelos.\r\nNo Spring Batch, a estratégia de steps paralelos permite que múltiplos steps sejam executados simultaneamente, o que pode melhorar a performance do processamento de grandes volumes de dados. No seu projeto, isso é configurado usando o Flow e o SimpleAsyncTaskExecutor.  Aqui está um resumo de como isso funciona no seu projeto:  \r\n  - Definição dos Steps: Você tem dois steps definidos, `migrarPessoaStep` e `migrarDadosBancariosStep`, que são responsáveis por processar dados de pessoas e dados bancários, respectivamente.  \r\n  - Configuração dos `Flows`: Cada step é encapsulado em um Flow usando o FlowBuilder. Isso permite que os steps sejam gerenciados como unidades de trabalho independentes.  \r\n  - Configuração do Executor: O `SimpleAsyncTaskExecutor` é usado para executar os flows em paralelo. Este executor cria novas threads para cada flow, permitindo que eles sejam executados simultaneamente.  \r\n  - Combinação dos `Flows`: Os flows são combinados usando o método split, que recebe o `SimpleAsyncTaskExecutor` como parâmetro. Isso indica ao Spring Batch que os flows devem ser executados em paralelo.  \r\n  - Criação do Job: O job é configurado para iniciar com os steps paralelos e finalizar com o método end. O `RunIdIncrementer` é usado para garantir que cada execução do job tenha um ID único.  \r\n  \r\n  #### Aqui está um exemplo de como isso é configurado no seu arquivo ParallelStepJobConfig.java:\r\n  ```java\r\n    @Bean\r\n    public Job parallelStepJob(@Qualifier(\"migrarPessoaStep\") Step migrarPessoaStep,\r\n    @Qualifier(\"migrarDadosBancariosStep\") Step migrarDadosBancariosStep) {\r\n    return jobBuilderFactory\r\n    .get(\"parallelStepJob\")\r\n    .start(stepsParalelos(migrarPessoaStep, migrarDadosBancariosStep))\r\n    .end()\r\n    .incrementer(new RunIdIncrementer())\r\n    .build();\r\n    }\r\n  \r\n    private Flow stepsParalelos(Step migrarPessoaStep, Step migrarDadosBancariosStep) {\r\n    Flow migrarPessoaFlow = migrarPessoaFlow(migrarPessoaStep);\r\n    Flow migrarDadosBancariosFlow = migrarDadosBancariosFlow(migrarDadosBancariosStep);\r\n  \r\n      return new FlowBuilder\u003cFlow\u003e(\"stepsParalelos\")\r\n              .start(migrarPessoaFlow)\r\n              .split(new SimpleAsyncTaskExecutor())\r\n              .add(migrarDadosBancariosFlow)\r\n              .build();\r\n    }\r\n    \r\n    private Flow migrarPessoaFlow(Step migrarPessoaStep) {\r\n    return new FlowBuilder\u003cFlow\u003e(\"migrarPessoaFlow\")\r\n    .start(migrarPessoaStep)\r\n    .build();\r\n    }\r\n    \r\n    private Flow migrarDadosBancariosFlow(Step migrarDadosBancariosStep) {\r\n    return new FlowBuilder\u003cFlow\u003e(\"migrarDadosBancariosFlow\")\r\n    .start(migrarDadosBancariosStep)\r\n    .build();\r\n    }\r\n  ```\r\n  Essa configuração permite que os steps migrarPessoaStep e migrarDadosBancariosStep sejam executados em paralelo, melhorando a eficiência do processamento.\r\n  ##### Aqui a diferença de tempo de execução do job antes e depois da implementação de steps paralelos:\r\n    ![Teste Steps Paralelos](resources/StepsParellelSequencial-1.png)\r\n    Diferença houve uma redução da metade do tempo de execução do job, de **`30s`** para **`13s`**.\r\n    ![Teste Steps Paralelos](resources/StepsParellelSequencial-2.png)\r\n\r\n\r\n### Exemplo de `Remote Chunking`: \r\nMostra como configurar o remote chunking usando Spring Integration.\r\n  ```java\r\n    EM PROGRESSO...\r\n  ```\r\n### Exemplo de `Processamento Assíncrono`: \r\nFornece um exemplo de execução de steps assíncronos.\r\n  A implementação de processamento assíncrono no Spring Batch pode melhorar significativamente a performance dos jobs, especialmente quando lidamos com grandes volumes de dados. A seguir, descrevo as principais adaptações feitas para melhorar a performance do job com base na sua implementação:  \r\n  - **AsyncItemProcessor**:  \r\n  Utilizado para processar itens de forma assíncrona.\r\n  Configurado com um `TaskExecutor` para controlar a quantidade de threads.\r\n  - **AsyncItemWriter**:  \r\n  Utilizado para escrever itens de forma assíncrona.\r\n  Delegado para um `JdbcBatchItemWriter` que escreve no banco de dados.\r\n  - **TaskExecutor**:\r\n  Configurado com um pool de threads para executar tarefas de forma paralela.\r\n  Utilizado tanto no `AsyncItemProcessor` quanto no Step para paralelizar o processamento e a escrita\r\n\r\nAqui um FluxoGrama de como foi implementado o Processamento Assíncrono:\r\n![Fluxograma de Processamento Assíncrono](resources/AsyncProcessamentoFluxograma.png)\r\n\r\nSegue as implementações. Aqui está a refatoração da classe de processamento com a utilização da configuração Assíncrona: PessoaProcessorConfig.\r\n````java\r\n  @Configuration\r\n  public class PessoaProcessorConfig {\r\n  \r\n    private static final RestTemplate restTemplate = new RestTemplate();\r\n  \r\n    @Bean\r\n    public AsyncItemProcessor\u003cPessoa, Pessoa\u003e asyncPessoaProcessor() {\r\n      AsyncItemProcessor\u003cPessoa, Pessoa\u003e processor = new AsyncItemProcessor\u003c\u003e();\r\n      processor.setDelegate(pessoaProcessor());\r\n      processor.setTaskExecutor(taskExecutor());\r\n      return processor;\r\n    }\r\n  \r\n    private ItemProcessor\u003cPessoa, Pessoa\u003e pessoaProcessor() {\r\n      return new ItemProcessor\u003cPessoa, Pessoa\u003e() {\r\n  \r\n        @Override\r\n        public Pessoa process(Pessoa pessoa)  throws Exception {\r\n          try {\r\n              String uri = String.format(\"http://my-json-server.typicode.com/giuliana-bezerra/demo/profile/%d\", pessoa.getId());\r\n              ResponseEntity\u003cString\u003e response = restTemplate.getForEntity(uri, String.class);\r\n              System.out.println(response.getBody());\r\n            } catch (RestClientResponseException e) {\r\n              System.out.println(pessoa.getId());\r\n            }\r\n          return pessoa;\r\n        }\r\n      };\r\n    }\r\n  \r\n    @Bean\r\n    public TaskExecutor taskExecutor() {\r\n      ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();\r\n      taskExecutor.setCorePoolSize(8);\r\n      taskExecutor.setMaxPoolSize(8);\r\n      taskExecutor.setQueueCapacity(8);\r\n      taskExecutor.setThreadNamePrefix(\"async-multiThreaded-\");\r\n      taskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());\r\n      return taskExecutor;\r\n    }\r\n  \r\n  }\r\n````\r\nConfiguração Assíncrona da classe Writer:\r\n````java\r\n  @Configuration\r\npublic class BancoPessoaWriterConfig {\r\n\r\n  @Bean\r\n  public AsyncItemWriter\u003cPessoa\u003e asyncBancoPessoaWriter() {\r\n    AsyncItemWriter\u003cPessoa\u003e asyncItemWriter = new AsyncItemWriter\u003c\u003e();\r\n    asyncItemWriter.setDelegate(bancoPessoaWriter(null));\r\n    return asyncItemWriter;\r\n  }\r\n\r\n  @Bean\r\n  public JdbcBatchItemWriter\u003cPessoa\u003e bancoPessoaWriter(@Qualifier(\"appDataSource\") DataSource dataSource) {\r\n    return new JdbcBatchItemWriterBuilder\u003cPessoa\u003e()\r\n            .dataSource(dataSource)\r\n            .sql(\"INSERT INTO pessoa (id, nome, email, data_nascimento, idade) VALUES (?, ?, ?, ?, ?)\")\r\n            .itemPreparedStatementSetter(itemPreparedStatementSetter())\r\n            .build();\r\n  }\r\n\r\n  private ItemPreparedStatementSetter\u003cPessoa\u003e itemPreparedStatementSetter() {\r\n    return new ItemPreparedStatementSetter\u003cPessoa\u003e() {\r\n\r\n      @Override\r\n      public void setValues(Pessoa pessoa, PreparedStatement ps) throws SQLException {\r\n        ps.setInt(1, pessoa.getId());\r\n        ps.setString(2, pessoa.getNome());\r\n        ps.setString(3, pessoa.getEmail());\r\n        ps.setDate(4, new Date(pessoa.getDataNascimento().getTime()));\r\n        ps.setInt(5, pessoa.getIdade());\r\n      }\r\n\r\n    };\r\n  }\r\n````\r\nConfiguração do Step:\r\n````java\r\n  @Configuration\r\npublic class MigrarPessoaStepConfig {\r\n\r\n  @Autowired\r\n  private StepBuilderFactory stepBuilderFactory;\r\n\r\n  @Autowired\r\n  @Qualifier(\"transactionManagerApp\")\r\n  private PlatformTransactionManager transactionManagerApp;\r\n\r\n  @SuppressWarnings(\"unchecked\")\r\n  @Bean\r\n  public Step migrarPessoaStep(ItemReader\u003cPessoa\u003e arquivoPessoaReader,\r\n                               AsyncItemWriter\u003cPessoa\u003e pessoaWriter,\r\n                               AsyncItemProcessor\u003cPessoa, Pessoa\u003e pessoaProcessor) {\r\n    return ((SimpleStepBuilder\u003cPessoa, Pessoa\u003e) stepBuilderFactory\r\n            .get(\"migrarPessoaStep\")\r\n            .\u003cPessoa, Pessoa\u003echunk(1000)\r\n            .reader(arquivoPessoaReader)\r\n            .processor((ItemProcessor) pessoaProcessor)\r\n            .writer(pessoaWriter)\r\n            .transactionManager(transactionManagerApp))\r\n            .build();\r\n  }\r\n}\r\n````\r\nEssas adaptações permitem que o processamento e a escrita dos dados sejam realizados de forma paralela, utilizando múltiplas threads, o que resulta em uma melhoria significativa na performance do job.\r\nSegue a demonstração de impacto no desempenho do processamento de jobs assíncronos, onde o tempo estava em torno de `4m` para processar os 1000 clientes, após a implementação o tempo de processamento \r\nfoi reduzido para `30s`.\r\n#### Antes:\r\n![Teste Processamento Assíncrono](resources/ProcessamentoAsync-1.png)\r\n#### Depois da implementação:\r\n![Teste Processamento Assíncrono](resources/ProcessamentoAsync-2.png)\r\n\r\n### Exemplo de `Multithreading`: \r\nIlustra como usar multithreading dentro de um step.\r\n#### Classe de configuração do TaskExecutor: \r\n```java\r\n  @Configuration\r\n  public class TaskExecutorConfig {\r\n  \r\n      /**\r\n       * TODO: Método responsável por criar um TaskExecutor para ser utilizado na execução de tarefas em paralelo, Efetuando uma escalabilidade vertical de desempenho.\r\n       * setCorePoolSize: Aqui é informado o numero de threads que deve ser criado ao executar as tarefas.\r\n       * setQueueCapacity: Aqui é informado o numero de tarefas que podem ser enfileiradas.\r\n       * setMaxPoolSize: Aqui é informado o numero máximo de threads que podem ser criadas.\r\n       * @return TaskExecutor - TaskExecutor que será utilizado para executar as tarefas em paralelo.\r\n       */\r\n  \r\n      @Bean\r\n      public TaskExecutor taskExecutor() {\r\n          ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();\r\n          taskExecutor.setCorePoolSize(4);\r\n          taskExecutor.setQueueCapacity(4);\r\n          taskExecutor.setMaxPoolSize(4);\r\n          taskExecutor.setThreadNamePrefix(\"task_executor_Multithread-\");\r\n          return taskExecutor;\r\n      }\r\n  }\r\n  ```\r\n#### Configuração do taskExecutor nos Steps Dados Bancrios e pessoas:\r\n```java\r\n  @Bean\r\npublic Step migrarDadosBancariosStep(ItemReader\u003cDadosBancarios\u003e arquivoDadosBancariosReader,\r\n                                     ItemWriter\u003cDadosBancarios\u003e bancoDadosBancariosWriter,\r\n                                     @Qualifier(\"taskExecutor\") TaskExecutor taskExecutor) {\r\n  return stepBuilderFactory\r\n          .get(\"migrarDadosBancariosStep\")\r\n          .\u003cDadosBancarios, DadosBancarios\u003echunk(1000)\r\n          .reader(arquivoDadosBancariosReader)\r\n          .writer(bancoDadosBancariosWriter)\r\n          .taskExecutor(taskExecutor)\r\n          .transactionManager(transactionManagerApp)\r\n          .build();\r\n}\r\n```\r\n  ```java\r\n    @Bean\r\n    public Step migrarPessoaStep(\r\n            ItemReader\u003cPessoa\u003e arquivoPessoaReader,\r\n            ClassifierCompositeItemWriter\u003cPessoa\u003e pessoaClassifierWriter,\r\n            ItemProcessor\u003cPessoa, Pessoa\u003e pessoaProcessor,\r\n            FlatFileItemWriter\u003cPessoa\u003e arquivoPessoasInvalidasWriter,\r\n            @Qualifier(\"taskExecutor\") TaskExecutor taskExecutor) {\r\n      return stepBuilderFactory\r\n              .get(\"migrarPessoaStep\")\r\n              .\u003cPessoa, Pessoa\u003echunk(1000)\r\n              .reader(arquivoPessoaReader)\r\n              .writer(pessoaClassifierWriter)\r\n              .taskExecutor(taskExecutor)\r\n              .stream(arquivoPessoasInvalidasWriter)\r\n              .transactionManager(transactionManagerApp)\r\n              .build();\r\n    }\r\n  ```\r\n#### Configuração do .saveState(false) nos Reader: Não salva o estado da leitura, pois não é sincronizados e não é thread-safe. Então deve ser desabilitado.\r\nobs.: Esta sendo feito essa configuração pois não é possivel restartar o Job.\r\n```java\r\n    @Bean\r\n\tpublic FlatFileItemReader\u003cPessoa\u003e arquivoPessoaReader() {\r\n\t\treturn new FlatFileItemReaderBuilder\u003cPessoa\u003e()\r\n\t\t\t\t.name(\"arquivoPessoaReader\")\r\n\t\t\t\t.resource(new FileSystemResource(\"./MultithreadingStep/files/pessoas.csv\"))\r\n\t\t\t\t.delimited()\r\n\t\t\t\t.names(\"nome\", \"email\", \"dataNascimento\", \"idade\", \"id\")\r\n\t\t\t\t.addComment(\"--\")\r\n\t\t\t\t.saveState(false)\r\n\t\t\t\t.fieldSetMapper(fieldSetMapper())\r\n\t\t\t\t.build();\r\n\t}\r\n```\r\n```java\r\n    @Bean\r\n\tpublic FlatFileItemReader\u003cDadosBancarios\u003e dadosBancariosReader() {\r\n\t\treturn new FlatFileItemReaderBuilder\u003cDadosBancarios\u003e()\r\n\t\t\t\t.name(\"dadosBancariosReader\")\r\n\t\t\t\t.resource(new FileSystemResource(\"./MultithreadingStep/files/dados_bancarios.csv\"))\r\n\t\t\t\t.delimited()\r\n\t\t\t\t.names(\"pessoaId\", \"agencia\", \"conta\", \"banco\", \"id\")\r\n\t\t\t\t.addComment(\"--\")\r\n\t\t\t\t.saveState(false)\r\n\t\t\t\t.targetType(DadosBancarios.class)\r\n\t\t\t\t.build();\r\n\t}\r\n```\r\n#### Aqui está o resultado do processamento Multithreading, antes e depois da implementação do TaskExecutor como técnica de escalabilidade vertical de desempenho.\r\nAntes da implementação do TaskExecutor, onde é possível observar o tempo de processamento de **`30s`**.\r\n![Teste Multithreading](resources/multithreading-1.png)\r\nResultando após implementação do TaskExecutor, onde é possível observar o tempo de processamento reduzido de **`30s`** para **`9s`**. Isso em uma escala maior de produção poder reduzir em muito o tempo de processamento.\r\n![Teste Multithreading](resources/multithreading-2.png)\r\n\r\n### Exemplo de `Partitioning Local`: \r\nDemonstra como particionar dados e processar partições em paralelo.\r\n  ```java\r\n    EM PROGRESSO...\r\n  ```\r\n\r\n## Conclusão\r\n\r\nOtimizar a performance de jobs no Spring Batch é crucial para lidar com grandes volumes de dados de forma eficiente. Utilizando estratégias como steps paralelos, remote chunking, processamento assíncrono, multithreading e partitioning local, você pode melhorar significativamente a performance dos seus jobs.\r\n\r\n## Desenvolvedor\r\n\u003ctable\u003e\r\n  \u003ctr\u003e\r\n    \u003ctd align=\"center\"\u003e\r\n      \u003ca href=\"#\"\u003e\r\n        \u003cimg src=\"https://avatars.githubusercontent.com/u/8467131?v=4\" width=\"100px;\" alt=\"Foto do Rafael Vieira no GitHub\"/\u003e\u003cbr\u003e\r\n        \u003csub\u003e\r\n          \u003cb\u003eRafael Vieira\u003c/b\u003e\r\n        \u003c/sub\u003e\r\n      \u003c/a\u003e\r\n    \u003c/td\u003e\r\n  \u003c/tr\u003e\r\n\u003c/table\u003e\r\n\u003ctable\u003e\r\n  \u003ctr\u003e\r\n    \u003ca href=\"https://www.linkedin.com/in/rafaelvieira-s/\"\u003e\r\n      \u003cimg alt=\"linkedin\" src=\"https://img.shields.io/twitter/url?label=Linkedin\u0026logo=linkedin\u0026style=social\u0026url=https%3A%2F%2Fwww.linkedin.com%2Fin%2Frafaelvieira-s%2F\"\u003e\r\n    \u003c/a\u003e\r\n    \u003ca href=\"https://medium.com/@rafael\"\u003e\r\n      \u003cimg alt=\"medium\" src=\"https://img.shields.io/twitter/url?label=Medium\u0026logo=medium\u0026style=social\u0026url=https%3A%2F%2Fmedium.com%2F%40rafael.\"\u003e\r\n    \u003c/a\u003e\r\n    \u003ca href = \"mailto:rafaelrok25@gmail.com\"\u003e\r\n      \u003cimg alt=\"gmail\" src=\"https://img.shields.io/twitter/url?label=gmail\u0026logo=gmail\u0026style=social\u0026url=https%3A%2F%2Fmail.google.com%2F\"\u003e\r\n    \u003c/a\u003e\r\n  \u003c/tr\u003e\r\n\u003c/table\u003e\r\n\r\n\r\n## 📝 Licença\r\n\r\nEsse projeto está sob licença. Veja o arquivo [LICENÇA](LICENSE.md) para mais detalhes.\r\n\r\n##  Versões do README\r\n\r\n[⬆ Voltar ao topo](#introdução)\u003cbr\u003e\r\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frafaelrok%2Fspring-batch-performance","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frafaelrok%2Fspring-batch-performance","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frafaelrok%2Fspring-batch-performance/lists"}