2017-04-25 26 views
1

mysql 5.7을 저장소 엔진과 함께 innodb로 사용. 나는 제품 정보를 저장하는 테이블을 가지고있다. 표는 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의 연결 자바 응용 프로그램을 통해 두 가지 작업이 : (제품 변경에 대한 정보를 포함)
1. 새로 들어오는 이벤트에 제품 ID 필요성에 대한 기존 이벤트보다 더 큰 버전이 있으면 삽입됩니다. 버전은 내 데이터 열에 json blob로 저장됩니다.
2. 상태를 변경하려면 productId 행을 업데이트하십시오.

내 격리 수준은 읽기 최선을 다하고 있습니다. 나는 두 가지 방법을 시도했지만 모두 교착 상태에 선도 :

Approach1 :

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; 

이 상황에서 교착 상태에 선도 :

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 

이 동시 업데이트가 다른 트랜잭션이 열립니다 다음 경우 :
1. 트랜잭션 1 - 삽입 무시 문이 행에 S 잠금을 설정했습니다.
2. 트랜잭션 2 - 행에 대해 X 잠금을 사용하기 위해 업데이트 명령문 선택이 대기 중입니다.
트랜잭션 1 - 행에 대해 X 잠금을 사용하려고하는 업데이트 문을 선택합니다.

트랜잭션 1이 S 잠금을 보유하고 트랜잭션 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 - 행에 대한 X 잠금을 사용하기 위해 업데이트 명령문 선택이 대기 중입니다.
3. 거래 한 - 삽입 문을 무시하고이 행에 S 잠금을하려고하지만 거래 1의 X 잠금이 이미

교착 상태에 이르게 잠금을 기다리고, 나는 동시 처리하는 방법을 알고 싶은 업데이트 및 교착 상태가 발생하지 않고 새 이벤트 (행 업데이트 대신) 내 테이블에 삽입하십시오.
1. 잠금 순서는 무엇이되어야합니까?
2. 동시 업데이트 및 새 행 삽입이 교착 상태없이 작동하는지 확인하는 방법.

이 어떤 도움을 주시면 감사하겠습니다 :)

답변

1

내가 몇 가지 실험 후를 해결하기 위해 관리, 핵심 문제는 S의 순서로 한 후 X 잠금은 하나의 트랜잭션과 다른 취한 X 잠금에서 촬영. 기본적으로 S 잠금은 시작시에 교착 상태가 발생한 모든 사례로 이어졌습니다.
그래서 첫 번째 문으로 트랜잭션 외부에서 insert ignore 문을 이동했습니다. 트랜잭션은 이제 X 잠금 만 사용합니다. 즉, 트랜잭션 중 하나가 X 잠금을 사용하는 다른 트랜잭션을 기다립니다.

합니다 Event1 : 새 이벤트를 삽입

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 : 두 사건이 나를 피하기 도움 만 X 잠금 경쟁 트랜잭션을 가지고

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 

그래서 기존 이벤트 업데이트 교착 상태.

+0

또는 죽은 트랜잭션을 다시 적용하십시오. 트랜잭션에 교착 상태 또는 기타 오류가 발생할 수 있기 때문에이 작업을 수행 할 준비가 필요합니다. –

+0

귀하의 제안에 대해 @RickJames에게 감사드립니다. 예, 거래를 다시 시작하는 것이 좋습니다. 그러나 이것은 반복되는 패턴이었고 위의 해결 방법으로 문제가 해결되었습니다. 일반적으로 교착 상태에 대해 어떻게 생각합니까? 피하지 말아야합니까? –

+0

일부 교착 상태는 피할 수 있습니다. 그리고 나는 그들을 피하려고 노력하고 있습니다. 그러나 나는이 포럼에서 모든 교착 상태를 피할 수 있다고 생각하는 사람들이 많다는 것을 알았고 너무 많은 시간을 소비하려고 노력했다. 나는 당신 자신의 문제를 해결했기 때문에 기쁘다. –