2012-08-22 3 views
4

TL; DR - MySQL에서는 테이블을 잠그고 동시에 트랜잭션을 사용할 수 없습니다. 이 주위에 어떤 방법이 있습니까?MySQL : 테이블을 잠그고 트랜잭션을 시작하는 방법은 무엇입니까?

(느린) 외부 시스템의 일부 데이터를 캐시하기 위해 사용하는 MySQL 테이블이 있습니다. 데이터는 웹 페이지 (PHP로 작성)를 표시하는 데 사용됩니다. 캐시 된 데이터가 너무 오래된 것으로 간주 될 때마다 웹 연결 중 하나가 캐시 된 데이터의 업데이트를 트리거해야합니다. 나는 그것을

  • 여러 클라이언트를 업데이트하고있는 동안 캐시 데이터가 너무 오래 결정하고 시도 할 수 있습니다 캐시 데이터를 읽으려고합니다

    • 다른 클라이언트 :

      내가 처리해야 할 세 가지 문제가 있습니다 일을하고있는 PHP 인스턴스가 언제든지 예기치 않게 종료 될 수 있습니다

    • 동시에 그것을 업데이트하고 데이터는

    나는 처음과 마지막을 해결할 수있는 손상되어서는 안된다 트랜잭션을 사용하여 문제를 처리하므로 클라이언트는 트랜잭션이 커밋 될 때까지 새 데이터를 즉시 볼 수 있으므로 이전 데이터를 읽을 수 있습니다. 문제가 발생하면 단순히 트랜잭션이 롤백됩니다.

    하나의 프로세스 만 업데이트를 수행 할 수 있도록 테이블을 잠그면 두 번째 문제를 해결할 수 있습니다. 다른 프로세스가 잠금을받을 때쯤에 그들은 펀치에 맞고 아무것도 업데이트 할 필요가 없다는 것을 알게 될 것입니다.

    즉, 테이블과 테이블을 잠글 필요가 있습니다. MySQL 설명서에 따르면 this is not possible. 트랜잭션을 시작하면 잠금이 해제되고 테이블을 잠그면 활성 트랜잭션이 커밋됩니다.

    이 문제를 해결할 수있는 방법이 있습니까? 아니면 내 목표를 달성하기위한 또 다른 방법이 있습니까?

  • 답변

    4

    만약 그랬다면, 난 캐시를 업데이트하기위한 뮤텍스와 읽기 분리에 대한 트랜잭션을 구현하기 위해 MySQL을 내 advisory locking function을 사용하십시오. 예 :

    begin_transaction(); // although reading a single row doesnt really require this 
    $cached=runquery("SELECT * FROM cache WHERE key=$id"); 
    end_transaction(); 
    
    if (is_expired($cached)) { 
        $cached=refresh_data($cached, $id); 
    } 
    ... 
    
    function refresh_data($cached, $id) 
    { 
    $lockname=some_deterministic_transform($id); 
    if (1==runquery("SELECT GET_LOCK('$lockname',0)") { 
        $cached=fetch_source_data($id); 
        begin_transaction(); 
        write_data($cached, $id); 
        end_transaction(); 
        runquery("SELECT RELEASE_LOCK('$lockname')"); 
    } 
    return $cached; 
    } 
    

    (BTW : 당신이 지속적인 연결을이하려고하면 나쁜 일이 일어날 수 있음)

    +0

    감사합니다. 이것은 잘 작동하는 것 같습니다! – Malvineous

    0

    두 번째 문제는 데이터베이스와 관련없이 해결 될 수 있습니다. 다른 클라이언트가 누군가가 이미 캐시 된 것을 알 수 있도록 캐시 업데이트 절차에 대한 잠금 파일을 준비하십시오. 이것은 각 코너 케이스를 잡을 수는 없지만 두 클라이언트가 동시에 캐시를 업데이트하는 경우 큰 차이가 있습니까? 결국 그들은 캐시에 대한 트랜잭션에서 업데이트를 수행하면서 일관성을 유지합니다.

    당신도이 테이블에 저장된 마지막 캐시 업데이트 시간을함으로써 자신을 고정 구현할 수있다. 클라이언트가 캐시 업데이트를 원할 때 테이블을 잠그고 마지막 업데이트 시간을 확인한 다음 필드를 업데이트하십시오.

    는 캐시를 업데이트 여러 클라이언트를 방지하기 위해 자신의 잠금 메커니즘을 구현 즉. 트랜잭션은 나머지를 처리합니다.

    +0

    사이트 파일의 로컬 복사본을 실행하는 여러 웹 서버가 있기 때문에 안타깝게도 잠금 파일은 여기서 작동하지 않습니다. 따라서 한 웹 서버는 다른 웹 서버가 다른 사람이 만든 잠금 파일을 보지 못합니다. 마지막 업데이트 시간을 테이블에 저장하고 있지만 캐시 새로 고침이 완료 될 때까지 업데이트 할 수 없습니다. 그렇지 않으면 시간이 업데이트되고 프로세스가 종료 될 때 (포인트 3 당) 데이터가 삭제됩니다 새로 고침되었지만 마지막 업데이트 시간이 잘못되었다고 말합니다. 전체 업데이트에 대해 해당 테이블을 잠그면 포인트 1이 중단되어 다른 클라이언트가 새로 고치는 동안 이전 데이터를 읽지 못하도록 차단합니다. – Malvineous

    5

    이 두 테이블을 잠그고

    이것은 어떻게 트랜잭션을 시작에 내가 필요로하는 것을 의미 할 수 있습니다 :

    SET autocommit=0; 
    LOCK TABLES t1 WRITE, t2 READ, ...; 
    ... do something with tables t1 and t2 here ... 
    COMMIT; 
    UNLOCK TABLES; 
    

    자세한 내용은를 참조하십시오.3210

    +0

    이것은 실제로 작동하는 것처럼 보이지만, InnoDB 테이블에 대해서만 문제가있는 것 같습니다. – Malvineous

    관련 문제