2012-05-01 69 views
1

我有一個應用程序,遺憾的是MyISAM表(bleagh ...)使用遺留mysql_ *函數,所以我不能使用事務。我有代碼獲得當前餘額,檢查這個餘額是否正常,如果是,它會減去一個數量並保存新餘額。MySQL SELECT返回舊值

問題是,我最近看到一個實例,其中兩個查詢獲取相同的起始餘額,減去一個數量,然後記錄新的餘額。既然他們都抓住了相同的起始餘額,那麼兩次UPDATES之後的期末餘額都是錯誤的。

100 - 10 = 90 
100 - 15 = 85 

當它應該是...

100 - 10 = 90 
90 - 15 = 75 

這些請求執行的若干分鐘的間隔,所以我不相信的差異是由於競爭條件。但我最初的想法是MySQL緩存存儲了獲取餘額的相同初始查詢的結果。然而,我讀到,如果修改了任何相關的表,這種類型的緩存就會被刪除。

我很有可能通過將所有內容放入一個查詢來解決問題,但我仍然想知道這一點。它使我感到神祕。如果緩存在修改表時被刪除,那麼發生的事情不應該發生。有沒有人聽說過這樣的事情,或有任何想法,爲什麼它可能發生?

+1

你能發佈一個簡化版本的代碼來重現這種行爲嗎? –

+0

你確定你不能使用'mysql_ *'進行交易嗎?我認爲這只是做'mysql_query(「START TRANSACTION」)等等,只要你使用innoDB表。 – bfavaretto

+0

@bfavaretto不幸的是他們是MyISAM。 – dqhendricks

回答

1

鎖定表是解決您的問題,我認爲:)

2

它不太可能成爲查詢緩存 - 如果底層數據集已被另一個查詢修改,MySQL足夠聰明地使緩存項失效。如果查詢緩存在過期時間內保持舊陳舊值,MySQL將完全無用。

你可能有未完成的未完成事務嗎?如果沒有適當的鎖定相關記錄,您的第二個查詢可能會很容易地抓取陳舊的數據。

+0

我同意。這不是一個緩存問題。這很可能是應用程序有過時的數據。OP說這是MYISAM,所以沒有交易。 –

+0

我現在在讀取或寫入任何數據之前鎖定所有的表格,我也在數據讀取中指定沒有緩存。問題再次發生。無法想象這裏發生了什麼。正如馬庫斯所說,這是MYISAM,所以沒有交易。禁用了應用程序中其他區域的所有編輯。我通過代碼搜索相關變量,並且沒有意外的任務。 299/300次這個代碼工作得很好,大約每月一次我看到這個問題發生。 – dqhendricks

2

最有可能您的應用程序有過時的數據。這很好,這是許多數據庫應用程序是如何工作的,但是當你執行的,而不是做這樣的事情你更新:

UPDATE account 
SET balance = :current_balance - 10 
WHERE account_id = 1 

你需要做更多的事情是這樣的:

UPDATE account 
SET balance = balance - 10 
WHERE account_id = 1 

這樣,您可以使用數據庫中的當前餘額,即使有人在同一時間對其進行了更改,而不是依賴陳舊的應用程序數據。

如果你想只改變值,如果沒有其他人修改了它,那麼你做這樣的事情:

UPDATE account 
SET balance = balance - 10 
WHERE account_id = 1 
    AND balance = :current_balance 

如果受影響的行數是1,那麼你就成功了,記錄hadn別人改變了。但是,如果受影響的行數爲0,則其他人會更改記錄。然後,您可以從那裏決定您想要做什麼。

+0

我會這樣做,除非我必須在餘額前執行一些檢查並創建新條目並更新餘額。我要實現表鎖定。 – dqhendricks

+0

@dqhendricks,你需要實現這個加鎖​​,然後,因爲併發不是問題。你說這些請求分開了幾分鐘。 –

+0

那麼,我在最初的SELECT中做了表鎖+ SQL_NO_CACHE。這應該解決我期望的任何陳舊的緩存數據問題。如果可能,您的解決方案需要進行大量重構,我希望避免這種重構。如果我再看到它,我會回來的。 – dqhendricks