背景
- 在將xxl-job-admin部署到正式環(huán)境后,發(fā)現(xiàn)存在重復(fù)調(diào)度的問題。系統(tǒng)部署在k8s中,共起了3個(gè)pods,后端存儲(chǔ)為TIDB。
- 發(fā)現(xiàn)問題后,當(dāng)即降pods的副本數(shù)降到1,可見重復(fù)調(diào)度問題消除。
- 打開xxl-job-admin的日志問題可以看到一直在刷Write conflict xxx等報(bào)錯(cuò)日志。
排查過程
?開源項(xiàng)目遇到問題第一步,先上GitHub上搜issue。果不其然被我搜索到一模一樣的問題。(ISSUE)。根據(jù)issues的描述,這個(gè)錯(cuò)誤可以與TIDB有關(guān)。結(jié)合自己使用的存儲(chǔ)底層,著手向這方面進(jìn)行了排查。

?接下來讓我們再次回看下xxl-job-admin中競爭調(diào)度的代碼,從下面的圖片中可以看出流程主要分為這么幾個(gè)部分
- (1)先通過jdbc獲取一個(gè)數(shù)據(jù)庫連接。
- (2)將事務(wù)自動(dòng)commit關(guān)掉。
- (3)通過select for update的方式來鎖住一行(排它鎖)。
- (4)執(zhí)行事務(wù)邏輯。
- (5)事務(wù)執(zhí)行完畢手動(dòng)提交事務(wù)。

?通過查詢TIDB的相關(guān)文檔,發(fā)現(xiàn)在TIDB版本<= v3.0.8之前使用的樂觀事務(wù)模型,也就是說不會(huì)進(jìn)行鎖等待,只會(huì)在事務(wù)提交沖突的時(shí)候進(jìn)行報(bào)錯(cuò)(Write conflict)。再結(jié)合文檔中描述,發(fā)現(xiàn)有兩處可以需要確認(rèn)的地方
- (1)使用的TIDB版本<= v3.0.8?不是,我使用的是4.0.6版本。當(dāng)文檔也提這么一句話:只有新創(chuàng)建的集群才會(huì)默認(rèn)使用悲觀事務(wù)模式,從舊版本升級上來的并不會(huì)修改事務(wù)類型
- (2)是否在事務(wù)中使用了自動(dòng)提交?從上面的代碼上看,在獲取JDBC的時(shí)候有顯式的關(guān)閉的事務(wù)提交。


?于是問題就轉(zhuǎn)行成確認(rèn)我當(dāng)前使用的TIDB是不是從低版本升級上來的,或者確認(rèn)當(dāng)前默認(rèn)使用的是不是悲觀事務(wù)模型。經(jīng)過詢問TIDB同事,告訴我當(dāng)前就是默認(rèn)悲觀事務(wù),但是我不信。
?既然不信,那Talk is cheap. Show me the code,手動(dòng)寫兩個(gè)main方法來測試下吧!就認(rèn)準(zhǔn)一條準(zhǔn)則,已經(jīng)是悲觀事務(wù)模式的話,那么tidb執(zhí)行for update的結(jié)果應(yīng)該跟mysql的一樣(鎖等待)。假設(shè)表kantlin中有(2,1)這行數(shù)據(jù),事務(wù)執(zhí)行時(shí)序大概如下:

?MySQL的結(jié)果是在Tx1和Tx2都可以成功提交, t7時(shí)刻, select執(zhí)行結(jié)果為b=22,且但Tx2從t3時(shí)刻開始,會(huì)被阻塞,一直到t6時(shí)刻, Tx1完成提交后, Tx2才能提交,所有在mysql中執(zhí)行select for update排它鎖語句是會(huì)等待鎖的,沒問題。
?但是TiDB中, Tx1提交會(huì)失敗(WriteConflict),到了t7時(shí)刻, select執(zhí)行結(jié)果為b=21,所以證明當(dāng)前的還是處于樂觀事務(wù)模式。
?有了這些證據(jù)后,再次請TIDB同事確認(rèn),TIDB同事經(jīng)過一番排除后發(fā)現(xiàn)確實(shí)是從舊版本升級上來的,應(yīng)該沒有改默認(rèn)的事務(wù)類型,并幫我手動(dòng)的指定事務(wù)級別為悲觀事務(wù)模式。我接著再對系統(tǒng)增加pods副本,發(fā)現(xiàn)沒有重復(fù)調(diào)度的問題,日志打印也正常了。
其它思考
使用樂觀鎖事務(wù)模型優(yōu)缺點(diǎn)是什么?
?TiDB 使用 Percolator 事務(wù)模型, 沖突檢測只在事務(wù)提交時(shí)才觸發(fā), 而MySQL則是通過鎖等待機(jī)制(如SELECT … FOR UPDATE)解決沖突問題.TiDB這樣設(shè)計(jì)有好有壞, 在沖突小的情況下,由于沒有鎖等待,系統(tǒng)的并發(fā)性能更好. 但在沖突嚴(yán)重的情況下, 會(huì)造成事務(wù)失敗增多,影響并發(fā)性能。
如何使用TIDB?
?作為開發(fā),當(dāng)前正從mysql向tidb切換過度,總是很直接就認(rèn)為當(dāng)mysql來用就可以了,而沒有過多的研究。從這次小小的掉坑,也學(xué)到不少tidb的知識。作為DBA的同事,他們確實(shí)是沒有責(zé)任幫你升級事務(wù)模型,那么就全靠開發(fā)把握了,還好是很容易在測試期間就發(fā)現(xiàn)的問題,但是想象下如何是在生產(chǎn)發(fā)現(xiàn),并且涉及到跟錢有關(guān)的批扣,那問題就大了。生產(chǎn)無小事!
其它的方式實(shí)現(xiàn)鎖?
?如果你手頭上真的只有一個(gè)<= v3.0.8版本的TIDB,但是又想多實(shí)例部署xxl-job-admin的話,那么其實(shí)基于DB實(shí)現(xiàn)分布式鎖主要有三種,xxl-jobs使用的是排它鎖,另外的唯一索引鎖或CAS樂觀鎖也都可以實(shí)現(xiàn)的,保險(xiǎn)點(diǎn)再弄個(gè)監(jiān)控線程清理長期占用的鎖就可以了。