2011-12-14 42 views
4

我需要更新Oracle數據庫中的一行,但我不會在我的基於Web的應用程序中靜默地闖入其他客戶端的更改。選擇和更新之間的競爭條件

在我目前的系統I執行以下操作:

SELECT * FROM table WHERE id=:ID AND lastmodified=:LASTMOD 

如果行仍然以相同的最後修改日期存在,當我們開始我們知道沒有人改變了它,所以我們可以使用的最後修改時間更新。

然而,當使用兩個會話手動執行此操作時,我注意到如果兩個客戶端大致同時選擇它可能會因爲錯過在同一秒內發生的選擇和更新步驟之間的行而發生更改,或者毫秒。

最終的結果是我打斷了其他用戶的更改,並且沒有發生警告。

我正在考慮使用SELECT FROM UPDATE,但顯然這是一個bad idea(特別是對web應用程序),文章建議重讀(這是我在上面做的),但我仍然認爲我有風險競賽狀況。

編輯:明確表示我關心時間被引用的方式。你可以採取

+1

我認爲你鏈接的文章非常可疑。在DBA的生活中,比SELECT FOR UPDATE更多的悲哀巴恩。例如,執行不必要的讀取的gazillions和做額外的工作,而不是有一個適當的鎖定策略的過程。 – APC 2011-12-14 10:36:53

+0

SELECT FOR UPDATE的實際問題是它是* stateful *而HTTP是* stateless *。使用專爲閱讀文檔而設計的技術來實現OLTP應用程序只是瘋狂的,並且是無盡悲傷的源泉,但顯然我們是一個受虐狂行業8-) – APC 2011-12-14 10:40:02

回答

5

我假設您的UPDATE聲明本身正在驗證您的SELECT聲明中所讀取的lastmodified值是否爲已排除的建議。

如果lastmodifiedDATE,再有就是如果有多個更新到同一行中的相同第二因爲DATE只有粒度的第二潛在的競爭狀態。另一方面,如果lastmodifiedTIMESTAMP,則由於TIMESTAMP將具有3到9位數的亞秒級精度(大多數Windows機器上是3,大多數Unix上是6),因此競爭條件可能出現的窗口更受限制機)。在相同的毫秒甚至相同的微秒內進行兩次更新是非常不可能的,但並非不可能。但這不是絕對可靠的。

您可以使用序列生成值而不是最後修改日期。這可以保證你不會失去更新,因爲NOCYCLE序列不會返回兩次相同的值。但是如果你沿着這條路走下去,你要麼失去了在每一行都有最後更新日期的信息好處,要麼你在表的每一行中存儲了一些額外的數據字節。這些權衡取決於您的應用程序可能是值得的,或者可能產生比解決問題更多的問題。

1

一種方法是,當用戶更新表,他們補充

AND lastmodified = :LASTMOD 

WHERE條款的更新語句,其中:LASTMOD是用戶的原始選擇返回的值。如果更新現在影響行(SQL%ROWCOUNT=0),那麼您知道自第一個用戶最初運行其選擇以來第二個用戶已更新該行。

0

我不確定SQL內部解決方案,但我假設您使用一些腳本語言(php?perl?)來執行web應用程序/後端的東西,所以您可以簡單地使用它們來使用文件鎖定或信號量/互斥鎖來鎖定寫入和等待(如果鎖定)或者只是告訴用戶稍等片刻並重試。

作爲替代方案,您可以查看MediaWiki源代碼。我知道他們已經得到了保護,以避免兩名用戶在數據庫中覆蓋彼此的內容。說實話,我真的認爲維基百科將是一個非常大的平臺例子,這種平行編輯可能偶爾會發生一次。

0

另一種選擇是在表格上啓用ROWDEPENDENCIES(這需要重建表格!)並使用ORA_ROWSCN僞列。在行與塊級別跟蹤SCN每行需要6個字節;然而,它小於DATETIMESTAMP列,並且不需要創建其他對象,如序列或觸發器以確保序列已填充。

欲瞭解更多,請參閱Tom Kyte的問題問題here