MySQL : 트랜잭션 대 잠금 테이블


110

데이터베이스 무결성을 보장하고 SELECT 및 UPDATE가 동기화 상태를 유지하고 다른 연결이 간섭하지 않도록하기 위해 트랜잭션 대 잠금 테이블과 약간 혼동됩니다. 다음을 수행해야합니다.

SELECT * FROM table WHERE (...) LIMIT 1

if (condition passes) {
   // Update row I got from the select 
   UPDATE table SET column = "value" WHERE (...)

   ... other logic (including INSERT some data) ...
}

다른 쿼리가 방해하고 동일한 작업을 수행하지 않도록해야합니다 SELECT(연결이 행 업데이트를 완료하기 전에 '이전 값'읽기).

LOCK TABLES table한 번에 하나의 연결 만이 작업을 수행하도록 기본으로 설정 하고 완료되면 잠금을 해제 할 수 있다는 것을 알고 있습니다 . 트랜잭션에서 랩핑하면 동일한 작업을 수행 할 수 있습니까 (다른 연결이 여전히 처리중인 동안 다른 연결이 동일한 프로세스를 시도하지 않도록 보장)? 아니면 것 SELECT ... FOR UPDATE또는 SELECT ... LOCK IN SHARE MODE더 나은?

답변:


173

테이블을 잠그면 다른 DB 사용자가 잠근 행 / 테이블에 영향을주지 않습니다. 그러나 잠금 자체는 논리가 일관된 상태로 나오는 것을 보장하지 않습니다.

은행 시스템을 생각해보십시오. 온라인으로 청구서를 지불 할 때 거래의 영향을받는 두 개 이상의 계정이 있습니다. 즉, 돈을 인출하는 계정입니다. 그리고 돈이 이체되는 수신자의 계정. 그리고 거래에 부과 된 모든 서비스 수수료를 기꺼이 입금 할 은행 계좌. (요즘 모두가 알고 있듯이) 은행이 엄청나게 어리 석다는 것을 감안할 때, 그들의 시스템이 다음과 같이 작동한다고 가정 해 봅시다.

$balance = "GET BALANCE FROM your ACCOUNT";
if ($balance < $amount_being_paid) {
    charge_huge_overdraft_fees();
}
$balance = $balance - $amount_being paid;
UPDATE your ACCOUNT SET BALANCE = $balance;

$balance = "GET BALANCE FROM receiver ACCOUNT"
charge_insane_transaction_fee();
$balance = $balance + $amount_being_paid
UPDATE receiver ACCOUNT SET BALANCE = $balance

이제 잠금 및 거래가없는이 시스템은 다양한 경쟁 조건에 취약하며, 그중 가장 큰 것은 귀하의 계정 또는 수신자의 계정에서 동시에 수행되는 여러 결제입니다. 코드에서 잔액을 검색하고 huge_overdraft_fees () 및 기타 작업을 수행하는 동안 다른 지불이 동일한 유형의 코드를 병렬로 실행하는 것은 전적으로 가능합니다. 그들은 당신의 잔액 (예 : $ 100)을 회수하고, 그들의 거래를 할 것입니다. 70 달러. 마지막으로 완료되는 항목에 따라 계정에있는 두 잔고 중 하나가됩니다 ($ 100-$ 20-$ 30). 이 경우 "귀하의 은행 오류"

이제 잠금을 사용한다고 가정 해 보겠습니다. 청구서 지불 ($ 20)이 먼저 파이프에 도달하므로 계정 기록을 확보하고 잠급니다. 이제 독점 사용이 가능하며 잔액에서 20 달러를 공제하고 새 잔액을 안심하고 다시 쓸 수 있습니다. 그러면 계정이 예상대로 80 달러가됩니다. 하지만 ... 어 ... 당신은 수취인의 계좌를 업데이트하려고하는데, 그것은 잠겨 있고, 코드가 허용하는 것보다 더 오래 잠겼습니다. 당신의 거래 시간이 초과되었습니다 ... 우리는 어리석은 은행들을 다루고 있습니다. 처리, 코드는 단지을 당기고 exit()$ 20는 전자의 퍼프로 사라집니다. 이제 당신은 20 달러를 내고 여전히 수신자에게 20 달러를 빚지고 있으며 전화기는 회수됩니다.

그래서 ... 거래를 입력하세요. 당신은 거래를 시작하고, 당신의 계좌에서 $ 20를 인출하고, 당신은 수신자에게 $ 20을 입금하려고합니다. 그리고 뭔가가 다시 터집니다. 그러나 이번에 exit()는 코드 대신을 할 수 rollback있으며 휙 휙, $ 20가 마법처럼 계정에 다시 추가됩니다.

결국 다음과 같이 요약됩니다.

잠금은 다른 사람이 처리중인 데이터베이스 레코드를 방해하지 못하도록합니다. 트랜잭션은 "이후"오류가 "이전"작업을 방해하지 않도록합니다. 둘 중 어느 쪽도 결국 모든 것이 정상적으로 작동한다고 보장 할 수 없습니다. 하지만 함께라면 그렇습니다.

내일 수업 : 교착 상태의 기쁨.


4
나는 또한 / 여전히 혼란 스럽다. 수취인 계정에 $ 100가 들어 있고 계정에서 $ 20 결제 금액을 추가한다고 가정 해 보겠습니다. 트랜잭션에 대한 나의 이해는 트랜잭션이 시작될 때 트랜잭션 내 작업이 트랜잭션 시작 시점의 상태로 데이터베이스를 본다는 것입니다. 즉, 변경할 때까지 수신자 계정은 $ 100입니다. 그래서 ... 20 달러를 더하면 실제로는 120 달러의 잔액이 설정됩니다. 하지만 거래 중에 누군가가 수신자 계좌를 $ 0로 빼면 어떻게 될까요? 이것은 어떻게 든 예방됩니까? 그들은 마술처럼 다시 $ 120를 얻습니까? 이것이 자물쇠도 필요한 이유입니까?
Russ

예, 자물쇠가 작동하는 곳입니다. 적절한 시스템은 트랜잭션이 진행되는 동안 다른 사람이 레코드를 업데이트 할 수 없도록 레코드를 쓰기 잠금합니다. 편집증 시스템은 아무도 "부실한"잔액을 읽을 수 없도록 기록에 무조건 잠금을 설정합니다.
Marc B

1
기본적으로 트랜잭션을 코드 경로 내부의 보안으로 간주합니다. "병렬"코드 경로에서 보안 항목을 잠급니다. 교착 상태가
Marc B

1
@MarcB, 트랜잭션을 사용하여 이미 잠금이 제자리에 있음을 이미 보장한다면 왜 명시 적으로 잠금을 수행해야합니까? 트랜잭션만으로는 불충분해서 명시 적 잠금을해야하는 경우도 있을까요?
Pacerier

2
이 답변은 정확하지 않으며 잘못된 결론으로 ​​이어질 수 있습니다. 이 문장 : "잠금은 다른 사람이 처리중인 데이터베이스 레코드를 방해하지 못하도록합니다. 트랜잭션은"나중 "오류가"이전 "작업을 방해하지 않도록합니다. 둘 중 어느 것도 작업이 제대로 작동하는지 보장 할 수 없습니다. 하지만 함께하면됩니다. " -해고 당할 수 있습니다. 매우 잘못되고 어리 석습니다. 기사 참조 : en.wikipedia.org/wiki/ACID , en.wikipedia.org/wiki/Isolation_(database_systems)dev.mysql.com/doc/refman/5.1/ ko /…
Nikola Svitlica 2015 년

14

일반적으로 SELECT는 트랜잭션에 있는지 여부에 관계없이 테이블을 잠그지 않기 때문에 트랜잭션 내부 SELECT ... FOR UPDATE또는 SELECT ... LOCK IN SHARE MODE트랜잭션을 원합니다 . 선택하는 것은 트랜잭션이 진행되는 동안 다른 트랜잭션이 해당 행을 읽을 수 있는지 여부에 따라 다릅니다.

http://dev.mysql.com/doc/refman/5.0/en/innodb-locking-reads.html

START TRANSACTION WITH CONSISTENT SNAPSHOT다른 트랜잭션이 계속해서 해당 행을 수정할 수 있으므로 트릭을 수행하지 않습니다. 이것은 아래 링크의 맨 위에 언급되어 있습니다.

다른 세션이 동시에 같은 테이블을 업데이트하면 [...] 데이터베이스에 존재하지 않는 상태의 테이블을 볼 수 있습니다.

http://dev.mysql.com/doc/refman/5.0/en/innodb-consistent-read.html


7

트랜잭션 개념과 잠금이 다릅니다. 그러나 트랜잭션은 ACID 원칙을 따르기 위해 잠금을 사용했습니다. 읽기 / 쓰기를하는 동안 다른 사람이 동시에 읽기 / 쓰기를하지 못하도록 테이블을 보려면 잠금이 필요합니다. 데이터 무결성과 일관성을 확인하려면 트랜잭션을 사용하는 것이 좋습니다. 잠금을 사용하는 트랜잭션에서 격리 수준의 혼합 개념이 있다고 생각합니다. 트랜잭션의 격리 수준을 검색하십시오. SERIALIZE는 원하는 수준이어야합니다.


정답이되어야합니다. 잠금은 경합 상태를 방지하기위한 것이고 트랜잭션은 종속 데이터로 여러 테이블을 업데이트하기위한 것입니다. 트랜잭션이 잠금을 사용 함에도 불구하고 완전히 다른 두 가지 개념이 있습니다.
Blue Water

6

나는 시도 할 때 비슷한 문제가 있었고 여러 스레드가 동일한 테이블을 업데이트 할 때 경쟁 조건을 일으킨 IF NOT EXISTS ...을 수행했습니다 INSERT.

여기에서 문제에 대한 해결책을 찾았습니다. 표준 SQL에서 INSERT IF NOT EXISTS 쿼리를 작성하는 방법

나는 이것이 당신의 질문에 직접적으로 대답하지 않는다는 것을 알고 있지만, 하나의 문장으로 확인하고 삽입하는 동일한 원칙이 매우 유용합니다. 업데이트를 수행하기 위해 수정할 수 있어야합니다.


2

잠금 및 트랜잭션과 혼동합니다. RMDB에는 두 가지가 있습니다. 잠금은 트랜잭션이 데이터 격리에 집중하는 동안 동시 작업을 방지합니다. 확인 정화 및 일부 우아한 솔루션에 대한 좋은 기사를.


1
잠금은 작업중인 레코드를 다른 사람이 방해하지 못하도록 방지하고, 트랜잭션이 수행하는 작업을 간결하게 설명하고 트랜잭션은 나중에 수행 한 오류 (병렬로 변경하는 다른 사용자)가 이전 작업을 방해하지 않도록 방지합니다 (누군가 작업을 수행 한 경우 롤백을 허용 함) 병렬로) 거래를 거의 요약합니다 ...이 주제에 대한 그의 이해에 대해 혼란스러워하는 것은 무엇입니까?
steviesama

1

나는

START TRANSACTION WITH CONSISTENT SNAPSHOT;

시작하려면

COMMIT;

끝으로.

스토리지 엔진이 트랜잭션 (InnoDB)을 지원하는 경우 중간에 수행하는 모든 작업은 데이터베이스의 다른 사용자와 격리됩니다 .


1
그가 선택한 테이블을 제외하고는 그가 특별히 잠그지 않는 한 (또는 그의 UPDATE가 발생할 때까지) 다른 세션에 잠기지 않습니다. 즉, 다른 세션이 와서 SELECT와 UPDATE 사이에서 수정할 수 있습니다.
Alison R.

MySQL 문서에서 START TRANSACTION WITH CONSISTENT SNAPSHOT을 읽은 후 실제로 동일한 행을 업데이트하지 못하도록 다른 연결을 잠그는 위치를 알 수 없습니다. 내 이해는 트랜잭션이 시작될 때 테이블이 시작되었음을 볼 수 있다는 것입니다. 따라서 다른 트랜잭션이 진행 중이고 이미 행을 가져 와서 업데이트하려고하는 경우 두 번째 트랜잭션은 업데이트되기 전에 행을 계속 볼 수 있습니다. 따라서 잠재적으로 다른 트랜잭션과 동일한 행을 업데이트하려고 할 수 있습니다. 그게 맞습니까, 아니면 진행 중에 누락 된 것이 있습니까?
라이언

1
@Ryan 어떤 잠금도하지 않습니다. 당신이 올바른지. 잠금 여부는 수행하는 작업 유형 (SELECT / UPDATE / DELETE)에 따라 결정됩니다.
Alison R.

4
내가 참조. 자신의 트랜잭션 읽기 일관성을 제공하지만 다른 사용자가 수행하기 직전에 행을 수정하는 것을 차단하지 않습니다.
Martin Schapendonk
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.