2014-02-09 35 views
0

在我的數據庫(MySQL)中,我有一個包含名爲number的字段的表(MyISAM)。該字段的每個值都是0或正數。非零值必須是唯一的。最後一點是,該字段的值是在我的php代碼中根據此表中另一個字段(稱爲isNew)的值生成的。代碼摺疊。Mysql:在更新值之前讀取鎖定表

$maxNumber = $db->selectField('select max(number)+1 m from confirmed where isNew = ?', array($isNew), 'm'); 
$db->query('update confirmed set number = ? where dataid = ?', array($maxNumber, $id)); 

代碼的第一行選擇數字字段的最大值並遞增它。第二行通過設置新鮮生成的數字來更新記錄。

此代碼由數百個客戶端同時使用,所以我注意到有時會出現重複的數字字段。據我所知,當兩個客戶幾乎同時讀取數字字段的值時,就會發生這種情況,並導致重複。

我已閱讀關於SELECT ... FOR UPDATE語句,但我不太確定它適用於我的情況。

所以問題是我應該追加FOR UPDATE到我的SELECT語句嗎?或者創建一個存儲過程來完成這項工作?或者可能完全改變數字生成的方式?

+0

可怕......僅僅使用一個AUTO_INCREMENT int和用它做。你的代碼受到競爭條件的影響,並且將永遠如此。 –

+0

是否有你需要使用MyISAM作爲你的表引擎的原因?這與汽車公司時髦的事情,只會增加你的困境。首先重新思考你的模式,然後找出你的計劃。 – Zarathuztra

+0

auto_increment無法使用,因爲根據提及的其他字段(isNew) – kbeat

回答

1

這絕對是可以做到的。 MyISAM不提供交易鎖定功能,所以忘記了諸如FOR UPDATE之類的內容。示例代碼中兩條語句之間的競爭條件確實存在空間。你實施它的方式,就像說話的bur。一樣。它的功能非常棒,並不是它效果不錯! :-)

我不明白你在這個SQL做什麼:

select max(number)+1 m from confirmed where isNew = ? 

number值獨特的整個表,或只在集合,其中isNew具有一定的價值?如果number的值在整個表中是唯一的,它會工作嗎?這將更容易創建,調試和維護。

您需要一個獲取數字的多連接安全方法。

你可以試試這個SQL。它將在一個語句中設置最大數量。

UPDATE confirmed 
    SET number = (SELECT 1+ MAX(number) FROM confirmed WHERE isNew = ?) 
    WHERE dataid = ? 

這將表現糟糕。如果沒有(isNew, number)上的複合索引,並且沒有聲明這兩列,那麼它將表現得非常糟糕。

如果您可以使用整個表格中唯一的數字,我建議您爲自己創建一個sequence設置,每次使用時都會返回一個唯一的數字。您需要使用一系列連續的SQL語句來執行此操作。這是怎麼回事。

首先,當您創建表格時,創建一個表格以使用名爲sequence(或任何您喜歡的名稱)。這是一個單列表格。

CREATE TABLE sequence (
    sequence_id INT NOT NULL AUTO_INCREMENT, 
    PRIMARY KEY (`sequence_id`) 
) AUTO_INCREMENT = 990000 

這將使序列表開始在990,000處發出數字。

其次,當您在應用程序中需要唯一編號時,請執行以下操作。

INSERT INTO sequence() VALUES(); 
DELETE FROM sequence WHERE sequence_id < LAST_INSERT_ID(); 
UPDATE confirmed 
    SET number = LAST_INSERT_ID() 
    WHERE dataid = ? 

這是怎麼回事? MySQL函數LAST_INSERT_ID()返回最近自動增量生成的ID號碼的值。因爲您在sequence表中插入了一行,所以它會返回生成的ID號。 DELETE FROM命令使該表避免了增加磁盤空間;我們不關心舊的身份證號碼。

LAST_INSERT_ID()是連接安全的。如果軟件在不同的數據庫連接上使用它,它們都會得到自己的值。

如果您需要知道最後插入的ID號,你可以發出這樣的SQL:

SELECT LAST_INSERT_ID() AS sequence_id 

,你就會得到它返回。

如果您使用的是Oracle或PostgreSQL而不是MySQL,您會發現它們提供的基本上是這樣的SEQUENCE對象。

下面是對另一個類似問題的答案。

Fastest way to generate 11,000,000 unique ids

+0

生成值,根據isNew字段,表中有兩組'number'值。如果isNew = 0,那麼'number'從1000開始,否則數字從900000開始。正如你看到的,'number'的值在表中是唯一的,除了那些是零的。我喜歡你的方法順序,所以在我的情況下,我只需要創建和支持兩個預定義的序列,而不是一個。謝謝! – kbeat

+0

對!這樣可行。請參閱編輯有關創建序列表時初始化auto_increment值的信息。 –