由彼觀彼,而不是由己觀彼。html
開始前,扯些許的題外話。咱們常常會看到一些相似於初、中、高級軟件工程師的區別的文章,以爲高級會如何,初中級又會如何,咱們這次就不區分title,而是經過經驗豐富與否、考慮問題是否全面、作事是否穩重來展開。spring
有這樣一個需求,咱們須要在國內WEB端新增的事件數據,不只要寫入國內的RDS,同時也要同步到國外的三個不一樣地點的RDS中(不可採用@scheduled方式進行定時同步)。markdown
@Transactional(rollbackFor = Exception.class) void newEvent(event) throws Exception { insertEventIntoInlandRds(event); asyncEventToOtherRds(event); // 異步同步數據 } 複製代碼
class CustomAsyncExceptionHandler implements AsyncUncaughtExceptionHandler { public void handleUncaughtException(Throwable throwable, Method method, Object... objects) { retry 5 times label = succeed after retring 5 times if label: insert async table successful label else: insert async table failed label and data } } 複製代碼
經過上述方式,想必有一批直接使用Spring Boot作項目開發的工程師,都自覺得標註上@Transactional
就能夠了,就會失敗回滾,也不須要什麼補救措施,那你就大錯特錯了。@Transactional
支持的是單體應用,遠程IO已經脫離了它的控制。異步
其次,同步失敗,重試5次沒問題,那同步概況記錄表應該放在哪裏呢?那些分別放到不一樣地域分別創建同步概況表的就不想說什麼了,retry都失敗了,你寫入同步概況到國外RDS就必定能成功嗎?答案是不能!因此記錄同步概況的表固然須要放在主RDS裏面(這裏主從是相對概念,從A同步到B,則A是主;若是從C同步到A,則C是主),也就是國內RDS。async
再次,若是asyncEventToOtherRds
調用失敗,致使newEvent
事務回滾,因此insertEventIntoInlandRds
也回滾,也就是國內RDS沒有event這條記錄了,可是海外還在不斷retry,海外可能的結果是失敗 or 成功。此時,會存在兩種不一樣狀況:1)海外同步失敗,則國內外數據是一致的;2)海外同步成功,則國內外數據就是不一致的。因此這裏存在的問題就是事務回滾卻沒有數據探測以及補償措施。ide
而後,若是retry了5次仍然失敗了,那該如何處理呢?這裏一樣須要有必定的補償措施,而不是簡單的記錄一下失敗同步記錄和數據就完事大吉,久而久之,累積同步失敗記錄多了必定會出大問題的。spring-boot
最後,就是這樣的設計明顯是沒有徹底理解業務的主次,沒有作好責任劃分和事務隔離。須要在保證國內RDS必定寫入成功,提交事務後,再進行海外RDS數據同步,這樣咱們就能夠針對性對症下藥處理便可,而不用一邊考慮國內失敗如何,海外又失敗如何,儘可能責任單一。oop
其實,這裏寫結論的時候,是很不想寫的。爲何呢,由於其實這個無關乎title,真的只是關乎經驗、考慮問題是否全面、是否有思辨思惟。牽扯到數據方面的問題的時候,必定要打起12分的精神,否則數據都不許,誰還敢用大家的產品呢?學習
可是,到此,仍是想說上兩句:1)技術必定要紮實,不要對一門技術只知其一;不知其二就以爲徹底OK。好比上面的@Transactional
註解的使用就是個很好的例子。2)必定要有思辨思惟去看到每一件事情,凡事都是雙面性的。ui
因此,這裏的結論就是不作結論。但願本身思考,本身是否屬於上述顧頭不顧尾的往前跑,卻無論產生的影響。
以前,耗子叔曾說過,「學習原本就是件逆人性的事情,若是要成長,必須逼本身一把」。話沒錯,正式由於停留在技術的膚淺層面,最終纔會出現上述問題。
也有人說,「失敗是成功之母」。不,失敗不會使你成功,總結失敗纔會助你成功。若是隻是一味地不斷重複錯誤,那你註定只能原地踏步。但願咱們可以剝離這種潛意識!擅於總結問題,發現問題,積累經驗,不斷成長。
寫完文章後,發現仍是有必要貼上一些有助於理解上述問題的文章連接。就在網上找了下最佳解讀的: