2010-03-17 4 views
13

나는 원자 적으로 소유하고 싶은 리소스 테이블을 가지고있다. 그러면 나는 방금 요청한 자원에 대한 정보를 원합니다. 한 사용자 당 하나 개의 자원의 한계가 있다면mysql에서 UPDATE를 사용하여 행 또는 리소스를 원자 적으로 클레임하는 방법

, 나는 다음과 같은 트릭을 수행 할 수 있습니다

UPDATE cars SET user = 'bob' WHERE user IS NULL LIMIT 1 
SELECT * FROM cars WHERE user = 'bob' 

이 방법, 나는 원자 자원을 주장하고 난 그냥 주장하는 행을 볼 수 있습니다.

'밥'이 여러 자동차를 소유 할 수있는 경우에는 작동하지 않습니다. 나는 내가 밥에 의해 벌써 요구하게되는 차의 목록을 알아들을 수 있고, 또 다른 하나를 요구할 수 있고, 그 다음 SELECT가 무엇이 바뀌 었는지에 관해 알기 위해 다시 알 수있다. 그러나 그것은 hackish를 느낀다.

궁금한 점은 마지막 업데이트로 업데이트 한 행을 확인할 수있는 방법이 있습니까?

오류가 발생하면 행을 원자 적으로 요청하는 다른 트릭이 있습니까? 난 정말 SERIALIZABLE 격리 수준을 사용하지 않으려합니다. 나는 같은 것을 할 경우 :

1 SELECT id FROM cars WHERE user IS NULL 
2 <here, my PHP or whatever picks a car id> 
3 UPDATE cars SET user = 'bob' WHERE id = <the one i picked> 

REPEATABLE READ 여기에 충분할 것이다? 즉, 다른 거래가 2 단계에서 소프트웨어가 선택한 행을 주장하지 않을 수 있습니까?

+0

발견 : http://stackoverflow.com/questions/1821142/atomically-mark-and-return-a-group-of-rows-in-database SQL 서버에 대해 물어 보았습니다 - 나는 MySQL에 해당하는 –

답변

8

UPDATE cars SET user = 'bob'WHERE id = 123 AND user IS NULL;

업데이트 쿼리는 변경된 행 수를 반환합니다. 업데이트되지 않은 경우, 자동차가 이미 다른 사람에 의해 소유권이 주장되었음을 알고 있습니다.

또는 SELECT ... FOR UPDATE를 사용할 수 있습니다.

+1

을 찾을 수 없어서 SELECT ... FOR UPDATE에 대해 알려 주셔서 감사합니다. –

1

사용할 수있는 것은 SELECT FOR UPDATE입니다. 이렇게하면 선택을하고, 선택한 것을 알고, 그 값을 업데이트 할 수 있습니다. 트랜잭션이 완료되면 잠금이 해제됩니다.

<?php 
$link = mysqli_connect("localhost", "my_user", "my_password", "test"); 

mysqli_autocommit($link, FALSE); 

$result = mysqli_query($link, "SELECT id FROM cars WHERE user IS NULL"); 

// do something with the results 

mysqli_query($link, "UPDATE cars SET user = 'bob' WHERE id = <the one that was picked>"); 

mysqli_commit($link); 

mysqli_close($link); 
?> 
+2

당신의 예가 잘못되었습니다, 당신은 UPDATE 쿼리에 "FOR UPDATE"를 추가해야합니다. 또한 매우 중요합니다 : 이것은 MyoAM이 아닌 스토리지 엔진으로 InnoDB에서만 작동합니다. 스토리지 유형은 "show table status;"와 함께 표시 될 수 있습니다. "ALTER TABLE tablename ENGINE = INNODB;"를 사용하여 스토리지 엔진을 변경할 수 있습니다. – seb

1
update cars 
set @id = id, user= 'bob' 
where user is null 

은 원자 보장되고 @id는 업데이트 된 마지막 행 무엇을 말할 것이다.

+0

이것은 정말 영리합니다. 비록 내가 여기에 내가 선택을 선호한다고 생각하지만, 확실히 어떤 점에서 이것을 사용할 것이다 ... –

+0

이것은 어떤 이유로 나는 이것이 오류 1064를 준다. (http://stackoverflow.com/questions/32615884/leasing-jobs-atomic- mysql-database로부터의 update-and-get-a-mysql- 데이터베이스). 'UPDATE'의'SET' 부분에 사용자 정의 변수를 설정할 수 있습니까? –

+0

이런 식으로 사용자 변수를 설정하는 것은 허용되지 않으므로 해킹을 사용하여 제안을해야합니다. 여기에 세부 정보 : http://stackoverflow.com/questions/32615884/leasing-jobs-atomic-update-and-get-from-a-mysql-database/32617172#32617172 –

0

왜 이러한 답변에는 많은 잘못된 정보가 있는지 모르겠지만 대답은 간단합니다 (고급 SQL 항목 일지라도). 거의 모든 RDBMS에서 "잠금"이 필요한 것입니다. 정확한 구문은 공급 업체 및 버전 및 사용자로부터 잠금을 숨기려고하는 제안 구문 (일반적으로 선택 및 업데이트가 동일한 쿼리에있는 경우)에 따라 다릅니다.

MySQL의 경우, 먼저 SELECT ... FROM ... FOR UPDATE;을 사용하면 데이터베이스에 반환하는 각 레코드에 배타적 잠금을 설정하도록 지시합니다.

중요 절대적으로 필요한 것보다 많은 행을 잠그지 마십시오! "WHERE"및 "LIMIT"절을 자유롭게 사용하면서 가능한 한 세분화 된 "SELECT FOR UPDATE"쿼리를 만듭니다.

그런 다음 데이터베이스에 대한 동일한 연결로 이전에 잠근 동일한 행에 UPDATE ...을 발행하면 해당 잠금이 해제되고 다른 사용자는 해당 행에 한 번 더 액세스 할 수 있습니다.

각 작업의 진행 상태를 설정하는 데 사용되는 STATUS 필드가있는 작업 대기열이 있다고 가정 해 보겠습니다. 0은 대기열에, 1은 진행중이고 2는 완료를 위해, 3은 실패 등에 대한 것입니다.

각 주자는 실행할 작업을 기본적으로 얻을 수 있습니다 (두 명의 주자가 동일한 작업을 시도하지 않도록)

SELECT ID, * FROM JOBS WHERE STATUS = 0 LIMIT 1 FOR UPDATE;

다음

다음

UPDATE JOBS SET STATUS = 1 WHERE JOBS.ID = X;

할 때 데이터베이스 작업을 실행하고 업데이트 할 수 있습니다 : 다음을 실행하여

MySQL의 문서에서 중요

UPDATE JOBS SET STATUS = [2|3] WHERE JOBS.ID = X;

: 트랜잭션이 커밋 또는 롤백 할 때 공유 모드 IN과 UPDATE 쿼리에 대한 LOCK으로 설정

모든 잠금이 해제됩니다.

SELECT FOR UPDATE 자동 커밋을 사용하는 경우 레코드를 잠그지 않습니다. 자동 연결을 해제하거나 (바람직하게) 사용하지 않음 START TRANSACTION; SELECT ... FROM ... FOR UPDATE; UPDATE ...; END TRANSACTION;