问题是这样的:
- 我有多个竞争线程(100 多个)需要访问一个数据库表
- 每个线程都会传递一个
String name
- 如果该名称存在于表中,数据库应返回该行的 id,如果该名称不存在,则应插入该名称并返回 ID。 - 数据库中永远只能有一个
name
实例 - 即。名称必须是唯一的
如何确保线程一不会在线程二尝试插入 name1
的同时插入 name1
?换句话说,如何保证 name
在并发环境中的唯一性?这也需要尽可能高效 - 这有可能成为严重的瓶颈。
我正在使用 MySQL 和 Java。
谢谢
最佳答案
假设名称列上有一个唯一约束,每个insert
都会获取一个锁。任何试图同时插入它的线程都将等待,直到第一次 insert
成功或失败(tx 提交或回滚)。
如果第一笔交易成功,第二笔交易将因违反唯一键而失败。然后你就知道它已经存在了。
如果每个事务有一个插入,那没问题。如果每个事务有超过 1 个插入,则可能会死锁。
Each thread will pass a String name - where that name exists in the table, the database should return the id for the row, where the name doesn't already exist, the name should be inserted and the id returned.
所以总而言之,算法是这样的:
1 read row with name
2.1 if found, return row id
2.2 if not found, attempt to insert
2.2.1 if insert succeeds, return new row id
2.2.2 if insert fails with unique constraint violation
2.2.2.1 read row with name
2.2.2.2 read should succeed this time, so return row id
因为唯一索引可能存在高竞争,insert
可能会阻塞一段时间。在这种情况下,交易可能会超时。进行一些压力测试,并调整配置,直到它能正确处理您的负载。
另外,您应该检查是否遇到了违反唯一约束异常或其他一些异常。
同样,这仅在每个事务有一个插入时有效,否则它可能死锁。
此外,您可以尝试使用“select * for update
”读取步骤 1 中的行。在这种情况下,它会一直等到并发插入提交或成功。由于索引争用,这可以稍微减少步骤 2.2.2 中的错误量。
关于java - 在这种并发情况下如何保证数据的一致性?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2432879/