為什麼Actor模型是高併發事務的終極解決方案?
使用者甲的操作
1.開始事務
2.訪問表A
3.訪問表B
4.提交事務
乙使用者在操作
1.開始事務
2.訪問表B
3.訪問表A
4.提交事務
如果甲使用者和乙使用者的兩個事務同時發生,甲事務鎖住了表A未釋放(因為整個事務未完成),正在準備訪問B表,而乙事務鎖住了表B未釋放(因為整個事務未完成),正在準備訪問A表,可是A表被甲事務鎖住了,等甲事務釋放,而甲事務真正等待乙事務釋放B表,陷入了無限等待,也就是死鎖Dead Lock。
也有道友使用多執行緒來模擬儲存過程:http://www.jdon.com/45727,每個執行緒裡開啟一個事務,類似上述問題也會出現死鎖。
問題出在哪裡?
是我們的思路方向出現問題:
其實無論是使用資料庫鎖 還是多執行緒,這裡有一個共同思路,就是將資料餵給執行緒,就如同計算機是一套加工流水線,資料作為原材料投入這個流水線的開始,流水線出來後就是成品,這套模式的前提是資料是被動的,自身不復雜,沒有自身業務邏輯要求。適合大資料處理或網際網路網站應用等等。
但是如果資料自身要求有嚴格的一致性,也就是事務機制,資料就不能被動被加工,要讓資料自己有行為能力保護實現自己的一致性,就像孩子小的時候可以任由爸媽怎麼照顧關心都可以,但是如果孩子長大有自己的思想和要求,他就可能不喜歡被爸媽照顧,他要求自己透過行動實現自己的要求。
資料也是如此。
只有我們改變思路,讓資料自己有行為維護自己的一致性,才能真正安全實現真正的事務。
資料+行為=物件,有人問了,物件不是也要被執行緒呼叫嗎?
例如下述程式碼,因為物件的行為要被執行緒呼叫,我們要使用同步鎖synchronized :
public class A { private volatile int lower, upper; //兩個狀態值 public int getLower() { return lower; } public int getUpper() { return upper; } public synchronized void setAUpper(int value){ if (value < a.getUpper()) a.setLower(value); } public asynchronization void setALower(int value){ if (value > a.getLower()) a.setUpper(value); } } <p class="indent"> |
上面這段程式碼業務邏輯是想實現lower<upper:
1. lower和upper的初始值是(0, 5),
2.一個客戶端請求執行緒A: setLower(4)
一個客戶端請求執行緒B: setUpper(3)
3. lower和upper是 (4, 3)
這個結果破壞了lower<upper這個邏輯一致性,所以,用鎖並不能保證邏輯一致性,而且還帶來了堵塞。鎖用錯了地方,不但沒有得到想要的,而且還失去更多。
下圖展示了鎖帶來堵塞,每個時刻只能允許一個執行緒工作,如同只能允許一個人蹲馬桶一樣。
[img index=1]
從歷史上看,鎖的問題如鬼魂一直伴隨著我們:
1.用資料表一個欄位來表示狀態,比如1表示已付款未發貨,2表示已付款已發貨,然後使用者來一個請求用SQL或儲存過程修改,這時使用的資料庫鎖。
2.用ORM實現,比如Hibernate JPA來修改狀態,雖然不用SQL了,但是Hibernate的悲觀鎖和樂觀鎖也讓人抓狂。
3.徹底拋棄資料庫,直接在記憶體快取中進行修改,使用Java的同步鎖,效能還是不夠,吞吐量上不去。如上圖提示,只能一個廁所蹲位一個人用,其他人必須排隊。
4.Actor模型。
Actor模型原理
Actor模型=資料+行為+訊息。
Actor模型內部的狀態由自己的行為維護,外部執行緒不能直接呼叫物件的行為,必須透過訊息才能激發行為,這樣就保證Actor內部資料只有被自己修改。
Actor模型如何實現?
Scala或ErLang的程式信箱都是一種Actor模型,也有Java的專門的Actor模型,這裡是幾種Actor模型比較
明白了Actor模型原理,使用Disruptor這樣無鎖佇列也可以自己實現Actor模型,讓一個普通物件與外界的互動呼叫透過Disruptor訊息佇列實現,比如LMAX架構就是這樣實現高頻交易,從2009年成功執行至今,被Martin Fowler推崇。
回到本帖最初問題,如何使用Actor模型解決高併發事務呢?
轉賬是典型的符合該問題的案例,轉賬是將A帳號到B帳號轉賬,使用Actor模型解決如下:
發出是否可轉出訊息--->訊息佇列--->A
A作為一個物件,注意不是資料表,物件是有行為的,檢查自己餘額是否可轉賬,如果可以,凍結這部分金額,比如轉賬100元,凍結100元,從餘額中扣除。因為外部命令是透過訊息順序進來的,所以下一個訊息如果也是扣除,再次檢查餘額是否足夠......
具體詳細流程可見:REST和DDD
那麼,既然Actor模型如此巧妙,而解決方向與我們習慣的資料喂機器的方式如此不同,那麼如何在實戰中能明顯發現某個資料修改應該用Actor模型解決呢?因為我們習慣將資料喂機器的思路啊?
使用DDD領域驅動設計或CQRS架構就能明顯發現這些特殊情況,CQRS是讀寫分離,其中寫操作是應領域專家要求編寫的功能,在這類方向,我們都有必要使用Actor模型實現,因為在這個方向上,領域專家的要求都表達為聚合根實體,聚合根就是用Actor模型實現最合適不過了。而讀方向,比如大資料處理,報表查詢,OLTP等等都是資料喂機器的方式。
有的道友會疑問,我們經常使用SSH,也就是Spring + Hibernate架構,這個預設是哪種方向呢?很顯然,預設是資料喂機器的方向,所以在實現寫操作時,特別警惕高併發發生死鎖等影響效能問題,當然也包括EJB架構。
有一種togaf架構,將企業軟體架構分為資料架構和應用架構等,實際是EJB或SSH的變相描述,這種架構的問題我們已經一目瞭然了,特別這樣的系統如果從面向內部管理轉向到SaaS模型時,這類高併發死鎖問題就特別容易發生,幾乎不具備可用性。前期12306火車票系統是這類問題的典型體現。
[該貼被banq於2013-09-12 08:09修改過]
[該貼被admin於2013-09-15 07:13修改過]
[該貼被admin於2013-09-15 07:29修改過]
[該貼被admin於2013-09-15 07:30修改過]
相關文章
- 什麼是高併發,怎麼解決高併發
- 大佬你是怎麼解決高併發的
- Lite Actor:方舟Actor併發模型的輕量級優化模型優化
- 什麼是政務新媒體的終極追求?
- 高併發解決方案詳解(9大常見解決方案)
- 高併發業務場景下的秒殺解決方案 (初探)
- 高併發和大流量解決方案
- 高併發解決方案orleans實踐
- 高併發下丟失更新的解決方案
- PHP高併發和大流量的解決方案PHP
- 海量資料和高併發的解決方案
- Exception in thread “main” 終極解決方案ExceptionthreadAI
- 高併發大容量NoSQL解決方案探索SQL
- golang定時任務踩坑及終極解決方案Golang
- mysql 高併發 select update 併發更新問題解決方案MySql
- 併發數、併發以及高併發分別是什麼意思?
- 高併發下的介面冪等性解決方案!
- 什麼是Actor思維?
- 終極自託管解決方案指南
- mac php環境終極解決方案MacPHP
- 前端(React)生成pdf終極解決方案(^_^)前端React
- Loguru:Python 日誌終極解決方案Python
- H5定位終極解決方案H5
- PHP下用Swoole實現Actor併發模型PHP模型
- 什麼是TOGAF解決方案? - Anatolii
- 到底什麼是解決方案公司?
- PHP高併發商品秒殺問題的解決方案PHP
- JavaWeb 亂碼問題終極解決方案!JavaWeb
- INSTALL_FAILED_NO_MATCHING_ABIS終極解決方案AI
- 分散式事務解決方案分散式
- 談談高併發系統的一些解決方案
- 一鍵成片解決方案是什麼?
- 常用的分散式事務解決方案分散式
- 分散式事務瞭解嗎?你們的多個服務間資料一致性解決方案是什麼?分散式
- YII2.0 jQuery(…).activeform is not a function in 終極解決方案jQueryORMFunction
- web app 無限載入終極解決方案WebAPP
- [iOS]終極橫豎屏切換解決方案iOS
- 單元件多UI形態的終極解決方案(React)元件UIReact
- WordPress中實現Markdown編輯的終極解決方案