从MySQL检索唯一标识符

时间:2015-03-03 15:57:42

标签: mysql jdbc concurrency unique counter

我有一个Web应用程序(MySQL上的JSP / Tomcat),它提供了填写在线合同的功能,并且需要在最后向客户返回一个唯一的计数器或标识符作为合同的协议。如果两个客户端同时确认该表单,则db上的SELECT MAX(COUNTER)不会防止标识符冲突。

我有一张表可以跟踪每种合同类型的客户可用的下一个标识符:

CONTRACT_TYPE   COUNTER
A                    21 
B                    13

获取下一个标识符的方法是:

public int getNewCounter(String contractType) {
    query1 = SELECT (COUNTER + 1) AS NEW_COUNTER from mytable WHERE CONTRACT_TYPE = 'A'
    query2 = UPDATE mytable SET COUNTER = <counter_returned_on_previous> WHERE CONTRACT_TYPE = 'A'
    return NEW_COUNTER;
}

这就存在并发客户端在同一时刻提交表单时可能会收到相同标识符的问题,这是我想避免的。

换句话说,有2个并发客户端可能会发生这种情况:

client1-query1
client1-query2
client2-query1
client2-query2

这是正确的行为,但我也可以这样:

client1-query1
client2-query1
client1-query2
client2-query2

导致标识符值冲突。允许多个客户拥有某种&#34;独家访问权限的最佳方式是什么?以上两个查询是为了保证函数返回唯一标识符?

非常感谢!

2 个答案:

答案 0 :(得分:0)

您不需要2个查询。

解决方案很简单:

UPDATE table1 SET counter = counter+1 WHERE contract_type = 'A';

您可以使用fiddle

答案 1 :(得分:0)

我回答我自己的问题。在并发环境中在MysSQL中生成唯一标识符的任务可以通过两种方式完成:

1)使用带事务的InnoDB表和SELECT FOR UPDATE锁定模式:

START TRANSACTION (or set autocommit to 0);     
SELECT COUNTER FROM mytable WHERE contract_type = 'A' FOR UPDATE;
UPDATE mytable SET COUNTER = COUNTER + 1 WHERE contract_type = 'A';     
COMMIT;

SELECT ... FOR UPDATE读取最新的可用数据,在它读取的每一行上设置独占锁。

2)只需一次访问该表:

 UPDATE myTable SET COUNTER = LAST_INSERT_ID(COUNTER + 1) WHERE contract_type = 'A';
 SELECT LAST_INSERT_ID();

这里的方式是SELECT语句只检索标识符信息(特定于当前连接)。它不访问任何表!

来源: http://dev.mysql.com/doc/refman/5.5/en/innodb-locking-reads.html