2012-02-06 43 views
0

這種事情已經完成了一百萬次,但我確信,但我的搜索foo今天看起來很虛弱,我想獲得關於通常被認爲是最佳實現這一目標的方法。鎖定線程間併發數據庫記錄

我的應用程序會跟蹤系統中在線用戶的會話。每個會話對應於數據庫中的單個記錄。會話可以通過以下兩種方式之一結束。收到「停止」消息,或會話可能超時。前一種情況很簡單,它在消息處理線程中處理,一切都很好。後一種情況是關注點來自哪裏。

爲了處理超時,每條記錄都有一個結束時間列,每次收到該會話的消息時都會更新一次。爲了使超時工作,我有一個線程返回NOW()(結束時間爲過去)的數據庫中的所有記錄,並通過處理關閉這些會話。這裏的問題是,當超時線程正在處理同一會話時,可能會收到會話消息。我最終在超時線程和消息處理線程之間進行競爭。

我可以使用一個信號燈或類似的東西,只是防止消息線程在超時發生時處理,因爲它只需要每30秒或一分鐘運行一次。但是,隨着用戶表變大,這將會遇到一些嚴重的性能問題。我想我想知道的方法是在消息線程中知道此記錄目前正在由超時線程處理。如果我能夠實現這一點,我可以放棄該消息或等待超時線程結束,但僅在衝突現在而不是總是衝突的情況下。

目前我的應用程序直接使用JDBC。如果我使用Hibernate這樣的框架,會不會有更簡單/標準的方法來解決這個問題?

+0

您可以通過會話ID而不是線程級別來包裝信號量,因此您已在消息處理程序和超時線程的關閉部分中同步(sessionID)塊。我認爲它不像你想要的那麼幹淨,但是它比將整個線程鎖定在單個信號量上的性能影響更小,因爲只有該會話的消息纔會被阻塞。 – Thomas 2012-02-06 16:24:18

+0

這將如何工作?我的理解是,這將鎖定* object * sessionID,而不是sessionID的*值*。由於在數據庫讀取之後在兩個線程中創建的對象不是同一個對象,所以它看起來好像永遠不會阻塞。如果您有一個鎖對象的字段並在同步塊之前從兩個線程更新它,則您擁有與之前的併發性相同的問題,只需將焦點切換到鎖對象即可。除非有一種方法可以同步我不知道的*值*上的塊。 – tdimmig 2012-02-06 21:51:13

+0

答案根據你的結構而改變。如果你有mysession.handleMessage(x)和mysession.closeAndCleanup(),你應該可以鎖定mysession。然後競爭條件是,如果在會話被標記爲關閉之後但在到達同步塊之前接收到消息,則將處理消息並關閉會話或關閉會話,並且消息將返回關於超時的錯誤。如果結構是handlemessage(ID,msg)和close(ID),其中ID只是一個int,那麼您需要一個Map或其他東西來同步。 – Thomas 2012-02-07 15:53:12

回答

1

這是各種瘋狂錯誤發生的絕佳機會,某些治療可能會導致性能問題。

經典的解決方案是使用事務(http://dev.mysql.com/doc/refman/5.0/en/commit.html)。這使您可以保證數據的一致性 - 但數據庫上長時間運行的事務將其變成一個巨大的瓶頸;如果您的「查找超時會話」代碼運行一分鐘,則該事務可能會在整個時間段內運行,從而有效鎖定對受影響的表的寫訪問權限。大多數系統不能很好地處理這個問題。

我對這種情況的偏愛解決方案是有一個「狀態機」狀態;我喜歡將它作爲歷史表來實現,但這確實會導致數據庫快速增長。

您將會話的狀態定義爲「啓動」,「運行」,「超時 - 關閉」,「超時 - 關閉」和「由用戶停止」(例如)。

您可以實現代碼,在您擁有的任何數據訪問邏輯中尊重狀態轉換邏輯。然後你的「清理」腳本的僞代碼可能是:

  • 更新的所有記錄,其結束時間< NOW(),其狀態爲「運行中,設置狀態=‘超時 - 關閉’
  • 對於狀態的每個記錄是「超時 - 關閉」
    • 做你需要做的
    • 更新該記錄集狀態什麼其他的東西「超時 - 關閉」,其中狀態=「超時 - 關閉「
  • 下一條記錄

修改會話記錄當前狀態的所有其他嘗試都必須檢查當前狀態對嘗試的更改是否有效。

例如,「手動」停止代碼應該是這樣的:

update sessions 
set status = "stopped by user" 
where session_id = xxxxx 
and status = 'running' 

如果自動關閉例程的時候拉開帷幕顯示用戶界面和數據庫的代碼中,其中間子句將不匹配任何記錄,因此其餘代碼不會運行。

爲此,所有修改會話狀態的代碼都必須檢查其前提條件;最可維護的方法是編碼狀態並允許轉換到單獨的數據庫表中。

你也可以編寫觸發器來強制執行這個邏輯,儘管我通常不是觸發器的粉絲 - 只有在必須時才這樣做。

我不認爲這增加了顯着的性能問題 - 但測試和優化。數據庫的大部分額外工作是爲更新語句添加額外的「where」子句;假設你有一個關於狀態的指數,它不太可能產生可衡量的影響。

+0

這會向工作負載添加一些數據庫調用,這顯然會引起性能問題。另外,除非我錯過了某些東西,否則您仍然有可能發生併發問題?如果線程2在線程1持續「超時 - 關閉」之前讀取記錄,由於沒有任何東西可以阻止它,它會認爲一切正常,並繼續進行任何處理。也許這可以通過在處理開始時標記問題窗口來縮小問題窗口,而不是等待結束,但窗口仍然存在。 – tdimmig 2012-02-06 21:54:53

+0

我已更新答案以反映您的疑慮。 – 2012-02-07 09:23:54