데이터베이스 무결성을 보장하고 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
더 나은?
답변
테이블을 잠그면 다른 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가 마법처럼 계정에 다시 추가됩니다.
결국 다음과 같이 요약됩니다.
잠금은 다른 사람이 처리중인 데이터베이스 레코드를 방해하지 못하도록합니다. 트랜잭션은 “이후”오류가 “이전”작업을 방해하지 않도록합니다. 둘 중 어느 쪽도 결국 모든 것이 정상적으로 작동한다고 보장 할 수 없습니다. 하지만 함께라면 그렇습니다.
내일 수업 : 교착 상태의 기쁨.
답변
일반적으로 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
답변
트랜잭션 개념과 잠금이 다릅니다. 그러나 트랜잭션은 ACID 원칙을 따르기 위해 잠금을 사용했습니다. 읽기 / 쓰기를하는 동안 다른 사람이 동시에 읽기 / 쓰기를하지 못하도록 테이블을 보려면 잠금이 필요합니다. 데이터 무결성과 일관성을 확인하려면 트랜잭션을 사용하는 것이 좋습니다. 잠금을 사용하는 트랜잭션에서 격리 수준의 혼합 개념이 있다고 생각합니다. 트랜잭션의 격리 수준을 검색하십시오. SERIALIZE는 원하는 수준이어야합니다.
답변
나는 시도 할 때 비슷한 문제가 있었고 여러 스레드가 동일한 테이블을 업데이트 할 때 경쟁 조건을 일으킨 IF NOT EXISTS ...
을 수행했습니다 INSERT
.
여기에서 문제에 대한 해결책을 찾았습니다. 표준 SQL에서 INSERT IF NOT EXISTS 쿼리를 작성하는 방법
나는 이것이 당신의 질문에 직접적으로 대답하지 않는다는 것을 알고 있지만, 하나의 문장으로 확인하고 삽입하는 동일한 원칙이 매우 유용합니다. 업데이트를 수행하기 위해 수정할 수 있어야합니다.
답변
잠금 및 트랜잭션과 혼동합니다. RMDB에는 두 가지가 있습니다. 잠금은 트랜잭션이 데이터 격리에 집중하는 동안 동시 작업을 방지합니다. 확인 이 정화 및 일부 우아한 솔루션에 대한 좋은 기사를.
답변
나는
START TRANSACTION WITH CONSISTENT SNAPSHOT;
시작하려면
COMMIT;
끝으로.
스토리지 엔진이 트랜잭션 (InnoDB)을 지원하는 경우 중간에 수행하는 모든 작업은 데이터베이스의 다른 사용자와 격리됩니다 .