又一批长事务,P0故障谁来背锅?
原創(chuàng):小姐姐味道(微信公眾號ID:xjjdog),歡迎分享,轉(zhuǎn)載請保留出處。
最近幾周,發(fā)生過多起因為事務問題引起的服務報錯。現(xiàn)象為數(shù)據(jù)庫連接池連接占滿,數(shù)據(jù)庫連接長時間等待,最終導致請求線程hang住,服務大面積報錯。這個時候,服務資源、數(shù)據(jù)庫資源大量空閑,但就是進行不下去,影響是比較惡劣的。
誰來背鍋?當然是架構(gòu)師。因為這次所有的服務都活著,沒運維什么事。
面試時,大家可能都會碰到關于事務相關的問題,升級版的可能是分布式事務的問題。在互聯(lián)網(wǎng)行業(yè)中,一句馬馬虎虎的補償事務就能蒙混過關,畢竟都是些短小精悍的接口。
但在很多企業(yè)級應用中,這行不通。我們必須直面慘淡的現(xiàn)實。
為什么要用長事務?
在許多業(yè)務非常復雜的后臺系統(tǒng),經(jīng)常頻繁操作DB,為了保證數(shù)據(jù)的一致性,能夠在出錯時回滾數(shù)據(jù),通常會使用事務。
就拿最簡單的單機數(shù)據(jù)庫事務來說。
在事務操作期間,如果持續(xù)時間過長,只有等事務結(jié)束之后,DB連接才會釋放,此類長時間占用DB連接的事務操作,稱為長事務。一旦外部有大量請求,并發(fā)調(diào)用此操作,那么將會有大量的DB連接被持有而沒有被釋放掉,直到連接池爆滿。
這個時候,如果有其他請求到來,那十有八九是以失敗告終。
也就是說,連接資源被少數(shù)長事務操作占用。在這種情況下,即使是最簡單接口查詢,都不能夠正常進行。
幾粒老鼠屎,壞了一鍋粥。
一些魔幻的反應
當你去排查這種問題的時候,可能會陷入僵局。jstack顯示,多數(shù)請求其實是阻塞在tomcat的線程池上,而且是一些訪問速度非常快的請求被阻塞。
比如,tomcat的200個線程,有180個阻塞在耗時不到1ms的/status接口上。
很多人就一臉懵逼。經(jīng)驗失靈。
jstack此時的輸出結(jié)果,欺騙了我們。真正造成阻塞的,是那額外的20多個線程。
有哪些改善?
保證事務的短小是一個基本要求,包括但不限于:
應控制慢查詢的調(diào)用頻率,盡量減少慢查詢。很多情況下,這條規(guī)則是自欺欺人的,需要業(yè)務做一些妥協(xié)。
事務內(nèi)不應包含任何RPC調(diào)用,減少事務的粒度。通常,一些RPC調(diào)用,包括其他非事務資源的調(diào)用,耗時非常不可控。如果把它們也納入事務的范圍之內(nèi),勢必會加劇資源的占用。事務內(nèi)不應包含其他容易超時或者長時間阻塞的服務,如HTTP調(diào)用、IO操作。
次優(yōu)先級服務如消息隊列,不應該放在事務內(nèi),避免因為消息隊列不可用引起的服務不可用。給類似消息隊列的組件,設置一個合理的超時時間的非常有必要的,否則它就會一直等在那里。但即使是這樣,也盡量不要把它們納入到事務操作之內(nèi)。
跨庫、跨類型(如Redis),不應該放在同一事務中,可避免交叉影響。
你可以看到上面的這些描述,有些和我們所追求的數(shù)據(jù)一致性是相悖的。這不奇怪,依然是CAP原理的權(quán)衡。有些業(yè)務選擇的是寧可卡死不再響應,也不能進入異常數(shù)據(jù);有些則首先讓業(yè)務運行下去,臟數(shù)據(jù)會通過補償事務進行修正。
一切看你的選擇。
設計總有人背鍋,補償總有人做出犧牲。
解決方式
那么如何來快速解決大事務造成的服務不可用問題呢?
除了擴容,其實是無解。重啟大法也不見得好用。因為被阻斷的請求,會以更兇猛的態(tài)勢再次來襲。
你可能會想到調(diào)大連接池的大小。但在實踐中得知,也不好用,大事務請求會迅速將連接池占滿。
但我們可以提前進行防御。
以Spring為例,事務的使用方式大多數(shù)是使用@Transactional注解來控制的,或者是聲明式事務方式。我建議以以下方式進行預防和發(fā)現(xiàn):
1) 重新掃描或者Review業(yè)務代碼,排查事務中是否有以上提到的各種情況。然后將除DB操作外的其他操作移動到事務之外。
2) 每個事務操作都給予足夠重視,對于執(zhí)行復雜度和時間復雜度不確定的事務,添加超時報警,及時發(fā)現(xiàn)引起的原因。
同時,還需要加強監(jiān)控,輔助進行問題排查。
1) ?業(yè)務可以考慮定時將數(shù)據(jù)庫連接池的信息進行打印,通過看日志的方式進行初步排查。
2) 使用jstack查詢執(zhí)行棧,找出阻塞的點。
3) 排查并聯(lián)系下游服務,找出主要原因
xjjdog傾向于使用監(jiān)控快速發(fā)現(xiàn)問題。如圖,通過連接池監(jiān)控,可以看到數(shù)據(jù)庫連接池連接數(shù)長時間保持在高位不釋放,同時等待的線程數(shù)急劇增加。發(fā)生此種現(xiàn)象多數(shù)可以考慮是否是以上原因引起。
發(fā)生問題時,應及時(多次)使用jstack定位到線程的阻塞位置,然后排查下游服務是否有問題,或者是否存在慢查詢。
最好的情況是服務已經(jīng)進行了對代碼的梳理,那么引起的原因大概率只剩下了慢查詢。針對慢查詢,druid數(shù)據(jù)庫連接池,提供了sql的聚合,能夠查看是每一類查詢語句的具體執(zhí)行情況。如圖,短時間內(nèi)SQL請求飆升,最大執(zhí)行時長上升,連接池占滿:
具體是哪一句SQL所引起的,一目了然。
End
長事務問題的危險級別屬于高危型,通常會造成嚴重的后果,可以通過觀察監(jiān)控,防范于未然。
最優(yōu)的解決方式,當然是業(yè)務模型的改進。但這東西第一涉及到開發(fā)成本,第二涉及到跨部門協(xié)作。
出錢的老板,無法聽懂你這些夢話。
在一些公司內(nèi)部,這兩者都是讓人抓狂的事情,還不如痛痛快快背個鍋,來得實在。
作者簡介:小姐姐味道??(xjjdog),一個不允許程序員走彎路的公眾號。聚焦基礎架構(gòu)和Linux。十年架構(gòu),日百億流量,與你探討高并發(fā)世界,給你不一樣的味道。
有道無術(shù),術(shù)可成;有術(shù)無道,止于術(shù)
歡迎大家關注Java之道公眾號
好文章,我在看??
總結(jié)
以上是生活随笔為你收集整理的又一批长事务,P0故障谁来背锅?的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Adnroid提高效率之资源文件改名
- 下一篇: 25岁之后,你更应该逼自己系统性成长