性能調優系列前序文章索引:
- 程式員必須掌握的性能調優:老兵哥結合個人經歷解釋了程式員往架構師方向發展時為什么要跨越性能調優這一關,以及介紹了從 X、Y、Z 三個維度優化性能的思路,
- 從 X 維度優化系統的性能:老兵哥分享了從 X 維度優化系統性能的思路,包括讓客戶端分計算存盤任務、優化互動設計等,主要是作為引子拓寬我們性能調優的思路,
- 應用容器 Tomcat 性能調優:Y 維度就是從業務 HTTP 請求的橫向處理流程來看,HTTP 請求會穿越網路、計算機、應用容器(Tomcat)、Spring、ORM(Hibernate)、資料庫等節點,在這個流程中每個節點都有許多可以可優化的地方,此文主要介紹通過優化應用容器(Tomcat)來優化系統性能的方法,
程式員在轉型架構師的程序中需要建立流程化、結構化、系統化的思維方式,而性能調優是非常難得的貧訓,它既給了我們壓力,也給了我們動力,跨越它就是突破自己的程序,
- X 維度,即業務維度,技術始終是服務業務的,任何技術問題的原點就是業務需求,在啟動技術層面的性能優化之前,我們有必要先審視一下業務流程是否合理,互動設計上有沒有可以優化的空間等,
- Y 維度,待業務維度優化完畢,接下來就是審視技術在實作當前業務流程或互動設計的全鏈路上有沒有可優化的地方,即 HTTP 請求處理全流程,從瀏覽器到應用容器,再到 Spring、Hibernate、資料庫等,
- Z 維度,除了沿著 HTTP 請求的橫向鏈路,我們還要審視支持應用系統的縱向技術堆疊,從上到下包括 JVM、作業系統和硬體等,這是整套應用系統運行的環境,許多性能問題都跟運行環境存在關系,

今天老兵哥將介紹通過優化開發框架 Spring 來優化系統性能的方法,
3. 開發框架 Spring
3.1 事務管理
事務(Transaction),是并發控制的基本單位,是用戶定義的一個操作序列,這些操作要么都做,要么都不做,是一個不可分割的作業單位,通過使用事務控制,我們可以極大地避免邏輯處理失敗導致的臟資料等問題,事務具有 4 個屬性:原子性、一致性、隔離性、持久性等,這四個屬性通常稱為 ACID 特性,
- 原子性(Atomicity),一個事務是一個不可分割的作業單位,事務包含的操作要么都做、要么都不做,
- 一致性(Consistency),事務必須讓資料庫從一個一致性狀態變到另一個一致性狀態,不能出現不一致,
- 隔離性(Isolation),一個事務的執行不能被其他事務干擾,即一個事務內部的操作及使用的資料對并發的其他事務是隔離的,并發執行的各個事務之間不能互相干擾,
- 持久性(Durability),持久性也稱永久性,指一個事務一旦提交,它對資料庫中資料的改變是永久性的,接下來的其他操作或故障不應該對其有任何影響,

Spring 事務管理是通過 XML 檔案或注解 @Transactional 配置的,其背后是靜態代理或動態代理等技術,在代理模式下,那些從代理傳遞傳過來的“外部”方法呼叫會被攔截,但“自我呼叫”是不會觸發事務的,例如,在目標物件中呼叫自身其他方法的方法是不會觸發事務的,即使被呼叫的方法標記為 @Transactional,

通常,我們很少關注 Spring 事務管理相關的屬性,但這些屬性的取值會影響系統的性能,Spring 事務管理最重要的兩個特性是:傳播級別、隔離級別,傳播級別,定義了事務的控制范圍;隔離級別,定義了事務在資料庫讀寫方面的控制范圍,我們知道,事務的控制范圍越大,系統的并發性就會越差,性能也就隨之降低,事務的隔離級別越高,系統的并發性也會越差,性能也會隨之下降,如果不了解這些屬性的取值規則,我們就不能選擇最合適的取值,不知不覺中就會浪費許多系統資源,接下來我們一起來看看這些屬性,
| 屬性 | 型別 | 描述 |
|---|---|---|
| propagation | 列舉型:Propagation | 傳播級別,可選,默認值:PROPAGATION_REQUIRED |
| isolation | 列舉型:Isolation | 隔離級別,可選,默認值:ISOLATION_DEFAULT |
| readOnly | 布爾型 | 讀寫型事務、只讀型事務 |
| timeout | INT 型,以秒為單位 | 事務超時閾值 |
| rollbackFor | 一組 Class 類,必須是 Throwable 的子類 | 一組例外類,遇到時必須回滾,默認情況下 Checked Exceptions 不進行回滾,僅 Unchecked Exceptions(即 RuntimeException 的子類)才進行事務回滾, |
| rollbackForClassname | 一組 Class 類的名字,必須是 Throwable 的子類 | 一組例外類名,遇到時必須回滾 |
| noRollbackFor | 一組 Class 類,必須是 Throwable 的子類 | 一組例外類,遇到時不需要回滾 |
Spring 事務管理的傳播級別 Propagation 取值有以下幾種:
| 傳播級別 | 說明 | 備注 |
|---|---|---|
| PROPAGATION_REQUIRED | 如果背景關系中已經存在事務,那么就加入到事務中執行;如果背景關系中不存在事務,則新建事務執行, | 這個級別通常能滿足處理大多數的業務場景, |
| PROPAGATION_SUPPORTS | 如果背景關系中已經存在事務,則支持加入到事務中執行;如果背景關系中不存在事務,則使用非事務的方式執行, | 這個通常是用來處理那些并非原子性的非核心業務邏輯操作,應用場景較少, |
| PROPAGATION_MANDATORY | 該級別的事務要求背景關系中必須要存在事務,否則就會拋出例外,這是避免背景關系呼叫代碼遺漏添加事務控制的保證手段, | 例如某段代碼不能被單獨呼叫執行,但是一旦被呼叫就必須要有事務包含,這種情況下就可以使用這個傳播級別, |
| PROPAGATION_REQUIRES_NEW | 每次都會新建一個事務,并且同時將背景關系中的事務掛起,執行當前新建事務完成以后,背景關系事務恢復再執行, | 問題1:如果某個子事務發生回滾,父事務是否回滾?答案是不會,因為子事務是新建事務,父事務已經被掛起,兩者不會受到影響,問題2:如果父事務發生回滾,子事務是否回滾?答案是不會,同樣的理由,但是可以手動控制,一旦子事務回滾,父事務也回滾, |
| PROPAGATION_NOT_SUPPORTED | 如果背景關系中已經存在事務,則掛起事務,執行當前邏輯,結束后恢復背景關系的事務, | 這個級別可以幫助你盡可能地縮小事務范圍,一個事務范圍越大,它存在的風險也就越多,例如某段代碼是回圈 1000 次的非核心業務邏輯操作,此類代碼如果包在事務中,勢必導致事務太大,很容易出現些難以考慮周全的例外情況,此時這個級別就派上用場了, |
| PROPAGATION_NEVER | 該級別要求背景關系中不能存在事務,一旦有事務,就拋出runtime例外,強制停止執行, | 無 |
傳播級別 PROPAGATION_REQUIRED 會為每一個被應用到的方法創建一個邏輯事務作用域,每一個邏輯事務作用域都可以自主地決定回滾條件,當這樣的邏輯事務作用域被外部邏輯事務作用域所包含時,它們在邏輯上是獨立的,但在實作層面它們會被映射到相同的物理事務上,

傳播級別 PROPAGATION_REQUIRES_NEW 為每一個相關的事務作用域使用了一個完全獨立的事務,在這種情況下,物理事務也將是不同的,因此,外部事務可以不受內部事務回滾狀態的影響獨立提交或者回滾,

Spring 事務管理的隔離級別 Isolation 取值有以下幾種:
| 隔離級別 | 說明 |
|---|---|
| Serializable | 最嚴格的級別,事務串行執行,資源消耗最大, |
| Repeatable Read | 保證了一個事務不會修改已經由另一個事務讀取但未提交(或回滾)的資料,避免了“臟讀取”和“不可重復讀取”的情況,但會帶來了更多的性能損耗, |
| Read Committed | 大多數主流資料庫的默認事務等級,保證了一個事務不會讀到另一個并行事務已修改但未提交的資料,避免了“臟讀取”,該級別適用于大多數系統, |
| Read Uncommitted | 保證了讀取程序中不會讀取到非法資料, |
上述說明中涉及的幾個專業術語:
- 臟讀(Dirty Reads):就是讀到了別的事務回滾前的臟資料,例如,事務 B 執行程序中修改了資料 X,在未提交前,事務 A 讀取了 X,而事務 B 卻回滾了,這樣事務 A 就形成了臟讀,
- 不可重復讀(Non-Repeatable Reads):不可重復讀字面含義已經很明確了,例如,事務 A 首先讀取了一條資料,然后執行邏輯的時候,事務 B 將這條資料改變了,然后事務 A 再次讀取的時候,發現資料不匹配了,這就是所謂的不可重復讀,
- 幻讀(Phantom Reads):我們小時候數鴨子,第一次數是 10 個,第二次數是 11 個,怎么回事,產生幻覺了?幻讀也是這樣子,事務 A 先根據條件索引到 10 條資料,然后事務 B 改變了資料庫一條資料,導致也符合事務 A 的搜索條件,這樣事務 A 再次搜索發現有 11 條資料了,這就產生了幻讀,
| 隔離級別與副作用 | 臟讀 | 不可重復讀 | 幻讀 |
|---|---|---|---|
| Serializable | 不會 | 不會 | 不會 |
| Repeatable Read | 不會 | 不會 | 會 |
| Read Committed | 不會 | 會 | 會 |
| Read Uncommitted | 會 | 會 | 會 |
從上面這張映射表中,我們知道最安全的是 Serializable,但是伴隨而來的是高昂的性能開銷,各種傳播級別、隔離級別本身沒有好壞,關鍵是根據業務需求選擇最合適的取值,避免無效的性能損耗,另外,Spring 事務管理還有兩個常用屬性,它們的取值也會影響性能:
- Readonly:只讀型事務要比讀寫型事務的性能更好,設定事務為只讀以提升性能,
- Timeout:設定事務的超時時間,一般用于防止大事務的發生,事務要盡可能的小,
3.2 二級快取
快取作為提高應用系統性能的一種有效途徑,在事務管理配置不當的情況下,將很難發揮應有的效用,因此,在做快取處理或者其他處理,要考慮事務管理對性能的影響,
關注「 IT老兵哥 」,賦能程式人生!堅持原創不易,請小伙伴們不吝點個「 贊 」哦!推薦軟技能文章,請點擊鏈接:程式員,怎樣打造個人影響力?

近期熱評系列《 程式員必須懂的架構師入門課 》:
- 架構到底是什么,你知道嗎? (閱讀人數:1201)
- 架構都有哪些,我該怎么選? (閱讀人數:886)
- 架構師都干什么,你知道嗎? (閱讀人數:1178)
- 練就哪些技能才勝任架構師? (閱讀人數:1145)
- 怎樣才能搞定上下游的客戶? (閱讀人數:492)
- 如何從開發崗轉型做架構師? (閱讀人數:1288)
- 程式員必須懂的架構入門課 (閱讀人數:611)
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/19539.html
標籤:架構設計
下一篇:實戰_Spring_Cloud
