我们有表MySql 5.5:
CREATE TABLE IF NOT EXISTS `invoices` (
`id` varchar(36) NOT NULL,
`client_id` smallint(4) NOT NULL,
`invoice_number` int(11) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `client_id_2` (`client_id`,`invoice_number`),
KEY `client_id` (`client_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
我们将数据插入到该表中:
INSERT INTO `invoices` ( `id` , `client_id` , `invoice_number` )
VALUES (
UUID(),
10 ,
( SELECT (MAX(`invoice_number`) +1) as next_invoice_number FROM `invoices` WHERE `client_id` = 10 )
);
“10”是client_id
值。
它可以工作,但是它的并发性很差。我怎样才能找到具有良好并发性的工作解决方案?
复合 - 主键自动增量不是解决方案。我们需要每个client_id值自动增量。复合主键自动增量在表中提供自动增量,而不是每个client_id列值。
答案 0 :(得分:0)
此处不确定错误并发的含义。虽然每个DML操作都在隐式事务上运行,但您也可以使用begin transaction ... end
构造将其包装在显式事务块中。
答案 1 :(得分:0)
似乎mysql确实将整个表锁定在insert into ... select上。
在伪代码下为我们(针对类似问题)工作的策略
function insert_user(){
begin_transaction
next_invoice_number = select_max_invoice_number + 1
insert_user(next_invoice_number)
end_transaction
}
function perform_insert(){
try
insert_user
catch RecordNotUniqueError
perform_insert
end
}
这需要在某种高级编程语言中执行查询。 您基本上启动了一个tansaction,首先执行查询以读取用户的下一个发票号。然后使用next_invoice_number执行插入查询,并希望获得最佳效果。如果有并发进程尝试为同一用户插入相同的发票号,则其中一个进程的事务将失败。然后它可以尝试重复它。最后,对于相同的发票号应该没有并发操作,并且每个交易都会成功。
答案 2 :(得分:0)
我在这里看到了很多问题。
首先,对于每个发票注册,您正在扫描同一个表格,以查找该特定客户应使用的下一个发票编号。
更快的解决方案是拥有一个包含两列的表:Customer_ID(密钥)和上次发票ID。
每当您需要注册新发票时,只需从此新表中获取并更新新发票号,并在插页中使用它。
其次,是什么让您认为您在示例中显示的操作不应该锁定表?
由于这种情况有时只发生,最好的解决办法是尽量减少碰撞的可能性,这里提出的方法肯定会这样做。
答案 3 :(得分:0)
以这种方式重新构建查询。这可能更简单,更快。
INSERT INTO `invoices` ( `id` , `client_id` , `invoice_number` )
SELECT UUID(),
10 ,
MAX(`invoice_number`) +1
FROM `invoices`
WHERE `client_id` = 10;
这是自己的交易吗?使用autocommit=1
?
或者这是一组更大的命令的一部分?可能它们是导致错误的部分原因?
您将如何随后获取客户端的UUID和/或invoice_number?应用程序是否需要显示它们和/或将它们存储在其他表中?