如何在竞争条件下将主键值插入表中?
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 如果是那种比赛条件,那么是的,你很高兴。以上是关于如何在竞争条件下将主键值插入表中?的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 Hibernate 在 MySQL 中获取自增主键值