2017-04-25 68 views
1

将存储引擎作为innodb使用mysql 5.7。我有一张储存产品信息的表格。该表看起来像这样具有独特的键上的productId导致死锁的行上的Mysql并发更新

| Field  | Type   | Null | Key | Default   | Extra      | 
+-----------+--------------+------+-----+-------------------+-----------------------------+ 
| id  | bigint(20) | NO | PRI | NULL    | auto_increment    | 
| productId | varchar(50) | NO | UNI | NULL    |        | 
| seller | varchar(100) | NO | MUL | NULL    |        | 
| updatedAt | timestamp | NO | MUL | CURRENT_TIMESTAMP | on update CURRENT_TIMESTAMP | 
| status | varchar(100) | NO | MUL | NULL    |        | 
| data  | longtext  | NO |  | NULL    |        | 
+-----------+--------------+------+-----+-------------------+-----------------------------+ 

我通过连接到该MySQL的Java应用程序有两种操作:
1.新进入的事件(包含关于产品的变更信息)产品ID需要如果它们的版本大于现有事件,则插入它们。该版本作为json blob存储在我的数据列中
2.更新productId的行以更改状态。

我的隔离级别是读取提交的。 我尝试了两种方式,但两者都导致死锁:

Approach1:

Transaction1 starts 
Insert ignore into products where productId='X' values(); // Takes a S lock on the row 
select * from products where productId='X' for update ; // Take a X lock on the row to prevent new writes and compare the incoming event with the current event 
Insert into products values on duplicate key update values // insert into row and on duplicate key update values 
commit 

一个并发更新将打开另一个交易:

Transaction2 starts 
select * from products where productId='X' for update ; // Take a X lock on the row to prevent new writes and compare the incoming event with the current event 
Insert into products values on duplicate key update values // insert into row and on duplicate key update values 
commit; 

这是导致死锁的情况当出现以下情况时:
1.事务1 - 插入忽略语句已对该行进行了S锁定。
2.事务2 - 选择更新语句正在等待对行进行X锁定。
3.事务1 - 选择更新语句尝试对行进行X锁定。

因为S锁被保持由事务1和事务2等待采取X锁,并且当事务1试图采取X锁,它会导致死锁这导致死锁。

方法2:

Transaction 1 starts: 
select * from products where productId='X' for update ; // If a row exists then it will be locked else I know it does not exist 
Insert ignore into products where productId='X' values(); 
commit 

Transaction 2 starts: 
select * from products where productId='X' for update ; // If a row exists then it will be locked else I know it does not exist 
commit 

这导致了局势的僵局时,以下几点:
1.事务1 - 选择更新语句采用该行的X锁。
2.事务2 - 选择更新语句正在等待对该行进行X锁定。
3.事务1 - 插入忽略语句试图采取对行的S锁,但成交1的X锁已经等待这导致死锁

因此,一个锁,我想知道如何处理并发更新并插入新的事件(而不是行更新)到我的表中,而不会导致死锁。
1.什么是锁定顺序?
2.如何确保并发更新和新行插入的工作没有死锁。

任何帮助,将不胜感激:)

回答

1

我管理的一些实验后解决它,核心问题是与S的序列,然后在一个事务中,而在另一个中得到的X锁摄X锁。基本上,一开始就采取的S锁导致所有病例都陷入僵局。
所以,我把insert语句作为第一条语句移到事务之外。事务现在只需要X个锁,这意味着其中一个事务在另一个上等待X锁。

事件1:插入一个新的事件

result = Insert ignore into products where productId='X' values(); 
if result == null 
return 
end 
Transaction start 
select * from products where productId='X' for update ; // Take a X lock on the row to prevent new writes and compare the incoming event with the current event 
Insert into products values on duplicate key update values // insert into row and on duplicate key update values 
commit 

事件2:更新现有的事件

Transaction start 
    select * from products where productId='X' for update ; // If a row exists then it will be locked else I know it does not exist 
    commit 

所以,无论是事件有交易,只有争夺一个X锁,这让我避免死锁。

+0

或者,重新应用已被杀死的交易。您需要做好准备来做到这一点,因为某些事情可能导致死锁或其他对交易致命的错误。 –

+0

感谢@RickJames的建议。是的,重新启动交易是一种选择。但是,这是一种反复出现的模式,上述解决方案解决了这个问题。总的来说,你对僵局有何看法,难道不应该避免吗? –

+0

可以避免一些死锁。我同意_trying_来避免它们。然而,我发现这个论坛上有很多人似乎认为所有僵局都可以避免,并花费太多时间来尝试这样做。我很高兴你解决了你自己的问题。 –