一、Java 核心
1. 基礎語法
final
關鍵字的作用- 修飾類:類不可被繼承(如
String
類),保證類的穩定性和安全性。 - 修飾方法:方法不可被重寫(防止子類篡改父類核心邏輯,如工具類方法)。
- 修飾變量:
- 基本類型:值不可修改(如
final int a = 10
,a
的值終身不變)。 - 引用類型:引用地址不可變(對象內容可修改,如
final List<String> list = new ArrayList<>()
,list
可添加元素但不能指向新集合)。
- 基本類型:值不可修改(如
- 修飾類:類不可被繼承(如
深拷貝 vs 淺拷貝
類型 特點 實現方式 適用場景 淺拷貝 僅復制對象本身及基本類型字段,引用類型字段與原對象共享同一份引用(新舊對象關聯) Object.clone()
默認實現(需實現Cloneable
接口)、BeanUtils.copyProperties()
簡單對象復制,無需完全隔離引用關系 深拷貝 遞歸復制所有字段(包括引用類型),新舊對象完全獨立(無任何關聯) 手動遞歸復制、序列化( Serializable
)、JSON 工具(如 Jackson)復雜對象復制,需徹底隔離引用關系
2. 集合框架
HashMap
底層結構(Java 8+)
采用數組 + 鏈表 + 紅黑樹混合結構,平衡查詢效率與內存開銷:- 數組(桶):初始容量為 16(
DEFAULT_INITIAL_CAPACITY
),擴容時按 2 倍增長(newCap = oldCap << 1
),存儲鏈表頭節點或紅黑樹根節點。 - 鏈表:哈希沖突時(不同 key 計算出相同索引),用鏈表存儲元素,查詢時間復雜度為 O (n)。
- 紅黑樹:當鏈表長度超過 8(
TREEIFY_THRESHOLD
)且數組長度≥64(MIN_TREEIFY_CAPACITY
)時,鏈表轉為紅黑樹(查詢時間復雜度優化為 O (log n));當長度≤6(UNTREEIFY_THRESHOLD
)時,紅黑樹轉回鏈表(降低樹結構維護成本)。
優化點:
- 哈希計算:
(key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16)
,通過高位與低位異或,減少哈希沖突(將高位信息融入低位)。 - 擴容機制:使用高位掩碼計算新索引(
newIndex = oldCap + (index & (oldCap - 1))
),避免節點重新哈希,直接定位新位置。
- 數組(桶):初始容量為 16(
HashMap
線程安全性HashMap
非線程安全,多線程并發操作可能導致:- 數據覆蓋:多線程同時
put
時,可能覆蓋彼此的結果。 - Java 7 死循環:鏈表采用頭插法,擴容時可能導致鏈表成環(Java 8 改為尾插法解決,但仍非線程安全)。
線程安全的 Map 實現:
ConcurrentHashMap
(推薦):- Java 7:基于分段鎖(
Segment
,每個 Segment 是獨立的哈希表),并發度為 Segment 數量(默認 16)。 - Java 8:移除 Segment,改用 CAS +?
synchronized
(只鎖單個桶),并發效率更高,支持computeIfAbsent
等原子操作。
- Java 7:基于分段鎖(
Hashtable
:全表鎖(所有方法用synchronized
修飾),并發性能差,已淘汰。Collections.synchronizedMap(Map)
:包裝普通 Map,通過同步代碼塊(synchronized (mutex)
)保證安全,性能一般(鎖整個 Map)。
- 數據覆蓋:多線程同時
ArrayList
?vs?LinkedList
特性 ArrayList
LinkedList
底層結構 動態數組(連續內存空間) 雙向鏈表(非連續內存) 隨機訪問 O (1)(通過索引直接訪問) O (n)(需遍歷鏈表) 插入 / 刪除 中間操作 O (n)(需移動元素),尾部操作 O (1) 中間操作 O (1)(修改指針),但定位節點需 O (n) 內存占用 較少(連續空間,有擴容冗余) 較多(每個節點含前后指針) 擴容機制 容量不足時擴容為 1.5 倍( oldCap + (oldCap >> 1)
)無擴容(動態添加節點) 適用場景 頻繁查詢、尾部增刪 頻繁中間插入 / 刪除
3. JVM
內存區域劃分
區域類型 包含區域 特點 可能的異常 線程私有 虛擬機棧 存儲棧幀(局部變量表、操作數棧、返回地址等),方法調用時創建,結束時銷毀。 棧深度超過限制→ StackOverflowError
;內存不足→OutOfMemoryError
。本地方法棧 為 native
方法(如Object.hashCode()
)提供內存空間。同虛擬機棧。 程序計數器 記錄當前線程執行的字節碼行號(如分支、循環、跳轉)。 唯一不會拋出 OOM 的區域。 線程共享 堆 存儲對象實例、數組,GC 主要工作區域(分新生代、老年代)。 內存不足→ OutOfMemoryError
。方法區(元空間) 存儲類信息(結構、方法數據)、常量池、靜態變量、JIT 編譯后的代碼。 JDK 8 前(永久代)可能 OOM;JDK 8 后元空間使用本地內存,默認無上限(可通過 -XX:MaxMetaspaceSize
限制)。垃圾回收(GC)判定機制
采用可達性分析算法:- GC Roots(根對象):
- 虛擬機棧中局部變量表引用的對象(如方法內的
User user = new User()
)。 - 方法區中類靜態變量引用的對象(如
static User user = new User()
)。 - 方法區中常量引用的對象(如
String s = "abc"
中的"abc"
)。 - 本地方法棧中
native
方法引用的對象。
- 虛擬機棧中局部變量表引用的對象(如方法內的
- 判定流程:從 GC Roots 出發,遍歷所有可達對象并標記;未被標記的對象為不可達對象,可被回收(但并非立即回收,需經歷多次標記)。
引用類型對回收的影響:
- 強引用(
User u = new User()
):永不回收,OOM 也不釋放。 - 軟引用(
SoftReference<User>
):內存不足時回收,用于緩存(如圖片緩存)。 - 弱引用(
WeakReference<User>
):GC 時立即回收,用于臨時關聯(如ThreadLocal
的 key)。 - 虛引用(
PhantomReference<User>
):僅用于跟蹤對象回收,必須配合引用隊列,無實際引用意義(如堆外內存回收)。
- GC Roots(根對象):
類加載機制
遵循雙親委派模型(防止類重復加載),流程:- 加載(Loading):通過類全限定名(如
com.xxx.User
)讀取.class
文件到內存,生成Class
對象(存方法區)。 - 驗證(Verification):檢查字節碼合法性(格式、語義、字節碼指令、符號引用),防止惡意 class 文件。
- 準備(Preparation):為靜態變量(
static
)分配內存并賦默認值(如static int a = 10
,此處賦 0)。 - 解析(Resolution):將符號引用(如類名、方法名)轉為直接引用(內存地址)。
- 初始化(Initialization):執行
<clinit>()
方法(合并靜態變量賦值與靜態代碼塊,按順序執行)。 - 使用(Using):實例化對象、調用方法。
- 卸載(Unloading):類不再被引用且類加載器被回收時,
Class
對象被卸載(極少發生)。
- 加載(Loading):通過類全限定名(如
垃圾回收機制
分代收集理論:
- 新生代(Young Generation):對象存活時間短(如方法內臨時變量),約 90% 對象會被回收,頻繁觸發
Minor GC
(年輕代 GC)。- 結構:Eden 區(80%) + From Survivor 區(10%) + To Survivor 區(10%)。
- 算法:復制算法(將 Eden 和 From Survivor 存活對象復制到 To Survivor,清空原區域,無碎片)。
- 老年代(Old Generation):對象存活時間長(如緩存對象),觸發
Full GC
(全局 GC)頻率低。- 算法:標記 - 清除(標記存活對象,清除未標記對象,可能產生碎片)或標記 - 整理(標記后將存活對象壓縮到一端,消除碎片)。
- 新生代(Young Generation):對象存活時間短(如方法內臨時變量),約 90% 對象會被回收,頻繁觸發
GC 算法實現:
Serial GC
:單線程回收,STW(Stop The World)時間長,適合客戶端應用(如桌面程序)。Parallel GC
:多線程回收,以吞吐量(用戶線程運行時間 / 總時間)為目標,JDK 8 默認年輕代收集器。CMS GC
(Concurrent Mark Sweep):低延遲優先,步驟:初始標記(STW)→并發標記→重新標記(STW)→并發清除(僅初始和重新標記 STW),缺點:內存碎片多、CPU 消耗高(JDK 9 后廢棄)。G1 GC
(Garbage-First):區域化分代式收集器(取代 CMS),將堆分為多個 Region,兼顧吞吐量和延遲,JDK 9 + 默認,適合大堆場景(如 8G 以上)。
JVM 調優經驗
- 核心參數:
bash
-Xms2g -Xmx2g # 堆初始和最大大小(設為相同,避免動態擴容) -XX:NewRatio=2 # 老年代:新生代=2:1(默認) -XX:SurvivorRatio=8 # Eden:From:To=8:1:1 -XX:MaxMetaspaceSize=256m # 元空間上限(默認無上限) -XX:+UseG1GC # 使用G1垃圾回收器 -XX:MaxGCPauseMillis=200 # G1目標最大STW時間(毫秒) -XX:+HeapDumpOnOutOfMemoryError # OOM時生成堆轉儲文件 -XX:HeapDumpPath=/path/to/dump # 堆轉儲文件路徑
- 調優場景:
Full GC
頻繁:檢查是否有大對象頻繁進入老年代(如大集合未釋放),增大老年代內存(-Xmx
)或調整晉升閾值(-XX:MaxTenuringThreshold
,默認 15)。- OOM(
Java heap space
):通過jmap -heap <pid>
分析堆使用,結合堆轉儲文件(jhat
或 MAT 工具)定位內存泄漏(如靜態集合未清理)。 - 延遲過高:切換至 G1 或 ZGC(超低延遲),調小
-XX:MaxGCPauseMillis
。
- 核心參數:
4. 多線程
線程安全集合
ConcurrentHashMap
:線程安全的哈希表(見上文)。CopyOnWriteArrayList
:讀寫分離,寫時復制新數組(add
/set
時),讀無需鎖,適合讀多寫少場景(如配置列表)。ConcurrentLinkedQueue
:無鎖并發隊列,基于 CAS 實現,高效(生產者 - 消費者模型)。BlockingQueue
:阻塞隊列(ArrayBlockingQueue
、LinkedBlockingQueue
),支持put
(滿時阻塞)和take
(空時阻塞),適合異步通信。
線程池
核心參數(
ThreadPoolExecutor
):java
new ThreadPoolExecutor(corePoolSize, // 核心線程數(長期存活,即使空閑)maximumPoolSize, // 最大線程數(核心 + 臨時線程)keepAliveTime, // 臨時線程空閑存活時間unit, // 時間單位(如TimeUnit.SECONDS)workQueue, // 工作隊列(存放等待執行的任務)threadFactory, // 線程工廠(自定義線程名、優先級等)handler // 拒絕策略(隊列滿且線程達最大時觸發) )
參數設置策略:
- 核心線程數(corePoolSize):
- CPU 密集型任務(如計算):
CPU核心數 + 1
(減少線程切換,充分利用 CPU)。 - IO 密集型任務(如網絡請求、文件讀寫):
CPU核心數 * 2
(線程多處于等待狀態,需更多線程提高吞吐量)。
- CPU 密集型任務(如計算):
- 最大線程數(maximumPoolSize):通常為核心線程數的 2 倍,需結合隊列容量(隊列滿時才創建臨時線程)。
- 工作隊列:
LinkedBlockingQueue
:無界隊列(默認容量 Integer.MAX_VALUE),適合高吞吐但需控制內存(避免 OOM)。ArrayBlockingQueue
:有界隊列,適合資源有限場景(需合理設置容量,避免隊列過大導致內存溢出)。SynchronousQueue
:無緩沖隊列(直接移交任務給線程),適合快速響應場景(配合maximumPoolSize
為 Integer.MAX_VALUE)。
- 拒絕策略:
AbortPolicy
:拋RejectedExecutionException
(默認)。CallerRunsPolicy
:由提交任務的線程執行(減緩提交速度,自帶限流)。DiscardPolicy
:默默丟棄新任務。DiscardOldestPolicy
:丟棄隊列中最舊的任務,嘗試提交新任務。
- 核心線程數(corePoolSize):
使用示例:
java
// 構建線程池 ExecutorService executor = new ThreadPoolExecutor(5, 10, 60, TimeUnit.SECONDS,new LinkedBlockingQueue<>(100),Executors.defaultThreadFactory(),new ThreadPoolExecutor.CallerRunsPolicy() ); // 提交任務 executor.submit(() -> System.out.println("Task running")); // 返回Future // 關閉線程池 executor.shutdown(); // 執行完現有任務后關閉,不再接受新任務
synchronized
鎖升級synchronized
通過鎖升級機制優化性能(從低效到高效):- 無鎖狀態:對象剛創建,無線程競爭,Mark Word 存儲對象哈希碼。
- 偏向鎖:同一線程多次獲取鎖,Mark Word 記錄線程 ID,避免 CAS 操作(適用于單線程場景)。
- 輕量級鎖:多線程交替獲取鎖,通過 CAS 競爭鎖(自旋嘗試),避免重量級鎖開銷(適用于短時間競爭)。
- 重量級鎖:多線程并發競爭激烈,自旋失敗后膨脹為重量級鎖(依賴操作系統互斥量),線程阻塞等待(適用于長時間競爭)。
二、MySQL 數據庫
1. 索引
底層結構(InnoDB)
采用B + 樹,原因:- 磁盤友好:非葉子節點僅存索引鍵,葉子節點存儲數據(聚集索引)或主鍵(二級索引),單節點存儲更多鍵,減少 I/O 次數。
- 范圍查詢高效:葉子節點通過雙向鏈表連接,支持快速范圍掃描(如
WHERE id > 100 AND id < 200
)。 - 平衡性好:B + 樹為平衡樹,查詢時間穩定在 O (log n)。
對比 B 樹:B 樹葉子節點和非葉子節點都存數據,單節點存儲鍵數少,樹高更高,I/O 效率低。
索引類型
- 聚集索引(主鍵索引):按主鍵排序,葉子節點存儲完整數據行,InnoDB 表必存在(無主鍵則選唯一索引,否則隱式生成 6 字節 ROWID)。
- 二級索引(非主鍵索引):按索引列排序,葉子節點存儲主鍵值,查詢時需通過主鍵回表(到聚集索引查完整數據)。
- 聯合索引:多列組合索引(如
(a,b,c)
),遵循最左前綴原則(查詢條件含a
或a+b
或a+b+c
時生效)。
索引失效場景
- 未遵循最左前綴原則(如聯合索引
(a,b,c)
,查詢WHERE b=1
則失效)。 - 對索引列進行計算 / 函數操作(如
WHERE YEAR(create_time) = 2023
,create_time
索引失效)。 - 使用
!=
、<>
、NOT IN
(優化器可能放棄索引,選擇全表掃描)。 - 隱式類型轉換(如
varchar
列用數字查詢:WHERE name = 123
,索引失效)。 OR
條件中存在非索引列(如WHERE a=1 OR b=2
,僅a
有索引則失效)。LIKE
以通配符開頭(如WHERE name LIKE '%張'
,無法使用索引)。- 數據量極小(優化器認為全表掃描比走索引更快)。
IS NULL
/IS NOT NULL
(取決于字段是否允許為 NULL 及數據分布)。
- 未遵循最左前綴原則(如聯合索引
索引設計原則
- 適合建索引:高頻查詢條件(
WHERE
)、排序 / 分組字段(ORDER BY
/GROUP BY
)、聯合索引需滿足最左前綴。 - 不適合建索引:低區分度字段(如性別、狀態,基數低)、頻繁更新字段(索引維護成本高)、表數據量極少(全表掃描更快)。
- 適合建索引:高頻查詢條件(
2. 事務
ACID 特性
- 原子性(Atomicity):事務要么全執行,要么全回滾。由
Undo Log
實現(記錄數據修改前狀態,回滾時恢復)。 - 一致性(Consistency):事務執行前后數據狀態合法(如轉賬后總金額不變)。由原子性、隔離性、持久性共同保證。
- 隔離性(Isolation):多事務并發時,操作互不干擾。由鎖(行鎖、間隙鎖)和 MVCC(多版本并發控制)實現。
- 持久性(Durability):事務提交后數據永久保存。由
Redo Log
實現(先寫日志再刷盤,崩潰后重放日志恢復)。
- 原子性(Atomicity):事務要么全執行,要么全回滾。由
隔離級別
隔離級別 臟讀(讀未提交數據) 不可重復讀(重讀數據變) 幻讀(新增數據被讀到) 實現方式 讀未提交(RU) 存在 存在 存在 無鎖,直接讀取最新數據 讀已提交(RC) 不存在 存在 存在 MVCC(快照讀,每次讀新快照) 可重復讀(RR) 不存在 不存在 不存在(InnoDB) MVCC + 間隙鎖(防止新增數據) 串行化(Serial) 不存在 不存在 不存在 全表鎖(事務按順序執行) 幻讀解決:InnoDB 在 RR 級別通過間隙鎖(鎖定索引范圍,如
WHERE id > 10
會鎖定 10 以上的間隙)防止其他事務插入新行,避免幻讀。顯式加鎖SELECT ... FOR UPDATE
也可解決。
3. 日志
日志類型 | 作用 | 特點 |
---|---|---|
Binlog(二進制日志) | 記錄所有寫操作(DDL、DML),用于主從復制(從庫同步主庫日志)和數據恢復(mysqlbinlog 回放)。 | 邏輯日志(記錄 SQL 語句或行變更),追加寫入,可通過expire_logs_days 自動清理。 |
Redo Log(重做日志) | InnoDB 引擎日志,記錄數據頁修改,保證事務持久性(崩潰后重放日志恢復數據)。 | 物理日志(記錄頁地址和修改內容),循環寫入(固定大小),有innodb_log_file_size 控制大小。 |
Undo Log(回滾日志) | InnoDB 引擎日志,記錄數據修改前狀態,用于事務回滾和 MVCC(提供歷史版本)。 | 事務提交后可刪除,存儲在 undo 表空間。 |
Slow Query Log(慢查詢日志) | 記錄執行時間超過long_query_time 的 SQL,用于優化慢查詢。 | 默認關閉,需手動開啟(slow_query_log = ON )。 |
Error Log(錯誤日志) | 記錄 MySQL 啟動、運行、關閉過程中的錯誤信息,用于排查故障。 | 必開啟,路徑由log_error 指定。 |
4. 慢查詢優化
定位慢查詢
- 開啟慢查詢日志:
sql
SET GLOBAL slow_query_log = ON; SET GLOBAL long_query_time = 2; -- 閾值(秒) SET GLOBAL slow_query_log_file = '/var/log/mysql/slow.log';
- 使用
EXPLAIN
分析執行計劃,關注:type
:訪問類型(ALL
全表掃描,ref
/range
走索引)。key
:實際使用的索引(NULL
表示未走索引)。rows
:預估掃描行數(值越小越好)。Extra
:Using filesort
(文件排序,需優化)、Using index
(覆蓋索引,優)。
- 開啟慢查詢日志:
優化措施
- 索引優化:添加缺失索引,修復失效索引(如避免函數操作索引列)。
- SQL 改寫:拆分復雜查詢、避免
SELECT *
(用覆蓋索引)、用JOIN
代替子查詢。 - 數據庫調優:調整
innodb_buffer_pool_size
(建議設為內存 50%-70%)、分庫分表(數據量超千萬時)。
三、Redis 緩存
1. 數據結構
數據類型 | 底層結構(核心) | 特點 | 應用場景 |
---|---|---|---|
String | 簡單動態字符串(SDS) | 二進制安全(可存文本、圖片),支持INCR /DECR 自增自減 | 緩存(用戶信息)、計數器(閱讀量)、分布式 ID |
Hash | 哈希表(數組 + 鏈表) | 鍵值對集合(field-value),適合存儲對象 | 存儲用戶信息(user:1 {name: "a", age: 20} ) |
List | 雙向鏈表 + 壓縮列表(元素少且小) | 有序,可重復,支持LPUSH /RPOP 兩端操作 | 消息隊列、最新列表(熱搜前 10) |
Set | 哈希表 + 整數集合(元素為整數且少) | 無序,唯一元素,支持SINTER (交集)/SUNION (并集) | 標簽(文章標簽去重)、共同好友 |
ZSet | 跳躍表 + 哈希表 | 按score 排序,元素唯一,支持ZRANK (排名) | 排行榜(游戲積分)、延遲隊列(按時間 score 排序) |
2. 持久化
持久化方式 | 原理 | 優點 | 缺點 |
---|---|---|---|
RDB | 定時生成內存快照(.rdb 文件),通過SAVE (阻塞)或BGSAVE (后臺線程)觸發。 | 恢復速度快(直接加載二進制文件)、文件小 | 可能丟失最后一次快照后的修改(如突發宕機)。 |
AOF | 記錄所有寫命令(追加到.aof 文件),通過appendfsync 控制同步策略(always /everysec /no )。 | 數據安全性高(everysec 最多丟 1 秒數據) | 文件大(命令冗余)、恢復速度慢(需重放命令)。 |
混合持久化(Redis 4.0+):AOF 文件開頭為 RDB 快照,后續為增量命令,兼顧兩者優點(aof-use-rdb-preamble yes
開啟)。
3. 主從復制
流程:
- 從節點執行
SLAVEOF <master-ip> <port>
,建立連接。 - 主節點執行
BGSAVE
生成 RDB,發送給從節點(全量復制)。 - 從節點加載 RDB 后,主節點通過
repl_backlog_buffer
同步增量寫命令(增量復制)。
- 從節點執行
問題及解決:
- 數據延遲:主從異步復制導致從庫數據滯后,適合讀多寫少場景(寫主讀從)。
- 腦裂:網絡分區時,從庫被誤認為主庫,導致數據不一致。解決:
min-slaves-to-write = 1
(主庫至少 1 個從庫連接才允許寫)。
4. 緩存問題(緩存三兄弟)
問題 | 原因 | 解決方案 |
---|---|---|
緩存穿透 | 查詢不存在的數據(如id=-1 ),緩存和 DB 都無數據,請求直達 DB。 | 1. 布隆過濾器(提前過濾不存在的 key);2. 緩存空值(短期有效,如 5 分鐘)。 |
緩存擊穿 | 熱點 key 過期瞬間,大量并發請求直達 DB。 | 1. 互斥鎖(Redis?SETNX ,只讓一個線程更新緩存);2. 熱點 key 永不過期(后臺異步更新)。 |
緩存雪崩 | 大量 key 同時過期或緩存集群宕機,請求全壓到 DB。 | 1. 隨機過期時間(避免集中過期);2. 緩存集群(主從 + 哨兵);3. 服務降級 / 限流。 |
5. 常見用途
- 緩存:存儲高頻訪問數據(如商品詳情),減輕數據庫壓力(設置合理 TTL 避免雪崩)。
- 分布式鎖:
SET key value NX PX 30000
(NX:不存在才設置,PX:30 秒過期),釋放鎖時驗證 value(避免誤刪)。 - 排行榜:ZSET 的
ZADD
(更新分數)、ZREVRANGE
(獲取 TopN)實現實時排名。 - 計數器:
INCR
/DECR
原子操作,適合閱讀量、點贊數等。 - 消息隊列:List 的
LPUSH
(生產者)和BRPOP
(消費者,阻塞彈出)實現簡單隊列。
6. 引入組件的考慮因素
- 必要性:是否解決核心問題(如緩存是否真能提升性能,避免過度設計)。
- 性能:吞吐量(QPS)、延遲(響應時間)是否滿足業務需求(Redis 單機 QPS 可達 10 萬 +)。
- 運維成本:部署、監控(
INFO
命令、Prometheus)、擴縮容難度(集群是否支持動態添加節點)。 - 社區支持:文檔完善度、版本更新頻率(Redis 社區活躍,問題解決快)。
- 數據一致性:與數據庫的同步策略(Cache-Aside、Write-Through)是否可控。
7. 高可用方案
模式 | 原理 | 特點 | 適用場景 |
---|---|---|---|
主從復制 | 主節點寫入,從節點同步數據并提供讀服務(一主多從)。 | 部署簡單,提升讀吞吐量;主節點故障需手動切換。 | 讀多寫少,可用性要求不高。 |
哨兵(Sentinel) | 哨兵集群監控主從節點,主節點故障時自動選舉新主節點(故障轉移)。 | 自動故障轉移,高可用;需至少 3 個哨兵節點(避免腦裂)。 | 生產環境基礎高可用方案。 |
Cluster 集群 | 數據分片到 16384 個槽(Slot),每個節點負責部分槽,多主多從(每個主節點有從節點)。 | 水平擴展(動態添加節點)、高可用;支持跨節點操作(MGET 需指定槽)。 | 數據量大(超 10GB)、高并發場景。 |
8. Redis Cluster 的插槽(Slot)
- 作用:
- 數據分片:
CRC16(key) % 16384
計算 key 所屬槽,每個節點負責部分槽(如 3 個主節點各負責~5461 個槽),實現數據分布式存儲。 - 路由轉發:客戶端連接任一節點,若 key 不在當前節點負責的槽,節點返回
MOVED
指令指引至目標節點。 - 動態擴容:槽可在節點間遷移(
CLUSTER MIGRATE
),無需停機(遷移過程中雙寫保證數據一致性)。
- 數據分片:
四、Spring 框架
1. IOC(控制反轉)
- 核心思想:將對象創建、依賴管理交給 Spring 容器,而非手動
new
對象,降低耦合。 - 依賴注入方式:
@Autowired
:按類型注入(默認),配合@Qualifier
按名稱注入。@Resource
:按名稱注入(默認),無名稱時按類型注入(JDK 注解,非 Spring)。- 構造器注入:通過構造方法傳遞依賴(推薦,避免循環依賴問題)。
- Bean 初始化流程:
- 讀取配置(XML / 注解)→ 2. 解析為
BeanDefinition
?→ 3. 實例化(new
對象)→ 4. 依賴注入(填充屬性)→ 5. 初始化(@PostConstruct
、InitializingBean
等)→ 6. 放入容器。
- 讀取配置(XML / 注解)→ 2. 解析為
2. AOP(面向切面編程)
原理:基于動態代理,在不修改源碼的情況下為方法添加額外功能(如日志、事務)。
- JDK 代理:目標類實現接口時,
Proxy.newProxyInstance()
生成代理類(實現同一接口)。 - CGLIB 代理:目標類無接口時,
Enhancer
生成子類代理(重寫目標方法),無法代理final
類 / 方法。
- JDK 代理:目標類實現接口時,
核心概念:
- 切面(
@Aspect
):封裝橫切邏輯的類(如日志切面)。 - 切點(
@Pointcut
):定義攔截哪些方法(如execution(* com.xxx.service.*(..))
)。 - 通知(Advice):切點執行時的動作(
@Before
前置、@After
后置、@Around
環繞等)。
- 切面(
應用場景:日志記錄、事務管理(
@Transactional
)、權限校驗、性能監控。
3. Bean 生命周期
- 實例化:容器通過構造方法創建 Bean 對象(
new
)。 - 屬性賦值:注入依賴(
@Autowired
/setter 方法)。 - 初始化:
- 執行
Aware
接口回調(BeanNameAware
、ApplicationContextAware
等)。 - 執行
@PostConstruct
注解方法(JSR-250 規范,初始化前)。 - 執行
InitializingBean.afterPropertiesSet()
方法(Spring 接口)。 - 執行自定義
init-method
(XML 配置或@Bean(initMethod)
)。
- 執行
- 使用:Bean 放入容器,供其他組件調用。
- 銷毀:
- 執行
@PreDestroy
注解方法(JSR-250 規范,銷毀前)。 - 執行
DisposableBean.destroy()
方法(Spring 接口)。 - 執行自定義
destroy-method
(XML 配置或@Bean(destroyMethod)
)。
- 執行
4. 循環依賴解決
場景:A 依賴 B,B 依賴 A。
Spring 通過三級緩存解決單例 Bean 循環依賴:
- 一級緩存(
singletonObjects
):存放完全初始化的 Bean。 - 二級緩存(
earlySingletonObjects
):存放半成品 Bean(已實例化未賦值)。 - 三級緩存(
singletonFactories
):存放 Bean 工廠(用于生成代理對象)。
流程:
- A 實例化后,放入三級緩存 → 2. A 需要注入 B,容器初始化 B → 3. B 需要注入 A,從三級緩存獲取 A 的早期引用(若 A 需代理,通過工廠生成代理),放入二級緩存 → 4. B 初始化完成,注入 A,放入一級緩存 → 5. A 注入 B,完成初始化,放入一級緩存(移除二、三級緩存)。
五、消息隊列(Kafka)
1. 消息不丟失保證
環節 | 措施 |
---|---|
生產者 | -?acks=all :消息需被所有 ISR(同步副本)確認后才算發送成功。- 開啟重試( retries=N ):發送失敗自動重試。- 用 linger.ms 批量發送,減少網絡開銷。 |
Broker | -?replication.factor≥3 :每個分區至少 3 個副本。-? min.insync.replicas≥2 :ISR 中至少 2 個副本存活才允許寫入。- 禁用 unclean.leader.election.enable (不允許非 ISR 副本成為 leader)。 |
消費者 | - 關閉自動提交(enable.auto.commit=false ),手動提交偏移量(commitSync() )。- 處理完消息后再提交,避免消費失敗但已提交偏移量。 - 冪等處理:通過消息 ID 去重,防止重復消費。 |
六、設計模式
1. 單例模式(Singleton Pattern)
- 概念:保證一個類在整個應用中只有一個實例,并提供一個全局訪問點。
- 核心思想:通過私有化構造方法,防止外部直接創建實例;在類內部自行創建唯一實例,并提供靜態方法供外部獲取。
- 適用場景:
- 全局配置類(如讀取配置文件的工具類),確保配置信息一致。
- 數據庫連接池,避免頻繁創建和關閉連接帶來的性能損耗。
- 日志工具類,保證日志輸出的一致性和順序性。
- 注意點:需考慮線程安全問題(如懶漢式需加鎖),以及反射、序列化對單例的破壞。
2. 工廠模式(Factory Pattern)
包括簡單工廠、工廠方法、抽象工廠三種形式:
- 簡單工廠模式:
- 概念:由一個工廠類根據傳入的參數,動態決定創建哪一種產品類的實例。
- 適用場景:產品種類較少且固定,如不同類型的日志記錄器(文件日志、控制臺日志)的創建。
- 工廠方法模式:
- 概念:定義一個創建產品的接口,由子類決定具體創建哪種產品,將產品創建延遲到子類。
- 適用場景:產品種類可能擴展,如不同數據庫(MySQL、Oracle)的連接工廠,新增數據庫時只需新增對應的工廠子類。
- 抽象工廠模式:
- 概念:提供一個接口,用于創建一系列相關或相互依賴的產品族,而無需指定具體類。
- 適用場景:需要創建多個產品族,且產品族內的產品相互關聯,如不同操作系統(Windows、Mac)下的按鈕、文本框等組件族的創建。
3. 觀察者模式(Observer Pattern)
- 概念:定義對象間的一種一對多依賴關系,當一個對象(被觀察者)狀態發生改變時,所有依賴它的對象(觀察者)會自動收到通知并更新。
- 核心思想:解耦被觀察者和觀察者,兩者通過抽象接口交互,互不依賴具體實現。
- 適用場景:
- 事件監聽機制(如 GUI 中的按鈕點擊事件,多個組件監聽同一按鈕)。
- 消息通知系統(如公眾號推送,訂閱者接收消息)。
- 狀態變化同步(如股票價格更新,多個指標面板實時刷新)。
4. 裝飾器模式(Decorator Pattern)
- 概念:動態地給一個對象添加額外的職責,同時不改變其原有的結構。相比繼承,更靈活地擴展功能。
- 核心思想:通過創建裝飾器類包裹原對象,裝飾器實現與原對象相同的接口,并在調用原對象方法前后添加新功能。
- 適用場景:
- 功能需要動態擴展且可組合的場景,如 IO 流(BufferedInputStream 裝飾 FileInputStream,增加緩沖功能)。
- 避免使用多層繼承導致的類爆炸,如給咖啡添加不同配料(牛奶、糖),每種配料作為一個裝飾器。
5. 適配器模式(Adapter Pattern)
- 概念:將一個類的接口轉換成客戶端期望的另一個接口,使原本因接口不兼容而無法一起工作的類能夠協同工作。
- 核心思想:創建適配器類,實現目標接口,并持有被適配類的實例,在適配器方法中調用被適配類的對應方法。
- 適用場景:
- 集成第三方組件,其接口與本地系統接口不匹配時(如第三方支付接口適配本地支付服務)。
- 舊系統改造,需復用舊類但接口不符合新需求時。
- 分類:
- 類適配器:通過繼承被適配類實現適配(單繼承限制,較少使用)。
- 對象適配器:通過持有被適配類實例實現適配(更靈活,常用)。
6. 代理模式(Proxy Pattern)
- 概念:為其他對象提供一種代理,以控制對原對象的訪問。代理對象在原對象和客戶端之間起到中介作用。
- 核心思想:代理類與原對象實現同一接口,客戶端通過代理類訪問原對象,代理類可在調用原對象方法前后添加額外邏輯。
- 適用場景:
- 遠程代理:為遠程對象(如分布式服務)提供本地代理,屏蔽網絡通信細節。
- 安全代理:控制對敏感對象的訪問(如權限校驗)。
- 延遲加載:在真正需要時才創建 heavy 資源(如大圖片加載前的代理)。
- AOP(面向切面編程):如 Spring 的事務管理,通過代理在方法執行前后添加事務控制邏輯。
7. 模板方法模式(Template Method Pattern)
- 概念:定義一個操作中的算法骨架,將某些步驟延遲到子類中實現。子類可重寫具體步驟,但不改變算法的整體結構。
- 核心思想:父類中定義模板方法(包含算法骨架),并將可變步驟聲明為抽象方法,由子類實現。
- 適用場景:
- 多個子類有共同的操作流程,但部分步驟實現不同,如數據庫訪問(連接、執行 SQL、關閉連接的流程固定,SQL 語句和結果處理可變)。
- 框架設計中,定義核心流程,允許用戶自定義部分邏輯(如 Servlet 的 doGet/doPost 方法,由框架控制調用時機)。
8. 策略模式(Strategy Pattern)
- 概念:定義一系列算法,將每個算法封裝起來并使它們可相互替換,讓算法的變化獨立于使用算法的客戶端。
- 核心思想:創建策略接口,不同算法實現該接口;客戶端持有策略接口的引用,可動態切換不同的策略實現。
- 適用場景:
- 存在多種類似的算法,且需要根據場景動態選擇時(如排序算法,可根據數據量選擇冒泡、快排、歸并等)。
- 避免使用多重條件判斷(if-else/switch),如電商平臺的折扣策略(新用戶折扣、會員折扣、節日折扣等)。
9. 迭代器模式(Iterator Pattern)
- 概念:提供一種方法順序訪問聚合對象中的元素,而無需暴露聚合對象的內部結構。
- 核心思想:定義迭代器接口(包含 hasNext、next 等方法),聚合對象提供獲取迭代器的方法,客戶端通過迭代器遍歷元素。
- 適用場景:
- 需統一遍歷不同聚合結構(如數組、鏈表、集合)的場景,如 Java 中的 Iterator 接口,可遍歷 List、Set 等不同集合。
- 避免聚合對象暴露內部實現,如自定義容器類,通過迭代器提供安全的遍歷方式。
10. 命令模式(Command Pattern)
- 概念:將一個請求封裝為一個對象,使請求的發送者和接收者解耦。發送者只需發送命令對象,無需知道接收者如何處理。
- 核心思想:創建命令接口(包含執行方法),具體命令類實現接口并持有接收者引用;發送者持有命令對象,通過調用命令的執行方法完成請求。
- 適用場景:
- 需記錄、撤銷、重做請求的場景(如文本編輯器的撤銷操作,每個編輯命令可被保存和反向執行)。
- 事件驅動系統(如 GUI 中的按鈕點擊,按鈕持有命令對象,點擊時執行命令)。
- 批量任務處理(如任務隊列,每個任務封裝為命令,依次執行)。