在平常開發中,尤爲是業務開發,少不了利用 Java 對數據庫進行基本的增刪改查等數據操做,
這也是 Java 工程師的必備技能之一。作好數據操做,不只僅須要對 Java 語言相關框架的掌
握,更須要對各類數據庫自身體系結構的理解。今天這一講,做爲補充 Java 面試考察知識點的
完整性,關於數據庫的應用和細節還須要在實踐中深刻學習。
今天我要問你的問題是,談談 MySQL 支持的事務隔離級別,以及悲觀鎖和樂觀鎖的原理和應用
場景?
典型回答
所謂隔離級別(Isolation Level),就是在數據庫事務中,爲保證併發數據讀寫的正確性而提出
的定義,它並非 MySQL 專有的概念,而是源於ANSI/ISO制定的SQL-92標準。
每種關係型數據庫都提供了各自特點的隔離級別實現,雖然在一般的定義中是以鎖爲實現單元,
但實際的實現千差萬別。以最多見的 MySQL InnoDB 引擎爲例,它是基於 MVCC(Multi-
2018/8/1 極客時間 | Java核心技術36講
https://time.geekbang.org/column/article/12288 2/7
Versioning Concurrency Control)和鎖的複合實現,按照隔離程度從低到高,MySQL 事務隔
離級別分爲四個不一樣層次:
讀未提交(Read uncommitted),就是一個事務可以看到其餘事務還沒有提交的修改,這是
最低的隔離水平,容許髒讀出現。
讀已提交(Read committed),事務可以看到的數據都是其餘事務已經提交的修改,也就
是保證不會看到任何中間性狀態,固然髒讀也不會出現。讀已提交仍然是比較低級別的隔
離,並不保證再次讀取時可以獲取一樣的數據,也就是容許其餘事務併發修改數據,容許不
可重複讀和幻象讀(Phantom Read)出現。
可重複讀(Repeatable reads),保證同一個事務中屢次讀取的數據是一致的,這是
MySQL InnoDB 引擎的默認隔離級別,可是和一些其餘數據庫實現不一樣的是,能夠簡單認爲
MySQL 在可重複讀級別不會出現幻象讀。
串行化(Serializable),併發事務之間是串行化的,一般意味着讀取須要獲取共享讀鎖,更
新須要獲取排他寫鎖,若是 SQL 使用 WHERE 語句,還會獲取區間鎖(MySQL 以 GAP 鎖
形式實現,可重複讀級別中默認也會使用),這是最高的隔離級別。
至於悲觀鎖和樂觀鎖,也並非 MySQL 或者數據庫中獨有的概念,而是併發編程的基本概念。
主要區別在於,操做共享數據時,「悲觀鎖」即認爲數據出現衝突的可能性更大,而「樂觀
鎖」則是認爲大部分狀況不會出現衝突,進而決定是否採起排他性措施。
反映到 MySQL 數據庫應用開發中,悲觀鎖通常就是利用相似 SELECT … FOR UPDATE 這樣的
語句,對數據加鎖,避免其餘事務意外修改數據。樂觀鎖則與 Java 併發包中的
AtomicFieldUpdater 相似,也是利用 CAS 機制,並不會對數據加鎖,而是經過對比數據的時
間戳或者版本號,來實現樂觀鎖須要的版本判斷。
我認爲前面提到的 MVCC,其本質就能夠看做是種樂觀鎖機制,而排他性的讀寫鎖、雙階段鎖
等則是悲觀鎖的實現。
有關它們的應用場景,你能夠構建一下簡化的火車餘票查詢和購票系統。同時查詢的人可能很
多,雖然具體座位票只能是賣給一我的,但餘票可能不少,並且也並不能預測哪一個查詢者會購
票,這個時候就更適合用樂觀鎖。
考點分析
今天的問題來源於實際面試,這兩部分問題反映了面試官試圖考察面試者在平常應用開發中,是
否學習或者思考過數據庫內部的機制,是否瞭解併發相關的基礎概念和實踐。
我從普通數據庫應用開發者的角度,提供了一個相對簡化的答案,面試官頗有可能進一步從實例
的角度展開,例如設計一個典型場景重現髒讀、幻象讀,或者從數據庫設計的角度,能夠用哪些
2018/8/1 極客時間 | Java核心技術36講
https://time.geekbang.org/column/article/12288 3/7
手段避免相似狀況。我建議你在準備面試時,能夠在典型的數據庫上試驗一下,驗證本身的觀
點。
其餘能夠考察的點也有不少,在準備這個問題時你也能夠對比 Java 語言的併發機制,進行深刻
理解,例如,隨着隔離級別從低到高,競爭性(Contention)逐漸加強,隨之而來的代價一樣
是性能和擴展性的降低。
數據庫衍生出不少不一樣的職責方向:
數據庫管理員(DBA),這是一個單獨的專業領域。
數據庫應用工程師,不少業務開發者就是這種定位,綜合利用數據庫和其餘編程語言等技
能,開發業務應用。
數據庫工程師,更加側重於開發數據庫、數據庫中間件等基礎軟件。
後面二者與 Java 開發更加相關,可是須要的知識和技能是不一樣的,因此面試的考察角度也有區
別,今天我會分析下對相關知識學習和準備面試的見解。
另外,在數據庫相關領域,Java 工程師最常接觸到的就是 O/R Mapping 框架或者相似的數據
庫交互類庫,我會選取最普遍使用的框架進行對比和分析。
知識擴展
首先,我來談談對數據庫相關領域學習的見解,從最普遍的應用開發者角度,至少須要掌握:
數據庫設計基礎,包括數據庫設計中的幾個基本範式,各類數據庫的基礎概念,例如表、視
圖、索引、外鍵、序列號生成器等,清楚如何將現實中業務實體和其依賴關係映射到數據庫
結構中,掌握典型實體數據應該使用什麼樣的數據庫數據類型等。
每種數據庫的設計和實現多少會存在差別,因此至少要精通你使用過的數據庫的設計要點。
我今天開篇談到的 MySQL 事務隔離級別,就區別於其餘數據庫,進一步瞭解 MVCC、
Locking 等機制對於處理進階問題很是有幫助;還須要瞭解,不一樣索引類型的使用,甚至是
底層數據結構和算法等。
常見的 SQL 語句,掌握基礎的 SQL 調優技巧,至少要了解基本思路是怎樣的,例如 SQL 怎
樣寫才能更好利用索引、知道如何分析SQL 執行計劃等。
更進一步,至少須要瞭解針對高併發等特定場景中的解決方案,例如讀寫分離、分庫分表,
或者如何利用緩存機制等,目前的數據存儲也遠不止傳統的關係型數據庫了。
2018/8/1 極客時間 | Java核心技術36講
https://time.geekbang.org/column/article/12288 4/7
上面的示意圖簡單總結了我對數據庫領域的理解,但願能夠給你進行準備時提供個借鑑。固然在
準備面試時並非一味找一堆書悶頭苦讀,我仍是建議從實際工做中使用的數據庫出發,側重於
結合實踐,完善和深化本身的知識體系。
接下來咱們仍是回到 Java 自己,目前最爲通用的 Java 和數據庫交互技術就是 JDBC,最多見的
開源框架基本都是構建在 JDBC 之上,包括咱們熟悉的JPA/Hibernate、MyBatis、Spring
JDBC Template 等,各自都有獨特的設計特色。
Hibernate 是最負盛名的 O/R Mapping 框架之一,它也是一個 JPA Provider。顧名思義,它
是以對象爲中心的,其強項更體如今數據庫到 Java 對象的映射,能夠很方便地在 Java 對象層
面體現外鍵約束等相對複雜的關係,提供了強大的持久化功能。內部大量使用了Lazy-load等技
術提升效率。而且,爲了屏蔽數據庫的差別,下降維護開銷,Hibernate 提供了類 SQL 的
HQL,能夠自動生成某種數據庫特定的 SQL 語句。
Hibernate 應用很是普遍,可是過分強調持久化和隔離數據庫底層細節,也致使了不少弊端,例
如 HQL 須要額外的學習,未必比深刻學習 SQL 語言更高效;減弱程序員對 SQL 的直接控制,
還可能致使其餘代價,原本一句 SQL 的事情,可能被 Hibernate 生成幾條,隱藏的內部細節也
阻礙了進一步的優化。
而 MyBatis 雖然仍然提供了一些映射的功能,但更加以 SQL 爲中心,開發者能夠側重於 SQL
和存儲過程,很是簡單、直接。若是咱們的應用須要大量高性能的或者複雜的 SELECT 語句
等,「半自動」的 MyBatis 就會比 Hibernate 更加實用。
2018/8/1 極客時間 | Java核心技術36講
https://time.geekbang.org/column/article/12288 5/7
而 Spring JDBC Template 也是更加接近於 SQL 層面,Spring 自己也能夠集成 Hibernate 等
O/R Mapping 框架。
關於這些具體開源框架的學習,個人建議是:
從總體上把握主流框架的架構和設計理念,掌握主要流程,例如 SQL 解析生成、SQL 執行到
結果映射等處理過程到底發生了什麼。
掌握映射等部分的細節定義和原理,根據我在準備專欄時整理的面試題目,發現不少題目都
是偏向於映射定義的細節。
另外,對比不一樣框架的設計和實現,既有利於你加深理解,也是面試考察的熱點方向之一。
今天我從數據庫應用開發者的角度,分析了 MySQL 數據庫的部份內部機制,而且補充了我對數
據庫相關面試準備和知識學習的建議,最後對主流 O/R Mapping 等框架進行了簡單的對比。 程序員
在平常開發中,尤爲是業務開發,少不了利用 Java 對數據庫進行基本的增刪改查等數據操做,
這也是 Java 工程師的必備技能之一。作好數據操做,不只僅須要對 Java 語言相關框架的掌
握,更須要對各類數據庫自身體系結構的理解。今天這一講,做爲補充 Java 面試考察知識點的
完整性,關於數據庫的應用和細節還須要在實踐中深刻學習。
今天我要問你的問題是,談談 MySQL 支持的事務隔離級別,以及悲觀鎖和樂觀鎖的原理和應用
場景?
典型回答
所謂隔離級別(Isolation Level),就是在數據庫事務中,爲保證併發數據讀寫的正確性而提出
的定義,它並非 MySQL 專有的概念,而是源於ANSI/ISO制定的SQL-92標準。
每種關係型數據庫都提供了各自特點的隔離級別實現,雖然在一般的定義中是以鎖爲實現單元,
但實際的實現千差萬別。以最多見的 MySQL InnoDB 引擎爲例,它是基於 MVCC(Multi-
2018/8/1 極客時間 | Java核心技術36講
https://time.geekbang.org/column/article/12288 2/7
Versioning Concurrency Control)和鎖的複合實現,按照隔離程度從低到高,MySQL 事務隔
離級別分爲四個不一樣層次:
讀未提交(Read uncommitted),就是一個事務可以看到其餘事務還沒有提交的修改,這是
最低的隔離水平,容許髒讀出現。
讀已提交(Read committed),事務可以看到的數據都是其餘事務已經提交的修改,也就
是保證不會看到任何中間性狀態,固然髒讀也不會出現。讀已提交仍然是比較低級別的隔
離,並不保證再次讀取時可以獲取一樣的數據,也就是容許其餘事務併發修改數據,容許不
可重複讀和幻象讀(Phantom Read)出現。
可重複讀(Repeatable reads),保證同一個事務中屢次讀取的數據是一致的,這是
MySQL InnoDB 引擎的默認隔離級別,可是和一些其餘數據庫實現不一樣的是,能夠簡單認爲
MySQL 在可重複讀級別不會出現幻象讀。
串行化(Serializable),併發事務之間是串行化的,一般意味着讀取須要獲取共享讀鎖,更
新須要獲取排他寫鎖,若是 SQL 使用 WHERE 語句,還會獲取區間鎖(MySQL 以 GAP 鎖
形式實現,可重複讀級別中默認也會使用),這是最高的隔離級別。
至於悲觀鎖和樂觀鎖,也並非 MySQL 或者數據庫中獨有的概念,而是併發編程的基本概念。
主要區別在於,操做共享數據時,「悲觀鎖」即認爲數據出現衝突的可能性更大,而「樂觀
鎖」則是認爲大部分狀況不會出現衝突,進而決定是否採起排他性措施。
反映到 MySQL 數據庫應用開發中,悲觀鎖通常就是利用相似 SELECT … FOR UPDATE 這樣的
語句,對數據加鎖,避免其餘事務意外修改數據。樂觀鎖則與 Java 併發包中的
AtomicFieldUpdater 相似,也是利用 CAS 機制,並不會對數據加鎖,而是經過對比數據的時
間戳或者版本號,來實現樂觀鎖須要的版本判斷。
我認爲前面提到的 MVCC,其本質就能夠看做是種樂觀鎖機制,而排他性的讀寫鎖、雙階段鎖
等則是悲觀鎖的實現。
有關它們的應用場景,你能夠構建一下簡化的火車餘票查詢和購票系統。同時查詢的人可能很
多,雖然具體座位票只能是賣給一我的,但餘票可能不少,並且也並不能預測哪一個查詢者會購
票,這個時候就更適合用樂觀鎖。
考點分析
今天的問題來源於實際面試,這兩部分問題反映了面試官試圖考察面試者在平常應用開發中,是
否學習或者思考過數據庫內部的機制,是否瞭解併發相關的基礎概念和實踐。
我從普通數據庫應用開發者的角度,提供了一個相對簡化的答案,面試官頗有可能進一步從實例
的角度展開,例如設計一個典型場景重現髒讀、幻象讀,或者從數據庫設計的角度,能夠用哪些
2018/8/1 極客時間 | Java核心技術36講
https://time.geekbang.org/column/article/12288 3/7
手段避免相似狀況。我建議你在準備面試時,能夠在典型的數據庫上試驗一下,驗證本身的觀
點。
其餘能夠考察的點也有不少,在準備這個問題時你也能夠對比 Java 語言的併發機制,進行深刻
理解,例如,隨着隔離級別從低到高,競爭性(Contention)逐漸加強,隨之而來的代價一樣
是性能和擴展性的降低。
數據庫衍生出不少不一樣的職責方向:
數據庫管理員(DBA),這是一個單獨的專業領域。
數據庫應用工程師,不少業務開發者就是這種定位,綜合利用數據庫和其餘編程語言等技
能,開發業務應用。
數據庫工程師,更加側重於開發數據庫、數據庫中間件等基礎軟件。
後面二者與 Java 開發更加相關,可是須要的知識和技能是不一樣的,因此面試的考察角度也有區
別,今天我會分析下對相關知識學習和準備面試的見解。
另外,在數據庫相關領域,Java 工程師最常接觸到的就是 O/R Mapping 框架或者相似的數據
庫交互類庫,我會選取最普遍使用的框架進行對比和分析。
知識擴展
首先,我來談談對數據庫相關領域學習的見解,從最普遍的應用開發者角度,至少須要掌握:
數據庫設計基礎,包括數據庫設計中的幾個基本範式,各類數據庫的基礎概念,例如表、視
圖、索引、外鍵、序列號生成器等,清楚如何將現實中業務實體和其依賴關係映射到數據庫
結構中,掌握典型實體數據應該使用什麼樣的數據庫數據類型等。
每種數據庫的設計和實現多少會存在差別,因此至少要精通你使用過的數據庫的設計要點。
我今天開篇談到的 MySQL 事務隔離級別,就區別於其餘數據庫,進一步瞭解 MVCC、
Locking 等機制對於處理進階問題很是有幫助;還須要瞭解,不一樣索引類型的使用,甚至是
底層數據結構和算法等。
常見的 SQL 語句,掌握基礎的 SQL 調優技巧,至少要了解基本思路是怎樣的,例如 SQL 怎
樣寫才能更好利用索引、知道如何分析SQL 執行計劃等。
更進一步,至少須要瞭解針對高併發等特定場景中的解決方案,例如讀寫分離、分庫分表,
或者如何利用緩存機制等,目前的數據存儲也遠不止傳統的關係型數據庫了。
2018/8/1 極客時間 | Java核心技術36講
https://time.geekbang.org/column/article/12288 4/7
上面的示意圖簡單總結了我對數據庫領域的理解,但願能夠給你進行準備時提供個借鑑。固然在
準備面試時並非一味找一堆書悶頭苦讀,我仍是建議從實際工做中使用的數據庫出發,側重於
結合實踐,完善和深化本身的知識體系。
接下來咱們仍是回到 Java 自己,目前最爲通用的 Java 和數據庫交互技術就是 JDBC,最多見的
開源框架基本都是構建在 JDBC 之上,包括咱們熟悉的JPA/Hibernate、MyBatis、Spring
JDBC Template 等,各自都有獨特的設計特色。
Hibernate 是最負盛名的 O/R Mapping 框架之一,它也是一個 JPA Provider。顧名思義,它
是以對象爲中心的,其強項更體如今數據庫到 Java 對象的映射,能夠很方便地在 Java 對象層
面體現外鍵約束等相對複雜的關係,提供了強大的持久化功能。內部大量使用了Lazy-load等技
術提升效率。而且,爲了屏蔽數據庫的差別,下降維護開銷,Hibernate 提供了類 SQL 的
HQL,能夠自動生成某種數據庫特定的 SQL 語句。
Hibernate 應用很是普遍,可是過分強調持久化和隔離數據庫底層細節,也致使了不少弊端,例
如 HQL 須要額外的學習,未必比深刻學習 SQL 語言更高效;減弱程序員對 SQL 的直接控制,
還可能致使其餘代價,原本一句 SQL 的事情,可能被 Hibernate 生成幾條,隱藏的內部細節也
阻礙了進一步的優化。
而 MyBatis 雖然仍然提供了一些映射的功能,但更加以 SQL 爲中心,開發者能夠側重於 SQL
和存儲過程,很是簡單、直接。若是咱們的應用須要大量高性能的或者複雜的 SELECT 語句
等,「半自動」的 MyBatis 就會比 Hibernate 更加實用。
2018/8/1 極客時間 | Java核心技術36講
https://time.geekbang.org/column/article/12288 5/7
而 Spring JDBC Template 也是更加接近於 SQL 層面,Spring 自己也能夠集成 Hibernate 等
O/R Mapping 框架。
關於這些具體開源框架的學習,個人建議是:
從總體上把握主流框架的架構和設計理念,掌握主要流程,例如 SQL 解析生成、SQL 執行到
結果映射等處理過程到底發生了什麼。
掌握映射等部分的細節定義和原理,根據我在準備專欄時整理的面試題目,發現不少題目都
是偏向於映射定義的細節。
另外,對比不一樣框架的設計和實現,既有利於你加深理解,也是面試考察的熱點方向之一。
今天我從數據庫應用開發者的角度,分析了 MySQL 數據庫的部份內部機制,而且補充了我對數
據庫相關面試準備和知識學習的建議,最後對主流 O/R Mapping 等框架進行了簡單的對比。
一課一
web