Innodb每个自定义列自动增量,并发性差

时间:2016-12-01 12:19:37

标签: mysql innodb

我们有表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列值。

4 个答案:

答案 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?应用程序是否需要显示它们和/或将它们存储在其他表中?