ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

MySQL主键冲突问题分析处理

MySQL主键冲突问题分析处理 目录背景问题分析分析数据分析代码验证分析结果原因分析验证MySQL参数解决办法修改MySQL配置参数修改代码背景因公司业务及预算调整系统部署从原有云服务提供商迁移到另外一家云服务提供商在测试新服务能力的时候发现应用系统某个功能不能正常使用仅仅是第一次成功。为了分析问题笔者使用以下环境还原报错场景进行讲解。Spring Boot: 3.0.2MySQL: 5.7.31MyBatis: 3.5.1问题分析通过查看服务日志发现后端接口报SQL异常-主键冲突如下图所示刚开始看到这个错误信息直接就懵了怎么在另外一个服务商那里跑得好好的到这边就主键冲突了呢。一通百度、Google之后突然之间有个想法会不会是这两个云服务商提供的MySQL服务某些参数有区别。分析数据因为数据是迁移过来的表中有大量旧数据不好确定到底是那个值冲突了。分析代码然后查看我们代码找到对应的PO类代码代码类似以下主要关注主键属性id的数据类型。publicclassUserPo{privateintid;privateStringname;privateStringemail;privateStringpassword;}代码中发现id属性是int类型的我们知道Java中int类型默认值为0在新增数据的时候并没有为id属性设置值代码类似下面这样UserPousernewUserPo();user.setEmail(testqq.com);user.setName(test);userMapper.insertUser(user);insert方法的代码如下所示Insert(insert into tb_user(id,name,email,password) values (#{user.id}, #{user.name}, #{user.email}, #{user.password}))intinsertUser(Param(user)UserPouser);从代码上看应该是id值传了0第一次数据保存成功后以后就会发生主键冲突异常了。验证分析结果通过代码分析我们去查表中确实有一条id为0的数据类似下图所示在新服务器上开启应用的DEBUG日志也在日志中查看到SQL日志中id为0的日志。原因分析为了查找0值可以作为主键的原因又是一通百度、Google最后找到了sql_mode这个参数。这个参数中有个NO_AUTO_VALUE_ON_ZERO的值。通常我们可以通过在AUTO_INCREMENT列上插入0或null值来获取下一个序列的值但是NO_AUTO_VALUE_ON_ZERO参数阻止了0值的这种行为。也即0值将会作为一个有效的值存入id列中。可以通过SQL Modes页面查看详细的sql_mode参数的详情Server System Variables页面查看sql_mode简要说明。验证MySQL参数验证下我们分析步骤中查到的sql_mode参数的具体现象。我们创建一张测试表tb_user建表语句如下CREATETABLEtb_user(idintNOTNULLAUTO_INCREMENT,namevarchar(64)NOTNULLDEFAULT,emailvarchar(128)NOTNULLDEFAULT,passwordvarchar(128)NOTNULLDEFAULT,PRIMARYKEY(id))ENGINEInnoDBDEFAULTCHARSETutf8mb4;查看当前MySQL版本及sql_mode参数。我们看到当前的MySQL版本5.7.37且sql_mode中没有NO_AUTO_VALUE_ON_ZEROMySQL5.7中sql_mode默认没有NO_AUTO_VALUE_ON_ZERO参数。插入两条数据看下效果insertintotb_uservalues(0,Rock,rockotc.cc,123456);insertintotb_uservalues(0,Kitty,kittyotc.cc,123456);数据插入成功且id值从1开始自增。我们修改下sql_mode参数在原有参数上加NO_AUTO_VALUE_ON_ZERO。SETsql_modeONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION,NO_AUTO_VALUE_ON_ZERO;然后再插入两条数据我们就会看到主键冲突的报错。查看数据也是有一条id为0的记录。解决办法修改MySQL配置参数为了能快速在新的云平台上部署应用并且避免出现其他的未知问题我们首先修改了MySQL的sql_mode参数和就平台的一致。当然我们不能通过以上命令去设置我们的MySQL服务而是通过云服务提供商的管理平台去设置操作麻烦一点而以。修改代码为了后期维护方便将对应的实体类id属性类型改为Integer与其他的代码保持一致。
返回列表