☰
MySQL批量插入与高并发ID生成优化实战
2026/11/3 16:17:39 网站建设 项目流程

1. 数据库批量插入性能优化实战

作为一名长期奋战在一线的Java开发者,我经常需要处理海量数据导入的场景。今天要分享的是我在实际项目中积累的一套完整的数据库优化方案,涵盖了从批量插入优化、唯一编号生成到慢SQL监控的全套解决方案。

1.1 告别低效的foreach批量插入

很多开发者习惯在MyBatis中使用foreach标签进行批量插入,像这样:

<insert id="insertList" parameterType="list"> insert into t_sample (id, sampleNo, sampleName) values <foreach collection="list" item="item" separator=","> (#{item.id}, #{item.sampleNo}, #{item.sampleName}) </foreach> </insert>

这种方式的致命缺陷在于:当数据量达到数千条时,会生成一条极其庞大的SQL语句。我曾经遇到过一次性插入5000条记录,生成的SQL超过1MB,不仅导致内存压力大,而且MySQL服务器解析这样的SQL也会消耗大量CPU资源。

优化方案:启用rewriteBatchedStatements参数 + MyBatis-Plus的saveBatch方法

  1. 在application.properties中添加参数:
spring.datasource.url=jdbc:mysql://localhost:3306/db?rewriteBatchedStatements=true
  1. 替换Service层代码:
// 旧方式(性能差) // mapper.insertList(dataList); // 新方式(高性能) this.saveBatch(dataList, 1000); // 每1000条批量提交一次

技术原理:rewriteBatchedStatements=true会启用JDBC的批量操作优化,将多个INSERT语句转换为高效的二进制协议格式传输。实测在插入1万条记录时,性能提升可达5-8倍。

1.2 批量插入的黄金参数配置

除了rewriteBatchedStatements,还有几个关键参数会影响批量插入性能:

# 连接池配置(使用Druid为例) spring.datasource.druid.max-active=50 # 根据服务器配置调整 spring.datasource.druid.initial-size=10 # 初始化连接数 spring.datasource.druid.min-idle=10 # 最小空闲连接 # MySQL特有优化 spring.datasource.druid.connection-properties=useServerPrepStmts=true;cachePrepStmts=true;prepStmtCacheSize=250;prepStmtCacheSqlLimit=2048

避坑指南:

  1. 批量大小建议设置在500-2000之间,太小达不到批量效果,太大会增加内存压力
  2. 事务范围要合理控制,避免一个事务包含太多插入操作
  3. 在高并发场景下,需要适当增加连接池大小

2. 高并发下的唯一编号生成方案

在批量导入场景中,经常需要为每条记录生成唯一编号。常见的方案各有优劣,需要根据业务场景选择。

2.1 传统方案的问题

很多系统会采用时间戳+随机数的方式:

String uniqueNo = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss")) + (int)(Math.random()*90+10);

这种方案在大批量导入时容易出现重复,特别是在分布式环境下几乎无法保证唯一性。

2.2 Redis原子计数器方案

@Autowired private StringRedisTemplate stringRedisTemplate; public String generateUniqueNo() { String dateStr = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMdd")); String redisKey = "order:seq:" + dateStr; Long seq = stringRedisTemplate.opsForValue().increment(redisKey); if(seq == 1) { // 设置24小时过期 stringRedisTemplate.expire(redisKey, 24, TimeUnit.HOURS); } return dateStr + String.format("%04d", seq); }

优势:

  • 绝对唯一且有序
  • 性能极高(Redis每秒可处理10万+次自增操作)
  • 天然支持分布式环境

注意事项:

  1. Redis需要做高可用部署,避免单点故障
  2. 序列号长度要预留足够(如%04d表示最多9999个序号)
  3. 业务日期变更时需要自动切换Key

2.3 雪花算法(Snowflake)方案

MyBatis-Plus内置了雪花算法实现:

String id = IdWorker.getIdStr(); // 生成19位数字ID

算法结构:

0 - 0000000000 0000000000 0000000000 0000000000 0 - 00000 - 00000 - 000000000000 | | | | | | 符号位(1) 时间戳(41) 数据中心ID(5) 机器ID(5) 序列号(12)

时钟回拨问题解决方案:

  1. 使用MyBatis-Plus的IdentifierGenerator接口自定义实现
  2. 在检测到时钟回拨时,可以:
    • 短暂等待时钟同步
    • 使用备用ID生成方案(如Redis)
    • 抛出明确异常告警
public class CustomIdGenerator implements IdentifierGenerator { @Override public Long nextId(Object entity) { // 自定义时钟回拨处理逻辑 try { return Snowflake.getInstance().nextId(); } catch (ClockBackwardsException e) { // 降级方案 return redisTemplate.opsForValue().increment("backup:id"); } } }

3. 数据库安全防护措施

3.1 防止全表更新/删除

MyBatis-Plus提供了BlockAttackInnerInterceptor拦截器:

@Configuration public class MybatisConfig { @Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); interceptor.addInnerInterceptor(new BlockAttackInnerInterceptor()); return interceptor; } }

这个拦截器会检查所有UPDATE和DELETE语句:

  1. 如果没有WHERE条件,直接抛出异常
  2. 如果WHERE条件中只有永真条件(如1=1),也会被拦截

3.2 SQL注入防护

除了使用MyBatis的预编译机制外,Druid还提供了额外的防护:

spring: datasource: druid: filter: wall: enabled: true config: delete-allow: false # 禁止delete语句 drop-table-allow: false # 禁止删表 none-base-statement-allow: false # 禁止非基础SQL

4. 慢SQL监控方案对比

4.1 MySQL原生慢查询日志

-- 开启慢查询日志 SET GLOBAL slow_query_log = 'ON'; -- 设置慢查询阈值(秒) SET GLOBAL long_query_time = 1; -- 记录未使用索引的查询 SET GLOBAL log_queries_not_using_indexes = 'ON';

分析工具:

mysqldumpslow -s t /var/lib/mysql/mysql-slow.log

优缺点:

  • 优点:对性能影响小,记录全面
  • 缺点:需要访问服务器,实时性差

4.2 p6spy方案

配置步骤:

  1. 添加依赖:
<dependency> <groupId>p6spy</groupId> <artifactId>p6spy</artifactId> <version>3.9.1</version> </dependency>
  1. 修改数据源配置:
spring.datasource.driver-class-name=com.p6spy.engine.spy.P6SpyDriver spring.datasource.url=jdbc:p6spy:mysql://localhost:3306/db
  1. 配置spy.properties:
appender=com.p6spy.engine.spy.appender.Slf4JLogger executionThreshold=1000 # 慢查询阈值(ms)

适用场景:

  • 开发环境调试
  • 测试环境性能分析

4.3 Druid监控方案

完整配置示例:

spring: datasource: type: com.alibaba.druid.pool.DruidDataSource druid: filters: stat,wall filter: stat: enabled: true slow-sql-millis: 1000 log-slow-sql: true stat-view-servlet: enabled: true login-username: admin login-password: admin

监控大屏功能:

  1. SQL监控:统计SQL执行次数、耗时等
  2. SQL防火墙:拦截恶意SQL
  3. Web应用监控:统计URI请求量
  4. 连接池监控:活跃连接数等

生产环境建议:

  1. 设置合理的慢SQL阈值(通常1-2秒)
  2. 定期分析慢SQL日志
  3. 对高频慢SQL建立索引优化机制

5. 实战经验总结

在最近的一个供应链管理系统中,我应用了这套优化方案:

  1. 批量插入优化:将5万条物料数据的导入时间从原来的3分钟缩短到15秒

  2. 编号生成方案:采用Redis+雪花算法的混合模式:

    • 日常小批量操作使用雪花算法
    • 批量导入时切换为Redis序列
    • 实现了零重复和百万级/天的编号生成能力
  3. 慢SQL监控:通过Druid发现了3个关键索引缺失问题,优化后系统响应时间提升40%

特别提醒:在实施这些优化时,一定要根据实际业务场景进行调整。比如:

  • 对实时性要求高的系统,可以适当降低慢SQL阈值
  • 在资源受限的环境中,需要权衡监控粒度和性能开销
  • 分布式ID生成要考虑业务连续性需求

这套方案经过多个生产环境验证,能够显著提升数据库操作性能和可靠性。特别是在处理大批量数据时,合理组合这些技术可以避免很多潜在问题。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询