java向数据库插入数据时的错误: Duplicate entry '' for key 'PRIMARY' 问题解决

本文探讨了在高并发环境下MySQL主键重复错误的解决方案,详细解释了如何通过调整innodb_autoinc_lock_mode参数来优化性能,以及在不同场景下的最佳实践。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

错误提示为:你插入的记录与数据表中原有记录的主键重复了(Duplicate)。所以插入失败

 

mysql主键设置成auto_increment时,进行并发性能测试出现主键反复Duplicate entry 'xxx' for key 'PRIMARY'
解决方法:
在my.cnf的[mysqld]片段中加入设置innodb_autoinc_lock_mode=0
同一时候注意调大jdbc的活跃链接数,如设置 jdbc.maxActive=300,由于设置innodb_autoinc_lock_mode=0可能导致链接过多。
注意,这样的方式仅仅须要在并发性能测试时设置,由于这样的方式在插入记录时需进行全表锁定,性能较差,平时生产环境中仅仅需使用默认的设置innodb_autoinc_lock_mode=1就可以,mysql的官方文档有说明:
1、innodb_autoinc_lock_mode = 0 (“traditional” lock mode)
这样的方式就和mysql5.1.22曾经一样,这样的方式的特点就是“表级锁定”,并发性较差
2、innodb_autoinc_lock_mode = 1 (“consecutive” lock mode)
这样的方式是新版本号中的默认方式,推荐使用,并发性相对较高,特点是“consecutive”,即保证同一条insert语句中新插入的auto_increment id都是连续的。
这样的模式下:
“Simple inserts”:直接通过分析语句,获得要插入的数量,然后一次性分配足够的auto_increment id,仅仅会将整个分配的过程锁住。
“Bulk inserts”:由于不能确定插入的数量,因此使用和曾经的模式同样的表级锁定。
“Mixed-mode inserts”:直接分析语句,获得最坏情况下须要插入的数量,然后一次性分配足够的auto_increment id,仅仅会将整个分配的过程锁住。须要注意的是,这样的方式下,会分配过多的id,而导致”浪费“。比方INSERT INTO t1 (c1,c2) VALUES (1,’a'), (NULL,’b'), (5,’c'), (NULL,’d');会一次性的分配5个id,而无论用户是否指定了部分id;INSERT … ON DUPLICATE KEY UPDATE一次性分配,而无论将来插入过程中是否会由于duplicate key而只运行update操作。
注意:当master mysql版本号<5.1.22,slave mysql版本号>=5.1.22时,slave须要将innodb_autoinc_lock_mode设置为0,由于默认的 innodb_autoinc_lock_mode为1,对于INSERT … ON DUPLICATE KEY UPDATE和INSERT INTO t1 (c1,c2) VALUES (1,’a'), (NULL,’b'), (5,’c'), (NULL,’d');的运行结果不同,现实环境通常会使用INSERT … ON DUPLICATE KEY UPDATE。
3、innodb_autoinc_lock_mode = 2 (“interleaved” lock mode)
这样的模式是来一个分配一个,而不会锁表,仅仅会锁住分配id的过程,和innodb_autoinc_lock_mode = 1的差别在于,不会预分配多个,这样的方式并发性最高。可是在replication中当binlog_format为statement-based时 (简称SBR statement-based replication)存在问题,由于是来一个分配一个,这样当并发运行时,“Bulk inserts”在分配的时会同一时候向其它的INSERT分配,会出现主从不一致(从库运行结果和主库运行结果不一样),由于binlog仅仅会记录开始的 insert id。

转载于:https://www.cnblogs.com/zgq123456/p/9996525.html

### Java解决主键重复导致的`BatchUpdateException` 当执行批量更新操作,如果遇到 `java.sql.BatchUpdateException: Duplicate entry` 错误,则表明尝试插入数据违反了数据库中的唯一约束条件。以下是针对该问题的具体解决方案: #### 数据库层面的原因分析 此错误通常由以下几个原因之一引起: 1. 插入数据与现有记录冲突,违反了表上的唯一索引或主键约束[^3]。 2. 批量插入过程中未对数据进行去重处理,导致多次插入相同的关键字段组合[^4]。 #### 解决方案 ##### 方法一:提前校验数据唯一性 在向数据库提交批量写入请求前,在应用层实现数据验证逻辑。通过查询目标表确认待插入记录是否存在冲突项后再决定是否继续插入。例如可以采用如下SQL语句来判断某条记录是否已存在于指定列下: ```sql SELECT COUNT(*) FROM your_table WHERE unique_column = ? ``` 对于每一条即将被加入的新纪录都运行上述检查命令;只有在其返回值等于零表示安全可插件否则跳过当前这条或者抛出自定义异常通知调用方存在潜在覆盖风险。 ##### 方法二:捕获并处理异常 允许失败但仍需完成其余部分的成功事务可以通过设置参数ignoreDuplicateKeyErrors=true让JDBC驱动忽略此类特定类型的错误而不中断整个批次流程。不过这种方法依赖于具体的RDBMS支持情况以及相应的连接属性配置方式可能有所不同因此需要查阅对应产品的官方文档获取确切做法[^2]。 另外一种通用策略是在程序内部手动捕捉到这个特殊的SQLException子类实例之后采取适当措施比如记录日志然后绕过有问题的那个元素接着往下走直到全部结束为止就像这样: ```java try { statement.executeBatch(); } catch (BatchUpdateException e) { int[] updateCounts = e.getUpdateCounts(); for(int i=0;i<updateCounts.length;i++) { if(updateCounts[i]==Statement.EXECUTE_FAILED){ System.out.println("Record "+(i+1)+" failed due to duplicate key."); } } } ``` ##### 方法三:调整业务设计模式 考虑改变现有的架构模型使得不再单纯依靠单一维度作为区分依据而是引入间戳或者其他随机因子共同构成复合型标识符从而降低发生碰撞的概率同也便于追踪历史版本变化轨迹[^1]。 --- ### 示例代码展示如何预防及应对这种情况的发生 下面给出了一段完整的示范代码片段展示了前面提到的一些技巧的实际运用场景: ```java import java.sql.*; public class BatchInsertExample { public static void main(String args[]) throws Exception{ String url="jdbc:mysql://localhost/testdb"; Connection conn=null; PreparedStatement pstmt=null; try{ Class.forName ("com.mysql.cj.jdbc.Driver").newInstance (); conn = DriverManager.getConnection(url,"root","password"); // Disable auto-commit mode. conn.setAutoCommit(false); String sql ="INSERT INTO devices(areaId, collectorCode,...) VALUES (?, ?, ...)"; pstmt =conn.prepareStatement(sql); List<Map<String,Object>> dataList=getDataList(); Set<String> existingKeys=new HashSet<>(); ResultSet rs=pstmt.executeQuery("select deviceCode from devices"); while(rs.next()){ existingKeys.add(rs.getString(1)); } boolean hasError=false; StringBuilder errorMessages=new StringBuilder(); for(Map<String,Object> data:dataList){ Object deviceCode=data.get("deviceCode"); if(existingKeys.contains(deviceCode)){ continue; // Skip duplicates silently or log warning message here. } setParameters(pstmt,data); pstmt.addBatch(); } try{ pstmt.executeBatch(); conn.commit(); }catch(BatchUpdateException bue){ hasError=true; int [] counts=bue.getUpdateCounts(); for(int count :counts ){ if(count==Statement.EXECUTE_FAILED){ errorMessages.append("Failed record "); } } conn.rollback(); } finally{ if(hasError){ throw new RuntimeException(errorMessages.toString()); } } }finally{ closeResources(conn,pstmt,null); } } private static void setParameters(PreparedStatement stmt , Map<String,Object> params)throws SQLException{ ... } private static void closeResources(Connection c, Statement s,ResultSet r){ .... } } ``` ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值