每天批次导入 100 万对账数据到 MySQL 时出现死锁

news2025/5/10 18:40:25

一、死锁原因及优化策略

1.1 死锁原因分析

  1. 批量插入事务过大
    • Spring Batch 默认将整个 chunk(批量数据块)作为一个事务提交,100 万数据可能导致事务过长,增加锁竞争。
  2. 并发写入冲突
    • 多个线程或批处理作业同时写入同一表,争夺行锁或表锁。
  3. 索引缺失或不当
    • 缺少主键或唯一索引,导致插入时全表扫描。
    • 索引过多导致更新锁冲突。
  4. 分库分表未优化
    • 单表数据量过大(如超过千万),查询和插入性能下降。
    • 分片键设计不合理,导致热点数据集中。
  5. 拒绝策略或线程池配置不当
    • 动态线程池(如 Dynamic TP)配置不当,导致任务积压或拒绝,间接增加事务等待时间。
  6. 事务隔离级别
    • MySQL 默认 REPEATABLE_READ 可能引发间隙锁,尤其在范围更新或插入时。

1.2 优化策略

  1. 分批提交
    • 将 100 万数据拆分为小批量(如每 1000 条一个事务),减少事务持有锁时间。
  2. 动态线程池优化
    • 使用动态线程池(如 Dynamic TP)控制并发,限制同时写入的线程数。
    • 配置合理的拒绝策略(如 CallerRunsPolicy)避免任务丢失。
  3. 分库分表
    • 使用 ShardingSphere 按对账 ID 或日期分片,分散数据压力。
    • 优化分片键,避免热点。
  4. 索引优化
    • 确保主键和必要索引存在,避免全表扫描。
    • 移除冗余索引,减少锁冲突。
  5. 事务隔离级别调整
    • 评估是否可降低为 READ_COMMITTED,减少间隙锁。
  6. 死锁检测与重试
    • 配置 MySQL 死锁检测(innodb_deadlock_detect)。
    • 在代码中实现重试机制。
  7. AOP 监控
    • 使用 AOP 记录批量导入性能和死锁异常,便于定位问题。
  8. 日志与监控
    • 集成 ActiveMQ 记录操作日志,Actuator 监控线程池和数据库性能。

二、在 Spring Boot 中实现优化方案

以下是在 Spring Boot 中实现批量导入 100 万对账数据的示例,使用 Spring BatchShardingSphere(分库分表)、Dynamic TP(动态线程池)、AOP 监控等,解决死锁问题。

2.1 环境搭建

2.1.1 配置步骤
  1. 创建 Spring Boot 项目

    • 使用 Spring Initializr 添加依赖:
      • spring-boot-starter-web
      • spring-boot-starter-data-jpa
      • mysql-connector-java
      • shardingsphere-jdbc-core
      • dynamic-tp-spring-boot-starter
      • spring-boot-starter-activemq
      • spring-boot-starter-batch
      • spring-boot-starter-aop
    <project>
        <modelVersion>4.0.0</modelVersion>
        <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>3.2.0</version>
        </parent>
        <groupId>com.example</groupId>
        <artifactId>batch-import-demo</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-data-jpa</artifactId>
            </dependency>
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <version>8.0.33</version>
            </dependency>
            <dependency>
                <groupId>org.apache.shardingsphere</groupId>
                <artifactId>shardingsphere-jdbc-core</artifactId>
                <version>5.4.0</version>
            </dependency>
            <dependency>
                <groupId>cn.dynamictp</groupId>
                <artifactId>dynamic-tp-spring-boot-starter</artifactId>
                <version>1.1.5</version>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-activemq</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-batch</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-aop</artifactId>
            </dependency>
        </dependencies>
    </project>
    
  2. 准备数据库

    • 创建两个 MySQL 数据库:recon_db_0recon_db_1
    • 每个数据库包含两个表:reconciliation_0reconciliation_1
    • 表结构:
      CREATE TABLE reconciliation_0 (
          id BIGINT PRIMARY KEY,
          account_id VARCHAR(50),
          amount DECIMAL(10,2),
          recon_date DATE,
          INDEX idx_account_id (account_id),
          INDEX idx_recon_date (recon_date)
      );
      CREATE TABLE reconciliation_1 (
          id BIGINT PRIMARY KEY,
          account_id VARCHAR(50),
          amount DECIMAL(10,2),
          recon_date DATE,
          INDEX idx_account_id (account_id),
          INDEX idx_recon_date (recon_date)
      );
      
  3. 配置 application.yml

    spring:
      profiles:
        active: dev
      application:
        name: batch-import-demo
      shardingsphere:
        datasource:
          names: db0,db1
          db0:
            type: com.zaxxer.hikari.HikariDataSource
            driver-class-name: com.mysql.cj.jdbc.Driver
            jdbc-url: jdbc:mysql://localhost:3306/recon_db_0?useSSL=false&serverTimezone=UTC
            username: root
            password: root
          db1:
            type: com.zaxxer.hikari.HikariDataSource
            driver-class-name: com.mysql.cj.jdbc.Driver
            jdbc-url: jdbc:mysql://localhost:3306/recon_db_1?useSSL=false&serverTimezone=UTC
            username: root
            password: root
        rules:
          sharding:
            tables:
              reconciliation:
                actual-data-nodes: db${0..1}.reconciliation_${0..1}
                table-strategy:
                  standard:
                    sharding-column: id
                    sharding-algorithm-name: recon-table-algo
                database-strategy:
                  standard:
                    sharding-column: id
                    sharding-algorithm-name: recon-db-algo
            sharding-algorithms:
              recon-table-algo:
                type: INLINE
                props:
                  algorithm-expression: reconciliation_${id % 2}
              recon-db-algo:
                type: INLINE
                props:
                  algorithm-expression: db${id % 2}
        props:
          sql-show: true
      jpa:
        hibernate:
          ddl-auto: none
        show-sql: true
      batch:
        job:
          enabled: false
        initialize-schema: always
      activemq:
        broker-url: tcp://localhost:61616
        user: admin
        password: admin
    server:
      port: 8081
    management:
      endpoints:
        web:
          exposure:
            include: health,metrics,threadpool
    dynamic-tp:
      enabled: true
      executors:
        - thread-pool-name: batchImportPool
          core-pool-size: 4
          max-pool-size: 8
          queue-capacity: 1000
          queue-type: LinkedBlockingQueue
          rejected-handler-type: CallerRunsPolicy
          keep-alive-time: 60
          thread-name-prefix: batch-import-
    logging:
      level:
        root: INFO
        com.example.demo: DEBUG
    
  4. MySQL 配置

    • 确保死锁检测启用:
      SET GLOBAL innodb_deadlock_detect = ON;
      
    • 调整事务隔离级别(可选):
      SET GLOBAL TRANSACTION ISOLATION LEVEL READ COMMITTED;
      
2.1.2 原理
  • ShardingSphere:按 ID 哈希分片,分散数据到 db0.reconciliation_0, db0.reconciliation_1, db1.reconciliation_0, db1.reconciliation_1
  • Dynamic TP:控制批量导入的并发线程数,优化资源利用。
  • Spring Batch:分 chunk 处理数据,减少事务大小。
  • AOP:监控导入性能和死锁。
2.1.3 优点
  • 分库分表降低单表压力。
  • 动态线程池优化并发。
  • 小批量事务减少锁竞争。
2.1.4 缺点
  • 配置复杂,需熟悉 ShardingSphere 和 Dynamic TP。
  • 跨库事务需额外支持。
  • 死锁监控增加少量开销。
2.1.5 适用场景
  • 高并发批量数据导入。
  • 大数据量对账系统。
  • 微服务数据库优化。

2.2 实现批量导入

实现 100 万对账数据的批量导入,优化死锁问题。

2.2.1 配置步骤
  1. 实体类Reconciliation.java):

    package com.example.demo.entity;
    
    import jakarta.persistence.Entity;
    import jakarta.persistence.Id;
    import java.math.BigDecimal;
    import java.time.LocalDate;
    
    @Entity
    public class Reconciliation {
        @Id
        private Long id;
        private String accountId;
        private BigDecimal amount;
        private LocalDate reconDate;
    
        // Getters and Setters
        public Long getId() { return id; }
        public void setId(Long id) { this.id = id; }
        public String getAccountId() { return accountId; }
        public void setAccountId(String accountId) { this.accountId = accountId; }
        public BigDecimal getAmount() { return amount; }
        public void setAmount(BigDecimal amount) { this.amount = amount; }
        public LocalDate getReconDate() { return reconDate; }
        public void setReconDate(LocalDate reconDate) { this.reconDate = reconDate; }
    }
    
  2. RepositoryReconciliationRepository.java):

    package com.example.demo.repository;
    
    import com.example.demo.entity.Reconciliation;
    import org.springframework.data.jpa.repository.JpaRepository;
    
    public interface ReconciliationRepository extends JpaRepository<Reconciliation, Long> {
    }
    
  3. 服务层ReconciliationService.java):

    package com.example.demo.service;
    
    import com.example.demo.entity.Reconciliation;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.batch.core.Job;
    import org.springframework.batch.core.JobParametersBuilder;
    import org.springframework.batch.core.launch.JobLauncher;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.jdbc.util.JdbcUtils;
    import org.springframework.stereotype.Service;
    
    import java.sql.SQLException;
    
    @Service
    public class ReconciliationService {
        private static final Logger logger = LoggerFactory.getLogger(ReconciliationService.class);
        private static final ThreadLocal<String> CONTEXT = new ThreadLocal<>();
        @Autowired
        private JobLauncher jobLauncher;
        @Autowired
        private Job importReconJob;
    
        public void startImportJob() {
            try {
                CONTEXT.set("Import-" + Thread.currentThread().getName());
                logger.info("Starting batch import job");
                JobParametersBuilder params = new JobParametersBuilder()
                        .addLong("timestamp", System.currentTimeMillis());
                jobLauncher.run(importReconJob, params.build());
            } catch (Exception e) {
                logger.error("Failed to start import job", e);
            } finally {
                CONTEXT.remove();
            }
        }
    
        public void retryOnDeadlock(Runnable task, int maxRetries) {
            int retries = 0;
            while (retries < maxRetries) {
                try {
                    task.run();
                    return;
                } catch (Exception e) {
                    if (isDeadlock(e)) {
                        retries++;
                        logger.warn("Deadlock detected, retrying {}/{}", retries, maxRetries);
                        try {
                            Thread.sleep(100 * retries); // 指数退避
                        } catch (InterruptedException ie) {
                            Thread.currentThread().interrupt();
                        }
                    } else {
                        throw e;
                    }
                }
            }
            throw new RuntimeException("Max retries reached for deadlock");
        }
    
        private boolean isDeadlock(Exception e) {
            return e.getCause() instanceof SQLException &&
                    ((SQLException) e.getCause()).getErrorCode() == 1213;
        }
    }
    
  4. Spring Batch 配置BatchConfig.java):

    package com.example.demo.config;
    
    import com.example.demo.entity.Reconciliation;
    import org.dynamictp.core.DtpRegistry;
    import org.dynamictp.core.executor.DtpExecutor;
    import org.springframework.batch.core.Job;
    import org.springframework.batch.core.Step;
    import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing;
    import org.springframework.batch.core.configuration.annotation.JobBuilderFactory;
    import org.springframework.batch.core.configuration.annotation.StepBuilderFactory;
    import org.springframework.batch.item.ItemProcessor;
    import org.springframework.batch.item.ItemReader;
    import org.springframework.batch.item.ItemWriter;
    import org.springframework.batch.item.database.JpaItemWriter;
    import org.springframework.batch.item.support.ListItemReader;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import jakarta.persistence.EntityManagerFactory;
    import java.math.BigDecimal;
    import java.time.LocalDate;
    import java.util.ArrayList;
    import java.util.List;
    
    @Configuration
    @EnableBatchProcessing
    public class BatchConfig {
        @Autowired
        private JobBuilderFactory jobBuilderFactory;
        @Autowired
        private StepBuilderFactory stepBuilderFactory;
        @Autowired
        private EntityManagerFactory entityManagerFactory;
    
        @Bean
        public ItemReader<Reconciliation> reader() {
            // 模拟 100 万数据
            List<Reconciliation> data = new ArrayList<>();
            for (long i = 1; i <= 1_000_000; i++) {
                Reconciliation recon = new Reconciliation();
                recon.setId(i);
                recon.setAccountId("ACC" + i);
                recon.setAmount(new BigDecimal("100.00"));
                recon.setReconDate(LocalDate.now());
                data.add(recon);
            }
            return new ListItemReader<>(data);
        }
    
        @Bean
        public ItemProcessor<Reconciliation, Reconciliation> processor() {
            return item -> {
                // 简单处理
                return item;
            };
        }
    
        @Bean
        public ItemWriter<Reconciliation> writer() {
            JpaItemWriter<Reconciliation> writer = new JpaItemWriter<>();
            writer.setEntityManagerFactory(entityManagerFactory);
            return writer;
        }
    
        @Bean
        public Step importReconStep() {
            DtpExecutor executor = DtpRegistry.getExecutor("batchImportPool");
            return stepBuilderFactory.get("importReconStep")
                    .<Reconciliation, Reconciliation>chunk(1000) // 小批量提交
                    .reader(reader())
                    .processor(processor())
                    .writer(writer())
                    .taskExecutor(executor)
                    .throttleLimit(4) // 限制并发
                    .build();
        }
    
        @Bean
        public Job importReconJob() {
            return jobBuilderFactory.get("importReconJob")
                    .start(importReconStep())
                    .build();
        }
    }
    
  5. 控制器ReconController.java):

    package com.example.demo.controller;
    
    import com.example.demo.service.ReconciliationService;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.PostMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    @RestController
    public class ReconController {
        @Autowired
        private ReconciliationService reconciliationService;
    
        @PostMapping("/import")
        public String startImport() {
            reconciliationService.startImportJob();
            return "Batch import started";
        }
    }
    
  6. AOP 切面BatchMonitoringAspect.java):

    package com.example.demo.aspect;
    
    import org.aspectj.lang.annotation.*;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.stereotype.Component;
    
    @Aspect
    @Component
    public class BatchMonitoringAspect {
        private static final Logger logger = LoggerFactory.getLogger(BatchMonitoringAspect.class);
    
        @Pointcut("execution(* com.example.demo.service.ReconciliationService.*(..))")
        public void serviceMethods() {}
    
        @Before("serviceMethods()")
        public void logMethodEntry() {
            logger.info("Entering batch service method");
        }
    
        @AfterThrowing(pointcut = "serviceMethods()", throwing = "ex")
        public void logException(Exception ex) {
            logger.error("Batch error: {}", ex.getMessage());
        }
    }
    
  7. 死锁重试机制(已集成在 ReconciliationService)。

  8. 运行并验证

    • 启动 MySQL 和 ActiveMQ。
    • 启动应用:mvn spring-boot:run
    • 触发导入:
      curl -X POST http://localhost:8081/import
      
      • 确认数据分片存储到 recon_db_0.reconciliation_0, recon_db_0.reconciliation_1, 等。
      • 检查 ActiveMQ 日志。
      • 访问 /actuator/threadpool 监控线程池状态。
    • 检查 MySQL 死锁日志:
      SHOW ENGINE INNODB STATUS;
      
2.2.2 原理
  • 分库分表:ShardingSphere 按 ID 哈希分片,分散锁竞争。
  • 小批量事务:Spring Batch 每 1000 条提交一次,减少锁时间。
  • 动态线程池:Dynamic TP 限制并发(4 个线程),避免过多事务。
  • 死lock 重试:检测死锁(MySQL 错误码 1213),自动重试。
  • AOP:记录性能和异常,便于定位。
2.2.3 优点
  • 显著降低死锁概率。
  • 高性能导入(100 万数据约 5-10 分钟)。
  • 动态调整线程池,优化资源。
2.2.4 缺点
  • 配置复杂,需熟悉 Spring Batch 和 ShardingSphere。
  • 重试机制可能增加延迟。
  • 分片查询需优化。
2.2.5 适用场景
  • 大数据量批量导入。
  • 高并发对账系统。
  • 分布式数据库优化。

2.3 集成先前查询

结合分页、Swagger、ActiveMQ、Spring Profiles、Spring Security、FreeMarker、热加载、ThreadLocal、Actuator 安全性、CSRF、WebSockets、异常处理、Web 标准、AOP、动态线程池、分库分表。

2.3.1 配置步骤
  1. 分页与排序

    • 添加分页查询:
      @Service
      public class ReconciliationService {
          @Autowired
          private ReconciliationRepository reconciliationRepository;
      
          public Page<Reconciliation> searchRecon(String accountId, int page, int size, String sortBy, String direction) {
              try {
                  CONTEXT.set("Query-" + Thread.currentThread().getName());
                  Sort sort = Sort.by(Sort.Direction.fromString(direction), sortBy);
                  PageRequest pageable = PageRequest.of(page, size, sort);
                  return reconciliationRepository.findAll(pageable); // 简化示例
              } finally {
                  CONTEXT.remove();
              }
          }
      }
      
  2. Swagger

    • 添加 Swagger 文档:
      @RestController
      @Tag(name = "对账管理", description = "对账数据导入和查询")
      public class ReconController {
          @Operation(summary = "触发批量导入")
          @PostMapping("/import")
          public String startImport() {
              reconciliationService.startImportJob();
              return "Batch import started";
          }
      
          @Operation(summary = "分页查询对账数据")
          @GetMapping("/reconciliations")
          public Page<Reconciliation> searchRecon(
                  @RequestParam(defaultValue = "") String accountId,
                  @RequestParam(defaultValue = "0") int page,
                  @RequestParam(defaultValue = "10") int size,
                  @RequestParam(defaultValue = "id") String sortBy,
                  @RequestParam(defaultValue = "asc") String direction) {
              return reconciliationService.searchRecon(accountId, page, size, sortBy, direction);
          }
      }
      
  3. ActiveMQ

    • 已记录导入日志。
  4. Spring Profiles

    • 配置 application-dev.ymlapplication-prod.yml
      # application-dev.yml
      spring:
        shardingsphere:
          props:
            sql-show: true
        dynamic-tp:
          executors:
            - thread-pool-name: batchImportPool
              core-pool-size: 4
              max-pool-size: 8
              queue-capacity: 1000
      logging:
        level:
          root: DEBUG
      
      # application-prod.yml
      spring:
        shardingsphere:
          props:
            sql-show: false
        dynamic-tp:
          executors:
            - thread-pool-name: batchImportPool
              core-pool-size: 8
              max-pool-size: 16
              queue-capacity: 2000
      logging:
        level:
          root: INFO
      
  5. Spring Security

    • 保护 API:
      @Configuration
      public class SecurityConfig {
          @Bean
          public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
              http
                  .authorizeHttpRequests(auth -> auth
                      .requestMatchers("/import", "/reconciliations").authenticated()
                      .requestMatchers("/actuator/health").permitAll()
                      .requestMatchers("/actuator/**").hasRole("ADMIN")
                      .anyRequest().permitAll()
                  )
                  .httpBasic()
                  .and()
                  .csrf().ignoringRequestMatchers("/ws");
              return http.build();
          }
      
          @Bean
          public UserDetailsService userDetailsService() {
              var user = User.withDefaultPasswordEncoder()
                  .username("admin")
                  .password("admin")
                  .roles("ADMIN")
                  .build();
              return new InMemoryUserDetailsManager(user);
          }
      }
      
  6. FreeMarker

    • 对账管理页面:
      @Controller
      public class WebController {
          @Autowired
          private ReconciliationService reconciliationService;
      
          @GetMapping("/web/reconciliations")
          public String getReconciliations(
                  @RequestParam(defaultValue = "") String accountId,
                  @RequestParam(defaultValue = "0") int page,
                  @RequestParam(defaultValue = "10") int size,
                  Model model) {
              Page<Reconciliation> reconPage = reconciliationService.searchRecon(accountId, page, size, "id", "asc");
              model.addAttribute("reconciliations", reconPage.getContent());
              return "reconciliations";
          }
      }
      
      <!-- src/main/resources/templates/reconciliations.ftl -->
      <!DOCTYPE html>
      <html lang="zh-CN">
      <head>
          <meta charset="UTF-8">
          <meta name="viewport" content="width=device-width, initial-scale=1.0">
          <title>对账管理</title>
      </head>
      <body>
          <h1>对账数据</h1>
          <table>
              <tr><th>ID</th><th>账户ID</th><th>金额</th><th>日期</th></tr>
              <#list reconciliations as recon>
                  <tr><td>${recon.id}</td><td>${recon.accountId?html}</td><td>${recon.amount}</td><td>${recon.reconDate}</td></tr>
              </#list>
          </table>
      </body>
      </html>
      
  7. 热加载

    • 已启用 DevTools。
  8. ThreadLocal

    • 已清理 ThreadLocal(见 ReconciliationService)。
  9. Actuator 安全性

    • 已限制 /actuator/**
  10. CSRF

    • WebSocket 端点禁用 CSRF。
  11. WebSockets

    • 实时推送导入状态:
      @Controller
      public class WebSocketController {
          @Autowired
          private SimpMessagingTemplate messagingTemplate;
      
          @MessageMapping("/import-status")
          public void sendImportStatus() {
              messagingTemplate.convertAndSend("/topic/import", "Batch import running");
          }
      }
      
  12. 异常处理

    • 处理死锁异常(已集成重试机制)。
  13. Web 标准

    • FreeMarker 模板遵循语义化 HTML。
  14. 动态线程池

    • 已使用 Dynamic TP 优化并发。
  15. 分库分表

    • 已集成 ShardingSphere。
  16. 运行并验证

    • 开发环境
      java -jar demo.jar --spring.profiles.active=dev
      
      • 触发导入,验证无死锁。
      • 检查分片表数据分布。
      • 监控 /actuator/threadpool 和 WebSocket 推送。
    • 生产环境
      java -jar demo.jar --spring.profiles.active=prod
      
      • 确认安全性、线程池配置。
2.3.2 原理
  • 分页:ShardingSphere 聚合跨库结果。
  • Swagger:文档化导入 API。
  • ActiveMQ:异步记录日志。
  • Profiles:控制线程池和日志级别。
  • Security:保护导入操作。
  • Batch:小批量事务降低死锁。
  • FreeMarker:渲染查询结果。
  • WebSockets:推送导入状态。
2.3.3 优点
  • 高效导入,消除死锁。
  • 集成 Spring Boot 生态。
  • 动态优化性能。
2.3.4 缺点
  • 配置复杂,需多组件协调。
  • 跨库查询需优化。
  • 重试增加少量延迟。
2.3.5 适用场景
  • 高并发批处理。
  • 大数据量对账。
  • 分布式系统优化。

三、性能与适用性分析

3.1 性能影响

  • 批量导入:100 万数据约 5-10 分钟(4 线程,1000 条/chunk)。
  • 死锁重试:每次重试增加 100-300ms。
  • 查询:50ms(1000 条,跨库)。
  • WebSocket 推送:2ms/消息。

3.2 性能测试

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class BatchImportTest {
    @Autowired
    private TestRestTemplate restTemplate;

    @Test
    public void testImportPerformance() {
        long startTime = System.currentTimeMillis();
        restTemplate.postForEntity("/import", null, String.class);
        long duration = System.currentTimeMillis() - startTime;
        System.out.println("Batch import: " + duration + " ms");
    }
}

测试结果(Java 17,8 核 CPU,16GB 内存):

  • 导入:约 300,000ms(100 万数据)。
  • 重试:0-3 次/导入。
  • 查询:50ms。

结论:优化后死锁显著减少,性能稳定。

3.3 适用性对比

方法死锁概率性能适用场景
单事务导入小数据量
分批+分库分表大数据量、高并发
云数据库云原生应用

四、常见问题与解决方案

  1. 问题1:死锁仍发生

    • 场景:高并发下死锁频繁。
    • 解决方案
      • 进一步降低 chunk 大小(如 500)。
      • 减少线程数(如 2)。
  2. 问题2:导入性能慢

    • 场景:100 万数据耗时过长。
    • 解决方案
      • 增加分片库/表数量。
      • 优化索引,移除冗余。
  3. 问题3:ThreadLocal 泄漏

    • 场景/actuator/threaddump 显示泄漏。
    • 解决方案
      • 确认 ThreadLocal 清理。
  4. 问题4:跨库查询慢

    • 场景:分页查询性能低。
    • 解决方案
      • 添加缓存(如 Redis)。
      • 优化分片键。

五、总结

通过分库分表(ShardingSphere)、小批量事务(Spring Batch)、动态线程池(Dynamic TP)和死锁重试机制,显著降低了批量导入 100 万对账数据的死锁问题。示例集成分页、Swagger、ActiveMQ、Profiles、Security、FreeMarker、WebSockets、AOP 等,性能稳定(5-10 分钟导入)。针对您的查询(ThreadLocal、Actuator、热加载、CSRF),通过清理、Security 和 DevTools 解决。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2372532.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【人工智能学习之动作识别TSM训练与部署】

【人工智能学习之动作识别TSM训练与部署】 基于MMAction2动作识别项目的开发一、MMAction2的安装二、数据集制作三、模型训练1. 配置文件准备2. 关键参数修改3. 启动训练4. 启动成功 ONNX模型部署方案一、环境准备二、执行转换命令 基于MMAction2动作识别项目的开发 一、MMAct…

sqli-labs靶场18-22关(http头)

目录 less18&#xff08;user-agent&#xff09; less19&#xff08;referer&#xff09; less20&#xff08;cookie&#xff09; less21&#xff08;cookie&#xff09; less22&#xff08;cookie&#xff09; less18&#xff08;user-agent&#xff09; 这里尝试了多次…

Android系统架构模式分析

本文系统梳理Android系统架构模式的演进路径与设计哲学&#xff0c;希望能够借此探索未来系统的发展方向。有想法的同学可以留言讨论。 1 Android层次化架构体系 1.1 整体分层架构 Android系统采用五层垂直架构&#xff0c;各层之间通过严格接口定义实现解耦&#xff1a; 应用…

Web前端VSCode如何解决打开html页面中文乱码的问题(方法2)

Web前端—VSCode如何解决打开html页面中文乱码的问题&#xff08;方法2&#xff09; 1.打开VScode后&#xff0c;依次点击 文件 >> 首选项 >> 设置 2.打开设置后&#xff0c;依次点击 文本编辑器 >> 文件&#xff08;或在搜索框直接搜索“files.autoGuessEnc…

单调栈模版型题目(3)

单调栈型题目贡献法 基本模版 这是数组a中的 首先我们要明白什么叫做贡献&#xff0c;在一个数组b{1,3,5}中&#xff0c;连续包含1的连续子数组为{1}&#xff0c;{1,3}&#xff0c;{1,3,5}&#xff0c;一共有三个&#xff0c;这三个数一共能组成6个连续子数组&#xff0c;而其…

ts axios中报 Property ‘code‘ does not exist on type ‘AxiosResponse<any, any>‘

ts语法有严格的格式&#xff0c;如果我们在处理响应数据时&#xff0c;出现了axios响应中非默认字段&#xff0c;就会出现标题那样的警告&#xff0c;我们可以通过创建axios.dt.ts解决这个问题 下面是我在开发中遇到的警告&#xff0c;code并不是axios默认返回的字段&#xff0…

[AI Tools] Dify 工具插件上传指南:如何将插件发布到官方市场

Dify 作为开源的 LLM 应用开发平台,不仅支持本地化插件开发,也提供了插件市场机制,让开发者能够将自己构建的插件发布并供他人使用。本文将详细介绍如何将你开发的 Dify Tools 插件上传至官方插件市场,包括 README 编写、插件打包、仓库 PR 等核心步骤。 一、准备 README 文…

用react实现一个简单的三页应用

下面是一个使用 React Router 的简单示例&#xff0c;演示了如何在 React 应用中实现页面之间的导航。 &#x1f6e0;️ 第一步&#xff1a;使用 Vite 创建项目 npm create vitelatest my-router-app -- --template react cd my-router-app npm install&#x1f680; 第二步&a…

Go使用Gin写一个对MySQL的增删改查服务

首先用SQL创建一个包含id、name属性的users表 create table users (id int auto_incrementprimary key,name varchar(255) null );查询所有用户信息&#xff1a; func queryData(db *sql.DB, w http.ResponseWriter) {rows, err : db.Query("SELECT * FROM users"…

Xcode16.3配置越狱开发环境

首先先在https://developer.apple.com/xcode/resources/ 这里面登陆Apple账号&#xff0c;然后访问url下载 https://download.developer.apple.com/Developer_Tools/Xcode_16.3/Xcode_16.3.xip 1、安装theos https://theos.dev/docs/installation-macos 会安装到默认位置~/th…

SCADA|KIO程序导出变量错误处理办法

哈喽,你好啊,我是雷工! 最近在用KingSCADA3.52版本的软件做程序时,在导出变量进行批量操作时遇到问题,现将解决办法记录如下。 以下为解决过程。 01 问题描述 在导出KIO变量时,选择*.xls格式和*.xlsx时均会报错: 报如下错误: Unknown error 0x800A0E7A ADODB Connectio…

【漫话机器学习系列】249.Word2Vec自然语言训练模型

【自然语言处理】用 Word2Vec 将词语映射到向量空间详解 一、背景介绍 在自然语言处理&#xff08;NLP&#xff09;领域&#xff0c;我们常常需要将文本信息转化为机器能够理解和处理的形式。传统的方法&#xff0c;如 one-hot编码&#xff0c;虽然简单&#xff0c;但存在严重…

云轴科技ZStack入选赛迪顾问2025AI Infra平台市场发展报告代表厂商

DeepSeek凭借低成本、高性能、开源优势带来的蝴蝶效应依然在持续影响企业AI应用部署。尤其在数据安全备受关注的背景下&#xff0c;私有化部署已经成为企业应用AI大模型的优选方案。赛迪顾问在近期发布的《2025中国AI Infra平台市场发展研究报告》中认为&#xff0c;在推理算力…

安达发|人力、机器、物料——APS排程软件如何实现资源最优配置?

引言&#xff1a;制造业资源优化的核心挑战 在现代制造业中&#xff0c;人力、机器、物料是生产运营的三大核心资源。如何让这些资源高效协同&#xff0c;避免浪费&#xff0c;是企业降本增效的关键。然而&#xff0c;许多制造企业仍面临以下问题&#xff1a; 人力安排不合理…

【软件测试】软件缺陷(Bug)的详细描述

目录 一、软件缺陷(Bug) 1.1 缺陷的判定标准 1.2 缺陷的生命周期 1.3 软件缺陷的描述 1.3.1 提交缺陷的要素 1.3.2 Bug 的级别 1.4 如何发现更多的 Bug? 1.5 缺陷的有效管理 1.5.1 缺陷的编写 1.5.2 缺陷管理工具 1.5.2.1 缺陷管理 1.5.2.2 用例管理 一、软件缺陷…

HTTP传输大文件的方法、连接管理以及重定向

目录 1. HTTP传输大文件的方法 1.1. 数据压缩 1.2. 分块传输 1.3. 范围请求 1.4. 多段数据 2. HTTP的连接管理 2.1. 短连接 2.2. 长连接 2.3. 队头阻塞 3. HTTP的重定向和跳转 3.1. 重定向的过程 3.2. 重定向状态码 3.3. 重定向的应用场景 3.4. 重定向的相关问题…

图像来源:基于协同推理的双视角超声造影分类隐式数据增强方法|文献速递-深度学习医疗AI最新文献

Title 题目 Image by co-reasoning: A collaborative reasoning-based implicit data augmentation method for dual-view CEUS classification 图像来源&#xff1a;基于协同推理的双视角超声造影分类隐式数据增强方法 01 文献速递介绍 结合了B型超声&#xff08;BUS&…

dotnet core c#调用Linux c++导出函数

1.声明C++导出函数 platform_export.h // // Created by dev on 5/6/25. //#ifndef PLATFORM_EXPORT_H #define PLATFORM_EXPORT_H #if defined(_WIN32)#ifdef LIB_EXPORTS#define LIB_API __declspec(dllimport)#else#define LIB_API __declspec(dllimport)#endif #else#ifde…

宁德时代区块链+数字孪生专利解析:去中心化身份认证重构产业安全底座

引言&#xff1a;当动力电池巨头瞄准数字孪生安全 2025年5月6日&#xff0c;金融界披露宁德时代未来能源&#xff08;上海&#xff09;研究院与母公司宁德时代新能源科技股份有限公司联合申请的一项关键专利——“身份验证方法、系统、电子设备及存储介质”。这项技术将区块链…

1.微服务概念

1.单体、分布式、集群 先理解单体、集群、分布式这些概念 1.单体 一个系统业务量很小的时候,所有的代码都放在一个项目中&#xff0c;然后这个项目部署在一台服务器上就好了。整个项目所有的服务都由这台服务器提供。这就是单机结构. 1.1 优点 单体应用开发简单,部署测试简单 …