轉載

chyx413332087 11年前發布 | 3K 次閱讀 5.2.1版本發布

先分析一下,如果像樓主那樣處理,會發生什么樣的沖突:
1、對于update來說,不同線程間的update不會有影響,因為數據庫會處理這個沖突。
2、對于select來說,如果在delete前又有線程update,就會出現重復的數據,這樣造成重復的處理;
3、對于insert來說,如果在delete前又有線程insert,就會造成重復的數據插入t_tableB表。
4、對于delete來說,如果一個線程剛update完,還沒來得及select和insert,另一個線程就delete了,那么數據就會丟失。
……這里的沖突很多,就不一一列舉了。

首先提個建議,樓主之前分別用PushStatus=-1表示未處理,PushStatus=0表示處理中/完畢。
可以再引入一個狀態來區分處理中和處理完畢。如PushStatus=0表示處理中,PushStatus=2表示處理完畢。
------------------------------------------

宗旨:減少沖突,減少阻塞,提高并發,盡量批量操作。

方案1-模仿CAS的無阻塞算法: 【縮小更新的粒度】
1、先找出符合要求的100條;
SELECT ID,Name,Content FROM t_tableA WHERE PushStatus=-1 limit 100
2、處理每條數據,處理前先更新,更新成功后再處理。(這樣能避免數據的重復處理,把同步的任務交給了數據庫)
UPDATE t_tableA SET PushStatus=0 WHERE PushStatus=-1 AND ID=:ID
3、對該條數據進行insert和delete,此處的修改都是按照ID的,所以不會造成沖突。
點評:沒有使用鎖,線程間不用阻塞等待。但是每條數據都要分開更新,如果每條數據處理時間很短的話,那么效率就會大打折扣。

方案2-讀取鎖
1、更新-讀取-更新:先更新是為了批量獲取,獲取后再更新是為了批量占用已選數據。
把這個過程鎖起來,保證每次讀的時候不會沖突,這樣就不會產生重復處理的數據了。
UPDATE t_tableA SET PushStatus=0 WHERE PushStatus=-1 limit 100;
SELECT ID,Name,Content FROM t_tableA WHERE PushStatus = 0;
UPDATE t_tableA SET PushStatus=2 WHERE PushStatus=0;
點評:在讀取的時候加鎖,讀取和占用更新都是批量的,既解決了數據沖突的問題,也大大減少數據庫的操作次數。
但是,由于加了讀取鎖,所以要盡量增大每次讀取的數量,減少讀取的次數,否則鎖競爭會影響效率。


方案3-分角色線程
這個場景中,有四個操作:讀取數據、處理數據、轉移數據、清理數據。
所以我們可以分成三種不同的線程來處理:
1、讀取線程Read:負責讀取數據,update和select操作,先update成處理中狀態PushStatus=0,select獲取數據,然后分配給數據處理線程Deal來做。
2、數據處理線程Deal:負責處理數據,處理完畢后update成完成狀態PushStatus=2,因為數據都是讀取線程Read傳過來的,由讀取線程統一管理,所以數據處理線程只管處理,不用考慮數據的沖突,真正實現并發操作。
3、清理線程Clear:負責把已完成PushStatus=2的數據轉移到B表,然后清理A表。【清理的時候也涉及到數據沖突的問題,類似上面的情況,就不再討論了】
點評:這三種線程角色相互獨立,可以并行;而同一種線程角色中又可以并發,實現橫向和縱向的并發。
但是結構相對比較復雜,適合規模大的負責的業務場景。
理想狀態:
讀取線程Read獲取數據后調用數據處理線程Deal,在Deal處理過程中,Read繼續獲取新的數據,等Deal處理完畢了,Read剛好獲取完新的數據,然后又傳到Deal中來處理。【兩者線程數量的比例根據各自處理時間來定】
另外,如果B表的實時性不高的話,清理線程Clear一個就夠了,讓他每隔一段時間掃一掃就可以了。

 本文由用戶 chyx413332087 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!