精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

隱藏了兩年的Bug,終于連根拔起,悲觀鎖并沒有那么簡單

開發 前端
接手的新項目,接二連三的出現賬不平的問題,作為程序員中比較執著的人,不解決誓不罷休。最終,經過兩次,歷時多日終于將其連根拔起。實屬不易,特寫篇文章記錄一下。

接手的新項目,接二連三的出現賬不平的問題,作為程序員中比較執著的人,不解決誓不罷休。最終,經過兩次,歷時多日終于將其連根拔起。實屬不易,特寫篇文章記錄一下。

文章中不僅會講到使用悲觀鎖踩到的坑,以及本人是如何排查問題的,某些思路和方法或許能對大家有所幫助。

事情的起源

運營同事時不時就提出查賬調賬的需求,原因很簡單,賬不平,不查不行。如果你有過財務相關系統的工作經歷,賬務問題始終是最難攻克的。

雖然剛接手項目,雖然很多業務邏輯還不了解,但出現這樣的技術挑戰,還是要堅決攻克的。

其實,這類問題的原因很簡單:熱點賬戶。當很多服務或線程操作同一個用戶的賬戶時,就會出現一個更新把另外一個更新覆蓋掉的情況。

賬戶不平

上圖可輕易看出,當兩個服務或線程同時查詢數據庫的一條數據(熱點賬戶),然后內存中做修改,最后更新到數據庫。如果出現并發情況,兩個線程都讀取了100,一個計算得80,一個計算得60,后更新的就有可能將前面的覆蓋掉。

解決方案通常有:

  • 單服務線程鎖;
  • 集群分布式鎖;
  • 集群數據庫悲觀鎖;

項目中已采用了悲觀鎖,就基于來進行排查追蹤原因。

何謂悲觀鎖

悲觀鎖是在對數據被的修改持悲觀態度,在整個數據處理過程中會將數據鎖定。

悲觀鎖的實現,往往依靠數據庫提供的鎖機制(也只有數據庫層提供的鎖機制才能真正保證數據訪問的排他性,否則,即使在應用層中實現了加鎖機制,也無法保證外部系統不會修改數據)。

通常會使用select ... for update語句來實現對數據的枷鎖。

for update僅適用于InnoDB,且必須在事務塊(BEGIN/COMMIT)中才能生效。在進行事務操作時,通過“for update”語句,MySQL會對查詢結果集中每行數據都添加排他鎖,其他線程對該記錄的更新與刪除操作都會阻塞。排他鎖包含行鎖、表鎖。

如下示例展示了悲觀鎖的基本使用流程:

  1. set autocommit=0;   
  2. //設置完autocommit后,執行正常業務。具體如下: 
  3. //0.開始事務 
  4. begin;/begin work;/start transaction; (三者選一就可以) 
  5. //1.查詢出商品信息 
  6. select status from t_goods where id=1 for update
  7. //2.根據商品信息生成訂單 
  8. insert into t_orders (id,goods_id) values (null,1); 
  9. //3.修改商品status為2 
  10. update t_goods set status=2; 
  11. //4.提交事務 
  12. commit;/commit work

因為關閉了數據庫自動提交,這里通過begin/commit來管理事務。

使用select…for update的方式通過數據庫實現了悲觀鎖。其中,id為1的那條數據就被鎖定,其它的事務必須等本次事務提交之后才能執行。這樣就保證了在操作期間數據不會被其它事務修改。

原因初步分析

在了解了賬不平的原因和悲觀鎖的基本原理之后,就可以進行問題的排查了。既然系統已經使用了悲觀鎖,竟然還會出現問題,那肯定是哪里漏掉了什么。

于是,排查了所有賬戶(account表)更新的地方,還真找到一處bug。

大多數地方都使用了悲觀鎖,先for update查詢一下,然后計算新的余額,再進行更新數據庫。但有一處竟然先查詢到了計算了余額,然后再進行加鎖,最后更新。

基本流程如下:

錯誤加鎖

在上述情況中,雖然線程B進行了加鎖處理,但由于計算新余額并未在鎖中,導致雖然使用了悲觀鎖,但依舊存在問題。正確的使用方式就是將計算余額的邏輯放在鎖中。

當然,如果線程B完全被遺忘加鎖了,也會出現同樣的問題。

在排查解決了上述bug,我開始嘚瑟了,以為徹底解決了賬不平的問題。

一個月之后

結果一個月之后,運營同事又來找了,偶爾依舊會出現賬不平的問題。剛開始我還以為是不是搞錯了,歷史的賬不平導致現在最終的不平。但最終還是下定決心再排查一次。

第一天,把賬不平的賬戶的賬務流水、涉及到代碼、日志全部捋一遍。這期間還遇到了很多小困難,最終注意克服。

困難一:數據查不動

賬務記錄表數據太多,上千萬的數據,最初的設計者并沒有創建索引。這就要了老命了,根據篩選條件根本查不出數據來。

這里就用到SQL優化的兩個技能點:limit限制查詢條數和高效的分頁策略。

關于limit限制查詢條件這一點很明顯,不僅減少了結果集,而且在遇到符合條件的數據之后會立馬返回。

高效的分頁策略在列表頁在查詢數據經常遇到,為了避免一次性返回過多的數據影響接口性能,一般會對查詢接口做分頁處理。

在Mysql中分頁一般用的limit關鍵字:

  1. select id,name,age from user limit 10,20; 

少量數據時,limit分頁沒啥問題。但如果表中數據量很多,就會出現性能問題。

比如分頁參數變成了:

  1. select id,name,age from user limit 1000000,20; 

Mysql會查到1000020條數據,然后丟棄前面的1000000條,只查后面的20條數據,非常浪費資源。

優化sql:

  1. select id,name,age from user where id > 1000000 limit 20; 

當然還可以使用between優化分頁:

  1. select id,name,age 
  2.  
  3. from user where id between 1000000 and 1000020; 

值得慶幸的是那張表的ID是自增的,于是用了id大于的條件,只差了最近的交易記錄,才勉強把數據查詢出來。

困難二:日志過多

由于系統日志打的比較詳細,一個項目每天大概幾個G的日志。要在這中間查詢到有用的日志,也是一個調整。

排查問題時,先使用了grep 命令找到出問題交易的賬號日志:

  1. grep 123 info.log 

當大概定位的到日志輸出時間了,再利用區間縮小日志范圍:

  1. grep '2021-11-17 19:23:23' info.log > temp.log 

這里同樣使用grep命令查找對應時間區間的日志,并將查找到日志輸出到temp.log文件中,然后通過sz命令,下載到本地進行篩選分析。

這里大家可以善用grep命令。同時也要善用輸出到新文件,這樣比每次查幾個G的內容方便多了。當然更方便的就是把篩選之后的日志下載本地,再次比對分析。

其他

關于代碼篩選這塊,沒有什么訣竅,除了從頭到位的捋一捋,沒有別的好方法。不過這個過程善用IDE的搜索和“Find usages”功能即可。

日終收獲

經過上述排查,最終在臨下班時,定位到了問題的原因:一個線程將余額更新之后,另外一個線程將其覆蓋了。在賬務流水記錄中存在了兩筆緊鄰,且計算前余額一樣的記錄。

得出結果之后,再排查其他的同類問題就方便多了,比如可采用group by來進行快速篩選:

  1. select count(id) as num , balance from account group by balance having num > 1; 

通過上述語句就可以快速查出有同樣計算前余額的記錄。當然,上述語句還可以添加條件和結果維度。

雖然找到的問題發生的地方,但并未完全找到問題的原因。

更深層次的Bug

本以為找到了問題發生的點,就能快速解決問題的,但的確小覷了這個Bug,又是一整天才排查出根本原因。

模擬高并發

找到出問題的代碼,看了實現邏輯,沒問題啊,也加了悲觀鎖,數據庫事務也沒失效,也沒有同Service的方法調用。怎么就會出現問題呢?

既然肉眼看不出來,那就用程序跑。于是,寫了一個單元測試,創建一個線程池,來調用對應加鎖方法。結果,依舊沒問題。

由于跑的是測試庫,生產庫用的是云服務,擔心是數據庫的差異,于是在Navicat驗證了悲觀鎖是否生效:

  1. START transaction ; 
  2.  
  3. select * from account where id = 1 for update

然后在另外一個查詢窗口執行:

  1. select * from account where id = 1 for update

發現,數據庫的鎖的確是生效的,在沒有執行commit操作之前,是查不到數據的。

僵局與希望

此時,完全陷入僵局。于是就開始大量搜索資料,多次閱讀代碼。

最終,在一篇寫得很水,但給了一個Hibernate javadoc文檔鏈接的文中,無意點了一下鏈接,獲得了巨大的啟發。

在javadoc看了一下session實現悲觀鎖的方法。項目中用了已經廢棄的get方法:

get

  1. @Deprecated 
  2. Object get(Class clazz, 
  3.                       Serializable id, 
  4.                       LockMode lockMode) 

**Deprecated.**LockMode parameter should be replaced with LockOptions

Return the persistent instance of the given entity class with the given identifier, or null if there is no such persistent instance. (If the instance is already associated with the session, return that instance. This method never returns an uninitialized instance.) Obtain the specified lock mode if the instance exists.

其中的“If the instance is already associated with the session, return that instance”讓我眼前一亮。難道是緩存在作祟?

上面的重點是:如果session中已經存在這么個對象實例,會直接返回這個實例。

感覺回去看代碼,還真是的,偽代碼如下:

  1. Account account = accountService.getAccount(type, userNo); 
  2. if(account == null){ 
  3.  //... 
  4. accountService.getAccountAndLock(account.getId()); 
  5. // ... 

上述代碼首先值得肯定的有兩點:第一,在加鎖之前先查了一次對象,這樣能避免因為對象不存在,鎖住全表;第二,就是鎖一條數據庫記錄時盡量采用id,精確定位到具體的記錄,避免鎖住其他記錄或整張表。

那么,是不是因為前面的查詢導致后面getAccountAndLock方法的實效呢?再來驗證一下。

于是,在單元測試中添加了前面的查詢,再次執行。哈哈,Bug終于復現了!

為了進一步證實,在底層的公共方法中添加了clear操作:

  1. public T findAndLock(Class cls, String primaryKey) throws DataAccessException { 
  2.  Session session = getHibernateTemplate().getSessionFactory().getCurrentSession(); 
  3.  // 添加驗證是否緩存問題 
  4.  session.clear(); 
  5.  Object object = session.load(cls, primaryKey, LockOptions.UPGRADE); 
  6.  return (T) object; 

再次執行單元測試,可正常加鎖。至此,Bug定位完畢。

問題的解決

既然已經定位問題,解決起來就非常方便了。上面使用session.clear()只是為了驗證,真實生產使用這種方法影響太大,而且是事后處理。

解決方案:將基于Hibernate的普通查詢,改為基于原生SQL的查詢。因為前面的普通查詢只需要id,那么只用一條SQL查詢ID即可,如果id為空,則不存在;如果id非空,則再進行下一步處理。

至此,問題完美解決。

小結

在解決上述問題的過程中,看似只是很簡單的悲觀鎖,但在排查的過程中還用到和涉及到了大量的其他知識,比如@Transactional事務失效場景的排查、事務的隔離級別、Hibernate的多級緩存、Spring的事物管理、多線程、Linux操作、Navicat手動事務、SQL優化、單元測試、Javadoc查閱等。 

所以,在解決問題之后,覺得十分有必要分享給大家。通過這個案例,你又學到了什么呢?

 

責任編輯:武曉燕 來源: 程序新視界
相關推薦

2016-02-15 09:52:21

虛擬現實

2009-02-19 20:25:34

SunSolaris發展趨勢

2018-12-18 09:20:06

2022-07-11 12:37:15

安全運營網絡攻擊

2013-01-06 13:45:14

2015-03-25 17:57:50

JavaJava糟糕

2021-03-18 08:08:16

FedoraLogoFedora 社區

2009-02-17 09:11:42

Unix時間錯誤

2023-08-28 08:24:07

myloaderMySQLGreatSQL

2021-06-15 16:17:19

Commit報錯事務

2019-10-24 08:25:44

IPv6互聯網網絡協議

2017-01-17 10:41:19

聯想企業網盤

2011-09-23 09:42:25

2020-12-14 09:35:20

CentOSRockyLinux

2020-03-24 09:00:32

企業征信信用風險益博睿

2023-05-18 15:00:06

2020-11-04 10:33:19

數據

2022-05-06 08:26:21

babel編譯器

2019-12-19 16:46:50

數據恢復軟件云計算技術

2013-05-06 09:19:36

云應用趨勢云服務云管理工具
點贊
收藏

51CTO技術棧公眾號

中文字幕亚洲精品一区| 中文字幕av一区二区三区人妻少妇 | 日韩欧美黄色大片| 欧美精品电影| 成人黄页毛片网站| 国产成人精品国内自产拍免费看 | 精品一二三区| 欧美一级黄色大片| 六月丁香婷婷在线| 亚洲资源一区| 国产欧美1区2区3区| 91黄在线观看| 男人天堂视频在线| 欧美精品91| 亚洲视频在线观看| 国产香蕉精品视频| 在线观看欧美| 在线观看欧美黄色| 人妻av中文系列| 精品美女在线观看视频在线观看| 99久久精品国产网站| 91精品久久久久久综合乱菊| 久久国产精品免费看| 亚洲一区二区三区| 一区二区三区国产视频| 久久久久9999| jizz性欧美23| 欧美一区永久视频免费观看| 日韩精品一区二区三区不卡| 黄色在线观看视频网站| ●精品国产综合乱码久久久久| 精品日本一区二区三区在线观看| 99久久精品国产色欲| 日本欧美一区二区| 国内揄拍国内精品| 欧美精品videos极品| 欧美成人自拍| 深夜福利91大全| 亚洲自拍偷拍图| 亚洲国产精品嫩草影院久久av| 欧美mv日韩mv国产网站app| 亚洲三级视频网站| 欧美影视资讯| 在线中文字幕不卡| 欧美三级午夜理伦三级| 97超碰免费在线| 亚洲一区二区三区中文字幕| 日本美女爱爱视频| 黄色动漫在线观看| 亚洲天堂久久久久久久| 伊人久久青草| 黄色国产网站在线播放| 亚洲免费av在线| 免费看污污视频| 影音先锋在线播放| 一区二区三区不卡视频| 精品视频在线观看一区二区| 欧美一卡二卡| 午夜视频一区二区三区| 欧美深夜福利视频| 亚洲天堂资源| 91成人在线观看喷潮| 亚洲综合在线网站| 六九午夜精品视频| 欧美一区国产二区| 激情综合激情五月| 日韩高清三区| 国产一区二区日韩| 久久噜噜色综合一区二区| 亚洲啊v在线观看| 欧美精品免费在线观看| 久久婷婷一区二区| 在线综合欧美| 国产精品xxx视频| 中文字幕在线2019| 国产乱码精品一区二区三| 春色成人在线视频| 天天干,夜夜爽| 国产日韩欧美精品电影三级在线| 亚洲视频小说| 国产精品一 二 三| 日韩一级在线观看| 免费拍拍拍网站| 精品捆绑调教一区二区三区| 精品久久久一区二区| 午夜精品久久久内射近拍高清 | 亚洲色图在线播放| 久久久久久久香蕉| 亚洲精品成人图区| 亚洲444eee在线观看| 亚洲女人天堂成人av在线| 久久久久久久久久码影片| 女人天堂在线| ...av二区三区久久精品| 精品国产乱码久久久久久闺蜜 | 亚洲欧美日韩天堂| 影音先锋久久| 亚洲国产日韩一区无码精品久久久| 男人久久天堂| 91福利在线观看| 亚洲综合123| 成人资源在线播放| 亚洲香蕉成视频在线观看| www欧美com| 99xxxx成人网| 成人妇女淫片aaaa视频| 日本高清视频在线| 国产精品国产精品国产专区不蜜 | 香蕉网在线播放| 97精品在线| 欧美在线精品免播放器视频| 99国产精品一区二区三区| 久久综合狠狠综合久久综合88| 一区二区三区四区欧美日韩| 国内激情视频在线观看| 色国产精品一区在线观看| 欧美性猛交乱大交| 日韩在线观看| 日本亚洲欧洲色α| 国精产品一品二品国精品69xx| 中文字幕免费观看一区| 女人和拘做爰正片视频| 无码国模国产在线观看| 日韩在线观看你懂的| 欧美亚洲精品天堂| 国产成人自拍在线| 中国成人亚色综合网站| 性欧美超级视频| 亚洲福利视频久久| 免费毛片在线播放免费| 毛片不卡一区二区| 日本一区二区三不卡| 欧美gv在线| 精品盗摄一区二区三区| 一区二区视频免费看| 精品一区二区三区免费| 亚洲ai欧洲av| 久久久久久久| 亚洲精品视频网上网址在线观看 | 欧美二区三区91| 夜夜春很很躁夜夜躁| 噜噜噜在线观看免费视频日韩 | heyzo久久| 国产mv久久久| 神马久久精品| 亚洲一区电影777| 九九久久久久久| 日韩在线观看电影完整版高清免费悬疑悬疑| 538国产精品视频一区二区| 国产91绿帽单男绿奴| 亚洲最色的网站| 国产a级片视频| 在线欧美福利| 久精品国产欧美| 亚洲欧美小说色综合小说一区| 日韩高清免费观看| 亚洲成色www.777999| 午夜先锋成人动漫在线| 欧美在线性爱视频 | 一级成人免费视频| 国产精品国产三级国产专播品爱网 | av丝袜在线| 亚洲精品99999| 国产精品视频免费播放| 久久久久国产精品人| www.99av.com| 亚洲久久久久| 国产乱码一区| 不卡一二三区| 中文字幕在线观看日韩| 国产精品免费无遮挡| 亚洲精品乱码久久久久久黑人| 性xxxxxxxxx| 日韩视频二区| 日韩区国产区| 日本一区二区三区视频在线看| 久久久久久久一| 国产在线观看高清视频| 3d动漫精品啪啪1区2区免费| 黄色一级视频在线观看| 91免费观看在线| 五月天激情播播| 亚洲激情社区| 午夜视频久久久| 97品白浆高清久久久久久| 欧美中文字幕在线视频| 黄色在线论坛| 日韩精品免费在线视频| 一级全黄裸体免费视频| 亚洲一二三四区| 久久久久久久毛片| 懂色一区二区三区免费观看| 亚洲人成无码www久久久| 在线观看国产精品入口| 欧美连裤袜在线视频| 精品视频在线观看免费观看| 欧美亚洲成人精品| 大片免费在线观看| 亚洲人成在线观看网站高清| 精品久久久免费视频| 91福利在线观看| 国产精品99精品| 中文字幕日本乱码精品影院| 亚洲av网址在线| 国产麻豆视频精品| 成年人在线观看视频免费| 影音先锋中文字幕一区| 伊人久久大香线蕉成人综合网| 在线日本制服中文欧美| 97人人模人人爽人人喊38tv| 成人18视频在线观看| 韩国美女主播一区| 91在线中文| 中文字幕亚洲无线码a| 青青色在线视频| 日韩欧美在线不卡| 91麻豆国产视频| 在线观看日韩av先锋影音电影院| 日韩av在线播放观看| 一区二区在线观看免费视频播放| 妖精视频在线观看免费| 久久综合网色—综合色88| 国产chinese中国hdxxxx| 国产一区二区视频在线播放| 日韩精品你懂的| 久久精品日韩欧美| 国产精品后入内射日本在线观看| 欧美人成网站| xxxxxx在线观看| 亚洲国产精品成人| 一区在线电影| 天天射—综合中文网| 色噜噜色狠狠狠狠狠综合色一| 色婷婷av一区二区三区丝袜美腿| 国产美女精品久久久| 亚洲性视频在线| 97超级碰碰| 一区二区网站| 不卡视频一区二区| 伊人久久大香线蕉av超碰| 亚洲伊人久久大香线蕉av| 国产成人视屏| 91日韩久久| youjizz欧美| 国产午夜精品在线| 精品福利一区| 欧美精品v日韩精品v国产精品| 日韩欧美ww| 欧美日韩另类综合| 成人情趣视频| 一本色道久久99精品综合| 欧美电影三区| a级网站在线观看| 欧美日韩国产亚洲一区| 2018中文字幕第一页| 精品成人在线| 国产91在线视频观看| 久久xxxx精品视频| 成人性视频欧美一区二区三区| 日韩高清不卡一区二区| 日本不卡一区二区在线观看| 国产综合久久久久影院| 国产精久久久久| 91偷拍与自偷拍精品| 黄色aaa视频| 国产精品久久久久久户外露出| 成人在线观看高清| 亚洲一区欧美一区| 久久久久亚洲av成人毛片韩| 欧美中文字幕一区| 国产精品人人爽| 精品国免费一区二区三区| 污视频在线免费观看| 国产一区二区三区直播精品电影 | 欧美激情在线一区| 亚洲一二三四| 91九色综合久久| 精品亚洲自拍| 亚洲欧美日产图| 亚洲一级高清| 日本在线观看免费视频| 国产高清不卡二三区| 醉酒壮男gay强迫野外xx| 国产免费成人在线视频| 久久av高潮av无码av喷吹| 色综合天天综合给合国产| 在线观看国产精品入口男同| 欧美电影精品一区二区| 国产福利小视频在线观看| 欧美成人午夜激情在线| 在线成人av观看| 亚洲自拍小视频免费观看| 亚洲人成亚洲精品| 日本久久高清视频| 狂野欧美一区| 无码人妻一区二区三区精品视频| 久久亚洲影视婷婷| 九九免费精品视频| 欧美在线观看视频在线| 嫩草影院一区二区| 久久九九热免费视频| 欧美黑人一区| 肥熟一91porny丨九色丨| 成人激情开心网| 国产成人无码精品久久久性色| 久久精品国产精品亚洲精品| 亚洲の无码国产の无码步美| 中文字幕一区免费在线观看| 综合激情网五月| 日韩午夜激情电影| 中文字幕在线免费| 欧美在线性爱视频| 黄色美女久久久| 日本在线视频www色| 日韩成人伦理电影在线观看| 国产极品一区二区| 亚洲综合色自拍一区| 午夜一区二区三区四区| 日韩高清中文字幕| 久草成色在线| 91亚洲人电影| 日韩免费av| 手机在线免费观看毛片| 2020国产精品久久精品美国| 国产在线观看免费av| 91麻豆精品国产91久久久使用方法 | 亚洲天堂2020| 2022成人影院| 久久伦理网站| 中文精品在线| 日韩少妇一区二区| 一区二区三区中文字幕| 国产免费不卡视频| 久久精品中文字幕| 日韩久久一区| 永久域名在线精品| 蜜臀91精品一区二区三区| 国产又粗又猛又爽又黄av| 色爱区综合激月婷婷| 免费看男男www网站入口在线 | 一区二区三区动漫| 成人在线爆射| 欧美主播一区二区三区美女 久久精品人 | 国产精品素人一区二区| 一区二区三区在线观看av| 日韩大陆毛片av| 中文字幕乱码在线播放| 久久av一区二区| 一本色道88久久加勒比精品| 欧美xxxxx少妇| 午夜精品爽啪视频| 同心难改在线观看| 国产91精品最新在线播放| 国产99亚洲| 中文字幕av专区| 亚洲免费观看高清在线观看| 性欧美18一19性猛交| 久久久女女女女999久久| 欧美高清视频看片在线观看| 国产高清精品在线观看| 久久久久国产精品麻豆ai换脸| 99re国产在线| 精品国产一区久久久| 亚洲精品不卡在线观看| 国产69精品久久久久久久| 久久精品日产第一区二区三区高清版 | 青青草精品视频| 九九精品视频免费| 欧美岛国在线观看| 国产精品迅雷| 久久精品国产精品亚洲精品色| 国产成人在线免费观看| 国产黄色片免费看| 最新日韩中文字幕| 在线视频亚洲欧美中文| 国产免费一区二区三区视频| 国产欧美日本一区二区三区| 国产精品女同一区二区| 韩国福利视频一区| 欧美视频免费| 一区二区三区四区影院| 日韩欧美在线视频观看| 麻豆视频在线| 豆国产97在线| 麻豆视频一区二区| 国产精品第72页| 少妇高潮 亚洲精品| 久久视频在线观看| 久久这里只精品| 亚洲成人av在线电影| av影片在线看| 国产视频99| 激情六月婷婷综合| 国产成人在线免费视频| 久久国产精品久久精品| 免费成人av| 四虎国产精品免费| 欧美中文字幕一区二区三区亚洲| 福利在线导航136| 中日韩在线视频|