【每日鲜蘑】Duplicate key异常🔥

2,379 阅读1分钟

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_idmember_row_label记录,这时会报这个错误。

场景

  1. 根据member_id查询member_row_label记录
  2. 记录不存在(由于记录不存在,这样即使在事务管理下,加共享锁和排他锁都控制不了),创建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