【Java高頻面試問題】數據庫篇
- 為什么MySQL選擇B+樹作為索引
- 一、B+ 樹的優勢特性
- 二、與常見數據結構的對比
- 索引優化
- 一、索引類型及使用場景
- 二、索引優化核心策略
- 1. 避免索引失效場景
- 2. 性能優化實踐
- 3. 表結構與架構優化
- 三、高頻面試問題參考答案
- 總結:面試核心要點
- 數據庫事務
- 一、事務基礎
- ?二、事務隔離級別
- ?三、Spring 事務管理
- 1. ?傳播機制(高頻考點)
- 2. ?事務失效場景
- 四、并發事務的控制方式
- ?1、鎖機制(Locking)
- 2、時間戳排序(Timestamp Ordering)
- 3、樂觀并發控制(OCC)
- 4、多版本并發控制(MVCC)
- 💎 ?總結對比
- ?五、高級考點
- 線上慢SQL查詢優化
為什么MySQL選擇B+樹作為索引
MySQL 選擇 B+ 樹作為索引結構是綜合磁盤 I/O 效率、范圍查詢能力、存儲利用率等因素的結果,其核心優勢如下(對比其他數據結構):
一、B+ 樹的優勢特性
-
多路平衡樹結構降低樹高?
- 每個節點可存儲大量鍵值(通常數百個),千萬級數據樹高僅 ?3-4 層?(二叉樹需 ?20+ 層?),減少磁盤 ?I/O 次數?(查詢耗時從
O(n)
優化至O(log n)
)。 - 示例?:查詢 1000 萬數據,B+ 樹僅需 ?3-4 次 I/O?,二叉樹需 ?20+ 次 I/O?。
- 每個節點可存儲大量鍵值(通常數百個),千萬級數據樹高僅 ?3-4 層?(二叉樹需 ?20+ 層?),減少磁盤 ?I/O 次數?(查詢耗時從
-
?葉子節點有序鏈表支持高效范圍查詢?
- 葉子節點通過雙向指針連接,范圍查詢(如
BETWEEN
、ORDER BY
)直接遍歷鏈表,?無需回溯父節點?。 - 對比哈希索引?:哈希僅支持等值查詢,無法高效處理范圍操作。
- 葉子節點通過雙向指針連接,范圍查詢(如
-
?非葉子節點不存數據,提升存儲利用率?
-
非葉子節點僅存?索引鍵+指針?(不存實際數據),單節點可容納更多鍵值:
-
1GB 索引?中非葉子節點約占 ?**10%?,葉子節點占 ?90%**?,顯著提升緩存命中率。
-
對比 B 樹?:B 樹非葉子節點存數據,導致 ?相同數據量下樹高增加 1 層?。
-
二、與常見數據結構的對比
?索引類型? | ?范圍查詢? | ?樹高/IO次數? | ?寫入性能? | ?適用場景? |
---|---|---|---|---|
?B+ 樹? | ? 高效 | ? 極低 (3-4層) | ? 平衡 | OLTP、高頻范圍查詢 |
?二叉樹/紅黑樹? | ? 支持 | ? 高 (O(n) 退化風險) | ?? 頻繁旋轉維護成本 | 內存數據結構 |
?哈希索引? | ? 不支持 | ? O(1) | ? 高 | 等值查詢場景 |
?B 樹? | ?? 部分支持 | ?? 較高 (比B+樹多1層) | ? 平衡 | 文件系統 |
二叉樹(紅黑樹):節點僅存 1 個鍵值,樹高過大導致 I/O 次數劇增,且插入刪除需頻繁旋轉維護平衡。
B 樹范:非葉子節點存數據,導致樹高增加、范圍查詢效率低于 B+ 樹。
哈希索引:不支持范圍掃描。
索引優化
一、索引類型及使用場景
?索引類型? | ?特點? | ?適用場景? |
---|---|---|
?主鍵索引? | 唯一、非空,聚簇索引結構 | 表的主鍵字段 |
?唯一索引? | 列值唯一,允許 NULL | 業務唯一字段(如手機號) |
?聯合索引? | 多列組合,遵循最左前綴匹配原則 | 多條件查詢(如 WHERE a=1 AND b=2 ) |
?覆蓋索引? | 查詢字段均在索引中,避免回表 | 高頻查詢的字段組合 |
?? ?聯合索引陷阱?:
- 違反最左前綴原則導致失效(如索引
(a,b,c)
,條件WHERE b=2
不生效) - 范圍查詢右側列失效(如
WHERE a>1 AND b=2
,b
無法用索引)
二、索引優化核心策略
1. 避免索引失效場景
?失效場景? | ?示例? | ?原因? | ?解決方案? |
---|---|---|---|
?對索引列使用函數? | WHERE YEAR(create_time)=2023 | 函數操作破壞索引值的有序性,優化器無法直接匹配索引樹結構 | 改用范圍查詢:WHERE create_time BETWEEN '2023-01-01' AND '2023-12-31' |
?隱式類型轉換? | WHERE phone=13800138000 (phone為VARCHAR) | 類型不匹配觸發隱式轉換,導致索引字段計算(如字符串轉數字) | 統一類型:WHERE phone='13800138000' |
?左模糊查詢? | WHERE name LIKE '%abc' | 通配符在前使B+樹無法利用前綴匹配特性,退化為全表掃描 | 改用右模糊:LIKE 'abc%' 或全文索引 |
?OR條件部分無索引? | WHERE a=1 OR b=2 (b無索引) | 優化器判定全表掃描成本低于“索引+回表”組合操作 | 拆分為UNION ALL 或為b建索引 |
?違反最左前綴原則? | WHERE b=1 AND c=2 (聯合索引為(a,b,c)) | 跳過最左列導致索引樹無法定位數據區間 | 查詢條件必須包含最左列(如WHERE a=1 AND b=1 ) |
?范圍查詢后索引失效? | WHERE a>1 AND b=2 (聯合索引(a,b)) | 范圍查詢導致后續索引列無法使用有序性 | 調整列順序或拆分為單列查詢 |
?IS NOT NULL條件? | WHERE a IS NOT NULL | 非覆蓋索引時需回表驗證數據是否存在,成本可能高于全表掃描 |
2. 性能優化實踐
- ?EXPLAIN 分析 SQL?:
EXPLAIN SELECT * FROM users WHERE age > 25;
關注 type
(掃描類型)、key
(使用索引)、Extra
(是否覆蓋索引)
-
?慢查詢定位?:
- 開啟慢日志:
slow_query_log=1, long_query_time=2
- 使用
SHOW PROFILE
分析執行耗時
- 開啟慢日志:
-
?索引覆蓋優化?:
-- 原查詢需回表
SELECT id, name, age FROM users WHERE city='Beijing';
-- 創建覆蓋索引(InnoDB 二級索引隱式包含主鍵)
CREATE INDEX idx_city_name_age ON users(city, name, age);
3. 表結構與架構優化
- ?垂直分表?:拆分大字段(如 TEXT)到單獨表,減少主表 I/O
- ?讀寫分離?:主庫寫 + 從庫讀,分散壓力
- ?冷熱數據分離?:歸檔歷史數據,減少主表體積
三、高頻面試問題參考答案
- ?B+Tree 為什么比 B-Tree 適合數據庫索引?
B+Tree 非葉節點不存數據,單頁存儲更多鍵值,降低樹高度;葉子節點鏈表支持高效范圍查詢,減少磁盤隨機 I/O
- ?如何優化深分頁
LIMIT 1000000,10
?
使用主鍵覆蓋索引,避免全表掃描
SELECT * FROM table WHERE id > (SELECT id FROM table ORDER BY id LIMIT 1000000,1) LIMIT 10;
- ?聯合索引 (a,b,c),
WHERE b=1 AND c=2
是否生效?
?否?,違反最左前綴原則。需至少包含 a
字段(如 WHERE a=1 AND b=1
)
總結:面試核心要點
?方向? | ?關鍵點? |
---|---|
?底層原理? | B+Tree 結構優勢、聚簇索引/二級索引區別、回表機制 |
?優化策略? | 最左前綴原則、覆蓋索引、索引失效場景規避、EXPLAIN 分析 |
?架構設計? | 讀寫分離、分庫分表、冷熱數據分離 |
?問題排查? | 慢查詢日志定位、PROFILE 分析、索引使用監控 |
終極建議?:結合業務場景設計索引(高頻查詢字段優先),避免過度索引;所有優化需通過 EXPLAIN 驗證
數據庫事務
數據庫事務可以保證多個對數據庫的操作構成一個邏輯上的整體。要么全部執行成功,要么全部不執行 。
一、事務基礎
-
?ACID 特性?
- ?原子性 (Atomicity):事務操作要么全成功,要么全失敗回滾(如轉賬操作)。
- ?一致性 (Consistency) ?:事務執行前后數據完整性不被破壞(如庫存不為負)。
- ?隔離性 (Isolation) ?:并發事務相互隔離,互不干擾。
- ?持久性 (Durability) ?:事務提交后數據永久存儲(如訂單持久化)。
🌈 補充:只有保證了事務的持久性、原子性、隔離性之后,一致性才能得到保障。也就是說 A、I、D 是手段,C 是目的!
-
?并發事務問題?
- ?臟讀?:讀取到其他事務未提交的數據。
- ?不可重復讀?:同一事務內多次讀取同一數據結果不同 (因其他事務修改)。
- ?幻讀?:同一查詢條件返回結果集數量變化 (因其他事務增刪數據)。
?二、事務隔離級別
隔離級別 | 臟讀 | 不可重復讀 | 幻讀 | 說明 |
---|---|---|---|---|
?READ UNCOMMITTED? | ? | ? | ? | 最低隔離,可能讀到未提交數據。 |
?READ COMMITTED? | ? | ? | ? | 僅讀取已提交數據 (Oracle 默認),解決臟讀。 |
?REPEATABLE READ? | ? | ? | ? | 保證多次讀取結果一致 (MySQL 默認),解決臟讀、不可重復讀。 |
?SERIALIZABLE? | ? | ? | ? | 完全串行化,性能最低但解決所有問題。 |
📌 ?MySQL 默認隔離級別為 REPEATABLE READ
,通過 MVCC 機制實現一致性讀。
?三、Spring 事務管理
1. ?傳播機制(高頻考點)
- PROPAGATION_REQUIRED?(默認):當前有事務則加入,無則新建。
@Transactional(propagation = Propagation.REQUIRED)
public void methodA() {methodB(); // 加入同一事務
}
- PROPAGATION_REQUIRES_NEW?:掛起當前事務,新建獨立事務。
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void methodB() { /* 新事務執行 */ }
- PROPAGATION_NESTED?:嵌套事務,外層失敗時回滾內層操作。
2. ?事務失效場景
- 非
public
方法使用@Transactional
。 - 自調用(同類方法內部調用)導致代理失效。
- 異常類型錯誤(默認僅回滾
RuntimeException
)或異常被捕獲未拋出。 - 未配置事務管理器或數據源問題。
四、并發事務的控制方式
?1、鎖機制(Locking)
通過對數據對象加鎖限制并發訪問,分為兩類:
-
共享鎖(S鎖/讀鎖):允許事務讀取數據,阻止其他事務加排他鎖(但允許多個事務同時加讀鎖)。
-
排他鎖(X鎖/寫鎖):允許事務修改數據,阻止其他事務加任何鎖。
-
?封鎖協議?:
- 一級封鎖:寫前加X鎖,事務結束釋放(防丟失修改)。
- 二級封鎖:讀前加S鎖(防臟讀),寫前加X鎖,讀后即釋S鎖。
- 三級封鎖:讀前加S鎖、寫前加X鎖,所有鎖事務結束釋放(防不可重復讀)。
2、時間戳排序(Timestamp Ordering)
為每個事務分配唯一時間戳,按時間順序調度操作:
- ?規則?:若事務T1時間戳早于T2,則T1操作優先執行,沖突時回滾時間戳大的事務。
- ?優點?:避免死鎖,但需全局時鐘維護時序。
3、樂觀并發控制(OCC)
假設事務沖突概率低,分三階段執行:
- ?讀階段?:記錄讀寫集,不立即加鎖。
- 驗證階段?:提交前檢測讀寫沖突(如向后/向前校驗)。
- 寫階段?:無沖突則提交,否則回滾重試。
適用場景?:低沖突、讀多寫少環境。
4、多版本并發控制(MVCC)
維護數據多個歷史版本,實現讀寫分離:
-
?讀操作?:訪問事務開始時的快照版本(避免阻塞寫操作)。
-
?寫操作?:創建新版本,不影響正在讀取的舊版本。
-
?典型應用?:
- MySQL的
REPEATABLE READ
隔離級別通過MVCC解決不可重復讀。 - Oracle采用MVCC優化行級鎖,減少阻塞。
- MySQL的
💎 ?總結對比
?方法? | ?核心思想? | ?優勢? | ?劣勢? |
---|---|---|---|
?鎖機制? | 強制串行訪問 | 強一致性,實現簡單 | 死鎖風險,并發度低 |
?時間戳排序? | 按時間順序調度 | 無死鎖 | 時鐘同步難,事務重啟率高 |
?樂觀控制? | 提交時沖突檢測 | 高并發,減少鎖開銷 | 高沖突時頻繁回滾 |
?MVCC? | 多版本快照讀 | 讀寫無阻塞,高并發 | 版本存儲開銷大 |
📌 ?實踐選擇?:單機高并發首選MVCC(如MySQL);分布式系統常組合使用MVCC與時間戳(如CockroachDB);低沖突場景可采用OCC優化性能。
?五、高級考點
- ?分布式事務?
-
CAP 理論?:一致性 (Consistency)、可用性 (Availability)、分區容錯性 (Partition Tolerance) 不可兼得。
-
解決方案?:
- ?2PC (兩階段提交):強一致,但存在同步阻塞問題。
- ?TCC (補償事務):通過 Try/Confirm/Cancel 柔性事務保證最終一致。
- ?Seata 等框架?:基于 AT 模式自動回滾。
-
?事務日志與鎖機制?
- ?Redo Log?:保證持久性,記錄物理修改。
- ?Undo Log?:保證原子性,記錄事務回滾所需信息。
- ?鎖分類?:樂觀鎖 (CAS)、悲觀鎖 (行鎖/表鎖)。
線上慢SQL查詢優化
-
?定位慢SQL?
- ?日志監控?:開啟MySQL慢查詢日志(
slow_query_log=ON
,long_query_time=1
) - ?連接池工具?:Druid監控面板(記錄執行耗時、最慢SQL排行)或云平臺慢SQL分析功能
- ?日志監控?:開啟MySQL慢查詢日志(
-
?分析執行計劃:關鍵字段分析?
EXPLAIN SELECT * FROM orders WHERE user_id=100;
type
:效率排序const > ref > range > index > ALL
(避免全表掃描)rows
:掃描行數(越少越好)Extra
:Using filesort
(需排序優化)、Using temporary
(需避免臨時表)
-
?制定優化方案?
- 索引優化 → SQL重寫 → 分頁/表結構優化 → 業務/架構優化
持續更新中…