2017-11-26 2 views
2

이메일 대기열 인 간단한 표가 있습니다.Innodb 트랜잭션 또는 테이블이 잠겨 있습니까?

CREATE TABLE `emails_queue_batch` (
    `eq_log_id` int(11) NOT NULL DEFAULT '0', 
    `eq_to` varchar(120) CHARACTER SET utf8 DEFAULT NULL, 
    `eq_bcc` varchar(80) CHARACTER SET utf8 DEFAULT '', 
    `eq_from` varchar(80) CHARACTER SET utf8 DEFAULT NULL, 
    `eq_title` varchar(100) COLLATE utf8_unicode_ci DEFAULT NULL, 
    `eq_headers` varchar(80) CHARACTER SET utf8 DEFAULT NULL, 
    `eq_content` longtext CHARACTER SET utf8, 
    `eq_sid` int(11) DEFAULT '0', 
    `eq_type` int(11) DEFAULT '0' COMMENT 'email type', 
    `eq_esp` int(11) DEFAULT '0', 
    PRIMARY KEY (`eq_log_id`), 
    KEY `email` (`eq_to`) 
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci 

여러 스레드가 한 번에 50 개의 행을 반복적으로 읽고 행을 삭제합니다.

$db->query(" LOCK TABLE $table WRITE "); 

    $query= "SELECT * FROM $table LIMIT ".CHUNK_SIZE. " " ; 

    $emails2send=$db->get_results ($query); 

    if (!empty ($emails2send)){ 

     // DELETE EMAIL 
     $eq_log_ids = array(); 
     foreach ($emails2send as $email) $eq_log_ids[]= $email->eq_log_id ; 

     $query= "DELETE FROM $table WHERE eq_log_id IN (".implode(',', $eq_log_ids).") "; 
     $db->query ($query); 

     $db->query (" UNLOCK TABLES "); // unlock the table so other sessions can read next rows 
     ........ code processing the read rows here ............. 
    } else { // if !empty emails2send 
     // $emails2send is empty 
     $db->query (" UNLOCK TABLES; ");  
     $stop_running=true; // stop running 
    } 

또 다른 스레드 (들)을 동시에 테이블에 쓰기 :

내가 사용한 것과 같은 행을 두 번 읽기를 방지합니다. 나는이 구성이 읽기 및 쓰기 모두 잠긴 테이블로 인해 교착 상태에 있다는 것을 이해하지 못합니다.

내 질문 : 이 솔루션은 올바른 솔루션을 잠그기 때문에 각 행을 한 번만 읽고 한 번만 읽습니다.

또는 트랜잭션으로 처리하는 것이 더 나은가요? 그렇다면 어떤 종류입니까? 나는 거래 경험이 없다.

+1

올바른 해결책은 실제 메시지 대기열 (예 : RabbitMQ 또는 ActiveMQ)을 사용하는 것입니다. 사실상 큐로 트랜잭션 데이터베이스를 사용하면 데이터베이스 큐에서 하나의 스레드 읽기로만 제한하지 않는 한 항상 잠금 경합 및 교착 상태가 발생합니다. –

+0

고마워, 잘하고있어. 메시지 대기열에 대한 한 가지 문제점은 문제가 발생하고 예를 들어 이메일 일괄 처리를 취소해야하는 경우 대기열에서 선택적으로 삭제할 수 없다는 것입니다. – Nir

+0

지난 일자리 중 하나에서 우리는 데이터베이스에 작업을 보관했지만 "디스패처"프로세스는 실행할 준비가되었을 때 작업을 끌어와 MQ에 게시했습니다. 발송자가 단일 스레드 인 한 사용자가 묻는 문제는 피할 수 있습니다. 그런 다음 MQ는 많은 작업자 스레드에서 읽을 수 있습니다. 이는 균형을 유지합니다. MQ에 게시 될 때까지 DB의 작업을 삭제하거나 수정할 수 있습니다. –

답변

1

플랜 A는 :

이렇게하면 2 초, 말, 미만 N 행을 처리 할 수있는 가정합니다. 당신은 N = 50입니다 - 이것은 너무 클 수 있습니다.

BEGIN; 
SELECT ... LIMIT 50 FOR UPDATE; 
... process ... 
... gather a list of ids to delete ... 
DELETE ... WHERE id IN (...) 
COMMIT; 

더 많이 잡을수록 빠르지 만 교착 상태가 발생할 확률도 높아집니다. 교착 상태가 발생하면 트랜잭션을 끝내기 만하면됩니다. 또한 교착 상태 발생 빈도를 추적하여 "50"을 조정하십시오.

플랜 B : 항목의 처리가 트랜잭션에 대해 "너무 오래"를 취 때 유용

. 나는 2 초가 아마도 "너무 길다"라고 말한다.

Grab a row to process: 
with autocommit=ON ... 
UPDATE ... SET who_is_processing = $me, 
       when_grabbed = NOW() 
       id = LAST_INSERT_ID(id), 
      WHERE when_grabbed IS NULL 
      AND any-other-criteria 
      LIMIT 1; 
$id = SELECT LAST_INSERT_ID(); 

... process $id ... (This may or may not involve transactions) 

Release the row (or, in your case, delete it): 
again, autocommit=ON suffices... 
DELETE ... WHERE id = $id; 

"Never"는 InnoDB에서 테이블 잠금을 사용합니다. (유스 케이스가있을 수 있지만 이것은 하나가 아닙니다.)

1

특히 교착 상태에있는 경우 트랜잭션을 사용하는 것이 좋습니다.

먼저 배치 크기를 50에서 1로 줄이고 문제가 개선되는지 확인하십시오. 그들은 그렇게 할 수 있습니다. 그리고 그것은 쉽습니다. 그리고 그것은 당신이 거래를 사용한다면 당신이 원할 것입니다.

둘째, 이러한 종류의 쿼리를 시도하십시오.

START TRANSACTION; 
    SELECT @id := table.eq_log_id, table.* FROM table LIMIT 1 FOR UPDATE; 
    /* handle the item here */ 
    DELETE FROM table WHERE eq_log_id = @id; 
    COMMIT; 

eq_log_id 경우에만 작동합니다 고유 (또는 차)의 핵심입니다. SELECT 작업이 행을 반환하지 않을 때까지 PHP 프로그램의 루프에서이 작업을 실행하십시오. 그런 다음 잠시 후 다시 시도하십시오.

null 기본값을 사용하여 테이블에 processed이라는 TIMESTAMP를 추가하는 것이 더 나을 것입니다. 그런 다음 행을 삭제하는 대신 타임 스탬프를 업데이트 할 수 있습니다. 이렇게하면 문제를 해결할 수 있습니다.

START TRANSACTION; 
SELECT @id:=eq_log_id, * FROM table WHERE processed IS NULL LIMIT 1 FOR UPDATE; 
/* handle the item here */ 
UPDATE table SET processed=NOW() WHERE eq_log_id = @id; 
COMMIT; 

당신은 내가이 때문에 생산이 보낸 메시지의 시간 이력을보고 정말 도움이 될 수있는 제안이

DELETE FROM table WHERE processed < CURDATE() - INTERVAL 1 DAY; 

처럼 모든 오래된 기록을 닦아 하룻밤 배치를 실행할 수 있습니다.

관련 문제