2014-07-18 40 views
3

我在中等到高负载情况下遇到死锁。这里是细节。Mysql在插入和更新时出现死锁

的MySQL-5.5.21-55

引擎:InnoDB的

表:订单

# Field, Type, Null, Key, Default, Extra 
id, bigint(20) unsigned, NO, PRI, , auto_increment 
sno, varchar(32), NO, MUL, , 
misc1, int, NO, , 0, 

表:OrderItem的

# Field, Type, Null, Key, Default, Extra 
id, bigint(20) unsigned, NO, PRI, , auto_increment 
order_id, bigint(20), YES, MUL, , 
f1, varchar(50), YES, , , 
f2, varchar(100), YES, , , 
misc2, int, NO, , 0, 
  • 订购 .sno是唯一
  • OrderItem的 .order_id没有被定义为外键,但用作应用外键
  • 订购具有一个一对多关系与OrderItem的
  • OrderItem的 .order_id + OrderItem的 .f1 + OrderItem的 .f2是唯一

USECASE:

  • 每当订单OrderItem的需要更新任何记录,我要废止旧记录(或删除),并插入新的。
  • 可能因此发生较早,在订购一个记录(例如order1)有3条记录OrderItem的(例如orderItem1,orderItem2,orderItem3)。但现在我想把它作为order1-> orderItem1,orderItem4,orderItem5或全​​新的集合。这就是为什么我想要将旧记录全部无效并插入新记录以查明订单项变得复杂的原因。

多线程会做这个操作;但他们会在不同的记录集上工作。我在一个时间

我试了一下在25 订单经营:

  • 插入订单;重复密钥更新订购并从订单项删除所有子项并插入订单项 s。
  • 呼吁IS_ACTIVE订单并标记为同一SNO的所有记录为0,并在秩序插入新记录的另一列;将新的子女插入订单项
  • 删除订单给定的sno;从删除OrderItem为同一个sno.Insert插入两个表中新鲜。

以上所有方法都会导致无法锁定,有些时候或其他时间。 没有其他线程或进程正在处理这些表。

观察:

通过以下链接

去了,发现更新/删除多个记录导致MySQL获得下一个关键锁定一个t REPEATABLE_READ隔离级别(这是默认级别)。在我看来这导致了这个问题。

感谢您能否提供解决方案。

+0

锁只锁定其他连接,不能在同一数据库连接上的操作之间发生死锁。 – Barmar

+0

“新订单”动作如何触发...?一个简单的http请求? –

+0

@Barmar连接不被线程共享。 –

回答

0

最后,(或许是一种解决方法,在网上与@ JackyCheng的建议),IM,

  • 插入到订单表(同时多条记录); 重复密钥没有更新
  • 插入到OrderItem表(一次多个记录);
  • 选择所有订购 .ids将被更新
  • 标记标识订单(IDS)为非活动一个接一个(这是关键部分,因为它使用了索引键锁,而不是下键锁)

总体交易执行时间仍低于30毫秒的25份订单。

1

既然你没有提到它,我会试试看,你有没有试过“FOR UPDATE”?

将连接自动提交设置为false。

在程序开始时,用这个来锁定所有相关的数据。

SELECT o.* , oi.* 
FROM order o 
INNER JOIN orderitem oi ON (o.id=oi.order_id) 
WHERE o.id = <order id to update> 
FOR UPDATE; 

然后你可以做任何你想要的那些条目。 然后提交。我认为问题的根本原因是多线程(显然),我想如果从等式中删除该元素更好。

想象一个系统,您有多个接收器接收请求。这些接收器只会做这样的事情:

//select to check if there is a existing record(no need to lock), if no, return fail as response 
SELECT o.* , oi.* 
FROM order o 
INNER JOIN orderitem oi ON (o.id=oi.order_id) 
WHERE o.sno = <sno to update>; 

insert into request_buffer (request_id, sno, new_order_item,create_date .......) 
values 
(1, abc , orderitem1......); 
//return success after inserting buffer. 

,你有池此表一个单独的单线程程序和处理这些缓冲区条目。

在这种情况下,它会从数据库更新过程中分离出多线程元素。我不确定收到的请求数量,但我认为如果您在每个周期/查询中处理更多的请求,那么性能不会有显着差异?

+0

我把这作为最后的手段;即一次执行一个记录的删除/更新操作。当我在where子句中使用范围,即IN子句时,它使用NEXT KEY锁,这会导致死锁。 –

+0

@PragalathanM稍微修改了第二种方法。接收器 –

+0

增加检查问题是负载。我们希望有多台服务器处理这些记录。记录每小时大约100万次。所以要么我们必须在同一台服务器或多台服务器上有多个线程。 –