2016-11-15 7 views
0

드문 교착 상태 오류가 발생했습니다. 두 쿼리 작업이 서로의 결과에 의존 할 때 데드락이 발생하여 MySQL이 그 중 하나를 롤백 할 때 교착 상태가 발생한다는 것을 이해합니다. 하지만 내 상황에서 MySQL은 자동 커밋 모드이고 트리거를 발생시키는 새로운 레코드를 삽입하고 있습니다. 그래서 나는 죽은 자물쇠 상황을 일으키는 이유를 얻지 못합니다. 여기 MySQL 트리거를 발생시키는 인서트로 교착 상태가 발생했습니다.

내 테이블 스키마입니다 :

---- 사용자 표 ----

CREATE TABLE `users` (
`insta_id` bigint(20) unsigned NOT NULL, 
`name` varchar(50) NOT NULL, 
`password` varchar(60) NOT NULL, 
`gem` int(10) unsigned DEFAULT '20', 
`coin` int(10) unsigned DEFAULT '20', 
PRIMARY KEY (`insta_id`), 
UNIQUE KEY `insta_id` (`insta_id`) 
) ENGINE=InnoDB DEFAULT CHARSET=latin1 

--- 표를 like_requests ---

CREATE TABLE `like_requests` (
`req_id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, 
`insta_id` bigint(20) unsigned NOT NULL, 
`media_id` varchar(50) NOT NULL, 
`remaining_like` int(10) unsigned NOT NULL, 
`active` tinyint(1) NOT NULL DEFAULT '1', 
`count` int(10) unsigned NOT NULL, 
PRIMARY KEY (`req_id`), 
KEY `insta_id` (`insta_id`), 
KEY `media_id` (`media_id`), 
CONSTRAINT `like_requests_ibfk_1` FOREIGN KEY (`insta_id`) REFERENCES `users`(`insta_id`) 
) ENGINE=InnoDB AUTO_INCREMENT=103902 DEFAULT CHARSET=latin1 

--- 좋아 표 ---

CREATE TABLE `likes` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, 
`insta_id` bigint(20) unsigned NOT NULL, 
`media_id` varchar(50) NOT NULL, 
`req_id` bigint(20) unsigned DEFAULT NULL, 
`date` timestamp NULL DEFAULT CURRENT_TIMESTAMP, 
PRIMARY KEY (`id`), 
UNIQUE KEY `id` (`id`), 
KEY `req_id` (`req_id`), 
KEY `insta_id` (`insta_id`), 
KEY `media_id` (`media_id`), 
CONSTRAINT `likes_ibfk_1` FOREIGN KEY (`req_id`) REFERENCES `like_requests`(`req_id`), 
CONSTRAINT `likes_ibfk_2` FOREIGN KEY (`insta_id`) REFERENCES `users`(`insta_id`) 
) ENGINE=InnoDB AUTO_INCREMENT=1704209 DEFAULT CHARSET=latin1 

나는 방아쇠를 가지고있다. 몇 가지 간단한 삽입을하고 함께

CREATE TRIGGER `after_insert_likes` AFTER INSERT ON `likes` 
    FOR EACH ROW BEGIN 
     UPDATE users SET users.coin=users.coin+1 
      WHERE users.insta_id = NEW.insta_id LIMIT 1; 
     IF NEW.req_id IS NOT NULL THEN 
      UPDATE like_requests 
       SET like_requests.remaining_like = like_requests.remaining_like-1 
       WHERE like_requests.req_id = NEW.req_id 
        AND like_requests.remaining_like > 0 
       LIMIT 1; 
     END IF; 
    END 

: N은 다음과 같이 정의, 테이블을 좋아

$sql = "INSERT INTO likes (insta_id,media_id,req_id) VALUES (?,?,?);"; 
    $pdo = $this->db; 

    $statement = $pdo->prepare($sql); 
    $statement->bindValue(1,$data['id'],PDO::PARAM_INT); 
    $statement->bindValue(2,$data['media_id']); 
    $statement->bindValue(3,$data['req_id'],PDO::PARAM_INT); 

    try 
    { 
     $statement->execute(); 
     return GetOkResponseWithMessage($response,"Like was submitted"); 
    } 
    catch (PDOException $exc) 
    { 
     return GetErrorResponseWithMessage($response,$exc->getMessage(),500); 
    } 

나는 다음과 같은 죽은 잠금 오류 로그를 얻을 :

*** (1) TRANSACTION: 
TRANSACTION 29031910, ACTIVE 1 sec starting index read 
mysql tables in use 4, locked 4 
LOCK WAIT 7 lock struct(s), heap size 1184, 3 row lock(s), undo log entries 1 
MySQL thread id 264238, OS thread handle 0x7f6522c6eb00, query id 753506  localhost xxxx updating 
UPDATE users SET users.coin=users.coin+1 WHERE users.insta_id=NEW.insta_id LIMIT 1 
*** (1) WAITING FOR THIS LOCK TO BE GRANTED: 
RECORD LOCKS space id 14 page no 1560 n bits 128 index `PRIMARY` of table `insta_star`.`users` trx table locks 4 total table locks 4 trx id 29031910 lock_mode X locks rec but not gap waiting lock hold time 0 wait time before grant 0 
*** (2) TRANSACTION: 
TRANSACTION 29031909, ACTIVE 1 sec starting index read 
mysql tables in use 4, locked 4 
7 lock struct(s), heap size 1184, 3 row lock(s), undo log entries 1 
MySQL thread id 264237, OS thread handle 0x7f65209f8b00, query id 753507 localhost xxxx updating 
UPDATE users SET users.coin=users.coin+1 WHERE users.insta_id=NEW.insta_id LIMIT 1 
*** (2) HOLDS THE LOCK(S): 
RECORD LOCKS space id 14 page no 1560 n bits 128 index `PRIMARY` of table `insta_star`.`users` trx table locks 4 total table locks 4 trx id 29031909 lock mode S locks rec but not gap lock hold time 0 wait time before grant 0 
*** (2) WAITING FOR THIS LOCK TO BE GRANTED: 
RECORD LOCKS space id 14 page no 1560 n bits 128 index `PRIMARY` of table `insta_star`.`users` trx table locks 4 total table locks 4 trx id 29031909 lock_mode X locks rec but not gap waiting lock hold time 0 wait time before grant 0 
*** WE ROLL BACK TRANSACTION (2) 

해야하는까지하지이를 죽은 자물쇠 대신에 자물쇠 대기에서?

트랜잭션을 다시 시작하지 않고 어떻게이 문제를 극복 할 수 있습니까?

+0

트리거 된 삽입을 추가 할 수 있습니까? 트랜잭션 주위를 감싸는 경우에는 full bock을 게시하십시오 – e4c5

+0

@ e4c5 삽입 코드는 단일 쿼리로 실행됩니다. 삽입을 수행하는 PHP 코드를 추가했습니다. – BlackBrain

답변

1

likes에서 (insta_id, media_id)은 고유합니까? 아니면 (insta_id, req_id) ?? 아니면 3 모두 ??? 그렇다면 PRIMARY KEY으로 만들고 id all together. If you must keep ID , get rid of UNIQUE (ID) , since PRIMARY KEY (id)`기능을 제공하십시오.

마찬가지로, UNIQUE(insta_id)을 제거하십시오.

생각해 여러 가지 명령으로 구성된 트랜잭션으로 결합 autocommitTRIGGER의 :함으로써 교착 상태의 가능성을 감소

BEGIN; 
INSERT INTO likes... -- Includes 2 uniqueness checks, 1 FK check 
UPDATE users ... 
if... UPDATE like_requests ... 
COMMIT; 

내 인덱스 변화가 어떤 물건의 속도가 빨라질 수 있습니다 제안했다. 변경 사항 일 수 있습니다. 교착 상태가 대기로 바뀌어도 나는 의심 스럽습니다.

교착 상태에 대한 최상의 방어는 사용자와 함께 진행되며이를 잡아서 트랜잭션 (이 경우에는 INSERT)을 재생합니다.

(관련 없음 :) media_id은 중복 저장되는 것처럼 보입니다.

2

스레드 2는 사용자 테이블의 행에 공유 잠금을 보유합니다.

그런 다음 스레드 1은 동일한 행에서 배타적 잠금을 얻으려고 시도하고 잠금 대기 상태가됩니다.

하지만 스레드 2는 자신의 잠금을 독점적으로 확장하려고 시도하기 때문에 시간 초과 할 수 없지만 잠금 대기중인 스레드 1을 기다려야하지만 스레드 2를 기다리고 있습니다.

이들은 각각 다른 스레드를 차단합니다.

교착 상태입니다.

서버가 죽일 트랜잭션을 선택하므로 불필요하게 서로를 차단하지 않습니다.

교착 상태 감지는 한 스레드가 다른 스레드를 즉시 희생 시키도록 허용합니다. 그렇지 않으면 그들은 둘 중 하나가 너무 오래 기다려서 죽을 때까지 잠금 대기 상태에 빠지게됩니다.


당신은 자동 커밋 모드에 있지만, 그렇다고해서 당신이 거래를하지 않는다는 의미는 아닙니다. InnoDB를 사용하는 모든 쿼리는 여전히 트랜잭션에서 처리되지만 자동 커밋을 통해 트랜잭션은 쿼리가 실행되기 시작하면 암시 적으로 시작되고 성공하면 암시 적으로 커밋됩니다.

+0

트랜잭션을 다시 시작하지 않고이 문제를 해결하는 방법을 알고 있습니까? – BlackBrain