如何在竞争条件下将主键值插入表中?

Posted

技术标签:

【中文标题】如何在竞争条件下将主键值插入表中?【英文标题】:How to insert a primary key value into a table under race conditions? 【发布时间】:2013-10-18 14:10:29 【问题描述】:

我们有一个代码可以将一个新值插入到作为主键的表列中。 首先,我们进行选择,如果该值不存在,则进行插入。

query = "SELECT AValue from ATableCache WHERE AValue=" + avalue;
ResultSet rs = stmt.executeQuery(query);
if (!rs.next()) 

    query = "INSERT INTO ATableCache (AValue) VALUES ('" + avalue + "')";
    stmt.executeUpdate(query);

在竞争条件下,当插入一个刚刚被另一个线程插入的值时,此算法可能会导致 SQL 错误。选项之一是同步,但这会减慢执行速度。请注意,插入很少发生。有没有更高效的算法?

【问题讨论】:

你的方法同步了吗? 如果插入很少发生,为什么还要担心同步会减慢执行速度? 那么为什么不尝试无条件地插入呢?检查错误代码是否 == 密钥违规以确定它是否有效/失败。 【参考方案1】:

Postgresql 使用序列来生成密钥,或者您可以使用串行数据类型。

对于序列,它很简单,这里是文档:http://www.postgresql.org/docs/8.1/static/sql-createsequence.html。缺点是您需要在插入时使用 nextval(),但您可以获得诸如能够选择要缓存多少值的功能。

您的另一个选择是将主键设置为其中一种序列类型。文档和关于连续剧来龙去脉的良好链接。

http://www.postgresql.org/docs/8.1/static/datatype.html#DATATYPE-SERIAL http://www.neilconway.org/docs/sequences/

Serial 将创建主键,您无需在插入中指定它。

【讨论】:

【参考方案2】:

我会让数据库生成密钥并确保事务是可序列化的。您需要正确设置数据库连接的隔离级别。

也许可以使用 PostgreSQL,但是根据这个答案,保证适用于所有 JDBC 驱动程序的通用解决方案已经避开了 Spring:

Is there anyway to get the generated keys when using Spring JDBC batchUpdate?

【讨论】:

【参考方案3】:
query = "INSERT INTO ATableCache (AValue) select '" + avalue +
"' where not exists (select 1 from ATableCache where AValue = '"+avalue +"')";

【讨论】:

@anarinsky 只是 select 用于检查 not exists 的假人。 我能看到实际的查询吗,例如avalue='RGG'? INSERT INTO ATableCache (AValue) SELECT 'RGG' where not exists (select 1 from ATableCache where AValue = 'RGG' )【参考方案4】:

我在您的帖子中看到了 postgresql 标签。在这种情况下,我建议您将主键设置为自动生成并注意将其放入您的插入语句中。

如果您尝试这种方法,我认为post 可以提供帮助。

【讨论】:

【参考方案5】:

没有那么多选择:

    您的avalue 生成代码将需要以原子方式创建唯一键。这将需要同步,以便线程不会共享该值。

    让数据库处理生成密钥。

    捕获异常并处理它(不推荐)。

【讨论】:

为什么选择 NO。 3不推荐? @anarinsky 这取决于你如何处理它,如果你尝试用另一个值重新插入,到那时,另一个线程可能也添加了那个值。 @anarinsky 如果是那种比赛条件,那么是的,你很高兴。

以上是关于如何在竞争条件下将主键值插入表中?的主要内容,如果未能解决你的问题,请参考以下文章

将主键 int 类型更改为串行

将主键插入第一个表后,如何使用触发器将主键插入另一个表?

如何在插入新行后立即将自动生成的主键值保存到第二列

如何使用 Hibernate 在 MySQL 中获取自增主键值

EF6 vs Entity Framework Core:插入实体而不将主键(身份)重置为零

主键生成策略