2010-03-17 41 views
13

我有一個資源表(可以說汽車),我想自動聲明。然後我需要關於我剛纔聲稱的資源的信息。如何使用mysql中的UPDATE自動聲明一行或資源

如果有每一個用戶資源的極限,我可以做下面的技巧:

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

這樣,我要求原子資源,然後我可以看到哪一行我只是聲稱。

當'bob'可以聲明多輛車時,這不起作用。我意識到我可以得到一份已經被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等效 – 2010-03-17 08:26:21

回答

8

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

更新查詢返回更改的行數。如果它沒有更新,你就知道這輛車已經被別人搶走了。

或者,您可以使用SELECT ... FOR UPDATE。

+1

感謝您讓我知道SELECT ... FOR UPDATE - 這正是我所期待的。 – 2010-03-17 13:24:54

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」。也很重要:這隻適用於InnoDB作爲存儲引擎,而不適用於MyISAM。存儲類型可以用「顯示錶​​狀態」顯示。您可以使用「ALTER TABLE tablename ENGINE = INNODB;」更改存儲引擎 – seb 2013-06-17 11:48:17

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

被保證是原子和@id會告訴你什麼是你更新的最後一排。

+0

這真的很聰明。我肯定會在某些時候使用這個,雖然在這裏我想我更喜歡select ... for update – 2010-03-17 13:23:39

+0

由於某種原因,這給了我錯誤1064(參見http://stackoverflow.com/questions/32615884/leasing-jobs-atomic-更新和-GET-從-A-mysql的數據庫)。是否允許在'UPDATE'的'SET'部分設置用戶定義的變量? – 2015-09-16 18:35:10

+0

看來你需要使用黑客來使你的建議工作,因爲以這種方式設置用戶變量是不允許的。詳情在這裏:http://stackoverflow.com/questions/32615884/leasing-jobs-atomic-update-and-get-from-a-mysql-database/32617172#32617172 – 2015-09-16 19:38:50

0

我不確定爲什麼這些答案中有太多的錯誤信息,但答案很簡單(即使是高級SQL主題)。這就是幾乎所有RDBMS中的「鎖定」。確切的語法取決於供應商和版本,並且一些提供嘗試從用戶隱藏鎖的語法(通常當select和update都在同一個查詢中時)。

對於MySQL,首先使用SELECT ... FROM ... FOR UPDATE;,它告訴數據庫在它返回的每條記錄上設置排它鎖。

重要不鎖定更多的行比你絕對需要!儘可能細化「SELECT FOR UPDATE」查詢,並自由使用「WHERE」和「LIMIT」子句。

之後,當與數據庫相同的連接在先前鎖定的相同行上發出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;

那麼做時,它可以運行作業,並更新數據庫:

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

重要

從MySQL文檔:

由SHARE模式鎖定和FOR UPDATE查詢設置的所有鎖在事務提交或回滾被釋放。

SELECT FOR UPDATE如果啓用自動提交,則不鎖定記錄。要麼禁用自動提交或(最好是)使用START TRANSACTION; SELECT ... FROM ... FOR UPDATE; UPDATE ...; END TRANSACTION;