2016-03-01 68 views
4

我有多個線程將事件寫入MySQL表events確保MySQL中的auto_increment值排序

該表的tracking_no列配置爲auto_increment,用於強制執行事件排序。 不同的讀者正在使用events,他們定期輪詢表以獲取新事件,並保留最後使用事件的值以獲取每次投票中的所有新事件。

事實證明,目前的實施留下遺漏一些事件的機會。

這是發生了什麼:

  • Thread-1開頭的「插入」交易,它需要從AUTO_INCREMENT列(1)的下一個值,但需要一段時間才能完成
  • Thread-2開始「插入」交易,它需要下一個auto_incremente值(2)並在Thread-1之前完成寫入。
  • Reader民意調查並要求track_number大於0的所有事件;它得到事件2,因爲Thread-1仍然落後。 事件被消耗,Reader將其跟蹤狀態更新爲2.
  • Thread-1完成插入,事件1出現在表中。
  • Reader對於2之後的所有事件再次進行輪詢,並且插入事件1時,它將永遠不會被再次拾取。

看來,這可以通過到事務完成時改變auto_increment策略來鎖定整個表來解決,但如果可能的話,我們會避免。

+0

我想補充當事件被創建(即使它最後一次更新)和消費者會詢問事件經過一定時間後插入(甚至更新),反映一個時間戳列。這將使您可以使用auto_increment,而無需表鎖定。另一種解決方案是減少交易開放的時間以避免此類問題。 – Shadow

+0

如何生成時間戳值? –

回答

1

我可以想到兩種可能的方法。 1)如果你的事件插入保證成功(也就是說,你永遠不會回滾一個事件插入,因此你的tracking_no中永遠不會有任何持續的空白),那麼你可以重寫你的Readers,以便他們跟蹤最後連續看到的事件 - 又名成功處理的最後一個事件。

閱讀器查詢事件存儲,按順序開始處理事件,然後在發現間隙時停止。剩下的事件被丟棄。下一個查詢使用上次成功處理的事件的序列號。

回滾會造成這種混亂,但是 - 併發寫入的場景可能會在流中留下持久的空白,這會導致讀者阻止。

2)您可以重寫您的查詢與時間代表的最大事件。有關設置時間戳記列的機制,請參閱MySQL create time and update time timestamp

然後,這個想法是,您的讀者查詢所有事件的序號比上次成功處理的事件更多,但時間戳小於now() - 一些合理的SLA間隔。

如果事件流的投影在時間上稍微落後,通常並不重要。所以你利用這一點,閱讀過去的事件,這可以保護你免受目前尚未完成的寫作。

這並不領域模型工作,雖然 - 如果要加載的事件流的寫入準備,從在過去的一個可測量的間隔流的工作是不會有太大的樂趣。好消息是,作家知道他們目前正在該對象的版本,因此在序列中它們產生的事件屬於。因此,您需要跟蹤架構中的版本,並將其用於衝突檢測。

注意我不完全清楚序列號應該用於排序。請參閱https://stackoverflow.com/a/9985219/54734

無論如何,合成密鑰(ID)毫無意義。他們的順序並不重要,他們唯一的重要性就是唯一性。你不能有意義測量兩個ID如何「相距甚遠」的,也不能說有意義,如果一個比另一個更大或更小。

所以這可能是有錯誤的問題的情況下。

+0

謝謝,第2點是我沒有想到的。我正在評估這是否可行,是否還有其他選擇。 –

+0

關於訂貨使用合成鍵:在我的情況我需要訂購,以確保事件按順序食用,因爲它們是時間敏感。我不介意在排序中存在差距(1,3,4)。我只關心讓他們全部按順序處理。 –