Y 維度就是從業務 HTTP 請求的橫向處理流程來看,HTTP 請求會穿越網路、電腦、應用容器(Tomcat)、Spring、ORM(Hibernate)、資料庫等節點,在這個流程中每個節點都有許多可以可優化的地方,今天老兵哥將介紹通過優化開發框架 Spring 來優化系統性能的方法。 ...
性能調優系列前序文章索引:
- 程式員必須掌握的性能調優:老兵哥結合個人經歷解釋了程式員往架構師方向發展時為什麼要跨越性能調優這一關,以及介紹了從 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)