Caused by: java.sql.SQLIntegrityConstraintViolationException: Duplicate entry 'Le1DNspbSb6r-GFsJqh1sA' for key 'UKmiippl8ygi3wyaqxebh4r3p1w'
原因
@Table(name = "member_row_label",
uniqueConstraints = {@UniqueConstraint(columnNames = "memberId")}
)
在存在id主键的基础上,又建立了member_id
的唯一索引。
可能有两个会话都在创建(insert
)同一个member_id
的member_row_label
记录,这时会报这个错误。
场景
- 根据
member_id
查询member_row_label
记录 - 记录不存在(由于记录不存在,这样即使在事务管理下,加共享锁和排他锁都控制不了),创建
member_id
对应的member_row_label
记录
以上流程,当多个会话对一个member_id
进行操作的时候,那么会出现多次insert
操作,但只有一次能够成功。
方案
解决方案1 - 分布式锁
使用现在成熟的分布式锁来锁定
member_id
, 这样之后一个会话才会进行insert
操作。
解决方案2 - 去掉唯一索引
去掉唯一索引会导致同一个
member_id
对应多条记录,但是可以通过额外的合并操作进行处理,也可能会衍生出新的问题。
解决方案3 - 提前生成记录
在会员生成时,就生成对应的
member_row_label
记录,也就是insert
的操作从高频接口
迁移到紧关联的低频接口
,比如用户注册时生成,注册对于member_id
是只进行一次操作的。
日志
Caused by: java.sql.SQLIntegrityConstraintViolationException: Duplicate entry 'Le1DNspbSb6r-GFsJqh1sA' for key 'UKmiippl8ygi3wyaqxebh4r3p1w'
at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:117)
at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:97)
at com.mysql.cj.jdbc.exceptions.SQLExceptionsMapping.translateException(SQLExceptionsMapping.java:122)
at com.mysql.cj.jdbc.ClientPreparedStatement.executeInternal(ClientPreparedStatement.java:975)
at com.mysql.cj.jdbc.ClientPreparedStatement.executeUpdateInternal(ClientPreparedStatement.java:1114)
at com.mysql.cj.jdbc.ClientPreparedStatement.executeUpdateInternal(ClientPreparedStatement.java:1062)
at com.mysql.cj.jdbc.ClientPreparedStatement.executeLargeUpdate(ClientPreparedStatement.java:1383)
at com.mysql.cj.jdbc.ClientPreparedStatement.executeUpdate(ClientPreparedStatement.java:1047)
at com.zaxxer.hikari.pool.ProxyPreparedStatement.executeUpdate(ProxyPreparedStatement.java:61)
at com.zaxxer.hikari.pool.HikariProxyPreparedStatement.executeUpdate(HikariProxyPreparedStatement.java)
at org.hibernate.engine.jdbc.internal.ResultSetReturnImpl.executeUpdate(ResultSetReturnImpl.java:175)
... 105 more