後端面試題總整理

Last updated: Apr 3, 2026

後端面試題總整理

目錄


1. 資料庫相關

1.1 資料庫隔離級別 (Transaction Isolation Levels)

ACID 特性

資料庫事務需滿足 ACID 特性:

  • Atomicity (原子性):事務中的所有操作要麼全部完成,要麼全部不完成
  • Consistency (一致性):事務執行前後,資料庫都保持一致性狀態
  • Isolation (隔離性):並行執行的事務之間互不干擾
  • Durability (持久性):事務提交後,對資料庫的改變是永久性的

四種隔離級別

從最寬鬆到最嚴格:Read Uncommitted → Read Committed → Repeatable Read → Serializable

隔離級別 Dirty Read
(髒讀)
Non-repeatable Read
(不可重複讀)
Phantom Read
(幻讀)
說明
Read Uncommitted ✅ 可能發生 ✅ 可能發生 ✅ 可能發生 可以讀取其他事務尚未提交的資料,最不安全但速度最快
Read Committed ❌ 已避免 ✅ 可能發生 ✅ 可能發生 只能讀取已提交的資料 (PostgreSQL 預設)
Repeatable Read ❌ 已避免 ❌ 已避免 ✅ 可能發生 同一事務中相同查詢會得到相同結果 (MySQL InnoDB 預設)
Serializable ❌ 已避免 ❌ 已避免 ❌ 已避免 最嚴格的隔離級別,事務串行執行,性能最差

異常現象說明

  • Dirty Read (髒讀):讀取到其他事務尚未提交的資料
  • Non-repeatable Read (不可重複讀):同一事務中兩次讀取同一資料得到不同結果
  • Phantom Read (幻讀):同一事務中重新執行查詢,會出現新增或減少的資料列

不同資料庫的預設隔離級別

  • MySQL InnoDB: Repeatable Read
  • PostgreSQL: Read Committed

設定隔離級別 (MySQL)

-- 設定 Session 級別
SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;
START TRANSACTION;
-- 執行查詢
COMMIT;

-- 設定全域級別
SET GLOBAL TRANSACTION ISOLATION LEVEL READ COMMITTED;

-- 查看當前隔離級別
SELECT @@session.transaction_isolation;  -- Session 級別
SELECT @@global.transaction_isolation;   -- 全域級別

使用場景建議

使用情境 建議的隔離級別
分析報表 / 資料查詢 Read Committed
高並發服務 Read Committed / Repeatable Read + 重試機制
金融交易 (如銀行轉帳) Repeatable Read / Serializable
庫存系統 / 訂票系統 Serializable 或加鎖

1.2 MySQL 鎖機制

鎖的分類

MySQL 中的鎖主要分為三個層級:

  1. 全域鎖 (Global Lock)
  2. 表級鎖 (Table-Level Lock)
  3. 行級鎖 (Row-Level Lock)

1.2.1 全域鎖

  • 使用場景:資料庫備份
  • 效果:資料庫中所有表變為唯讀狀態
-- 加全域鎖
FLUSH TABLES WITH READ LOCK;

-- 解鎖
UNLOCK TABLES;

1.2.2 表級鎖

類型

  • 表鎖 (Table Lock)
  • 元數據鎖 (Metadata Lock, MDL)
  • 意向鎖 (Intention Lock)
  • AUTO-INC 鎖

表鎖特性

  • 讀讀共享、讀寫互斥、寫寫互斥
  • 粒度大,開銷小,不會產生死鎖
  • 併發性能較差,適合 MyISAM 引擎

表鎖類型

鎖類型 說明
讀鎖 持有讀鎖可以讀表但不能寫表;允許多個執行緒持有讀鎖;其他執行緒加寫鎖會被阻塞
寫鎖 持有寫鎖的執行緒可讀可寫;只有持有寫鎖的執行緒能訪問該表,其他執行緒被阻塞
-- 加讀鎖
LOCK TABLES authors READ;

-- 加寫鎖
LOCK TABLES comment WRITE;

-- 解鎖當前 Session 所有表鎖
UNLOCK TABLES;

意向鎖

  • 在使用 InnoDB 引擎的表中對某些記錄加行級鎖之前,需要先在表級加意向鎖
  • 意向鎖的目的是為了快速判斷表裡是否有記錄被加鎖
  • 意向鎖之間不會衝突,只會與表級的共享鎖和獨佔鎖衝突

1.2.3 行級鎖

類型

  • Record Lock (記錄鎖)
  • Gap Lock (間隙鎖)
  • Next-Key Lock (記錄鎖 + 間隙鎖)

特性

  • InnoDB 支援行級鎖,MyISAM 不支援
  • 讀讀共享、讀寫互斥、寫寫互斥
  • 粒度小,開銷大,可能產生死鎖
  • 併發性能好

Record Lock (記錄鎖)

-- 對讀取的記錄加共享鎖 (S鎖)
SELECT * FROM authors WHERE id = 1 LOCK IN SHARE MODE;

-- 對讀取的記錄加獨佔鎖 (X鎖)
SELECT * FROM authors WHERE id = 1 FOR UPDATE;

Gap Lock (間隙鎖)

  • 鎖定兩個索引之間的間隙,防止其他事務在此範圍內插入或修改記錄
  • 可以避免幻讀現象

Next-Key Lock

  • 概念上等於 Record Lock + Gap Lock
  • 建立一個前開後閉的索引間隙,避免其他事務在此間隙插入資料

1.2.4 死鎖 (Deadlock)

根本原因:兩個或多個事務加鎖順序不一致

處理方式

  • 設定鎖等待超時時間
  • 使用死鎖檢測機制
  • 確保事務按相同順序獲取鎖

InnoDB vs MyISAM

特性 InnoDB MyISAM
預設版本 MySQL 5.5+ MySQL 5.1 及之前
事務支援 ✅ 支援 ❌ 不支援
行級鎖 ✅ 支援 ❌ 不支援,僅支援表鎖
外鍵 ✅ 支援 ❌ 不支援
崩潰恢復 ✅ 自動恢復 ❌ 不支援
儲存空間 較大 較小,可壓縮
適用場景 高並發、事務處理 大量查詢、篩選

1.3 SQL 查詢優化與執行計畫

什麼是執行計畫?

執行計畫 (Execution Plan) 是資料庫引擎執行 SQL 查詢的具體步驟,包括:

  • JOIN 順序
  • 索引使用情況
  • 表掃描方式
  • 預估成本

取得執行計畫

-- MySQL / PostgreSQL
EXPLAIN SELECT * FROM orders WHERE status = 'shipped';

-- PostgreSQL - 包含實際執行時間
EXPLAIN ANALYZE SELECT * FROM orders WHERE status = 'shipped';

-- MySQL - JSON 格式,更詳細
EXPLAIN FORMAT=JSON SELECT * FROM orders WHERE status = 'shipped';

執行計畫關鍵指標

指標 說明 理想值 / 需注意的問題
type 訪問方式 (ALL, index, ref, eq_ref) ✅ 優先選擇 ref 或 eq_ref,避免 ALL (全表掃描)
key 使用的索引 ❗ 若為 NULL,表示未使用索引
rows 預估掃描的行數 ✅ 越低越好
filtered 傳遞到下一階段的行數百分比 ❗ 低於 30% 表示過濾效果差
Extra 額外資訊 ❗ “Using temporary” 或 “filesort” 表示性能差

優化步驟

1. 識別昂貴的操作

  • 全表掃描 (Full Table Scan) 而非索引掃描
  • 大數據集上的嵌套迴圈連接 (Nested Loop Join)
  • 排序操作 (Sort) 或 Hash 聚合
  • 使用臨時表或磁碟

2. 檢查索引使用

  • 確保 WHERE 條件和 JOIN 使用索引欄位
  • 複合索引的欄位順序要與查詢條件匹配
  • 添加缺失的索引
  • 移除未使用或低效的索引

3. 避免 SELECT *

  • 只查詢需要的欄位,減少 I/O

4. 分析 JOIN 順序

  • 將過濾條件推到 JOIN 之前執行
  • 使用 CTE (Common Table Expression) 隔離昂貴的分支

5. 重構 WHERE/HAVING 子句

  • 確保謂詞是 Sargable (Search Argument-able)
  • 避免 WHERE YEAR(date_col) = 2023,改用 WHERE date_col BETWEEN '2023-01-01' AND '2023-12-31'
  • 使用顯式 JOIN 而非相關子查詢

6. 檢查 GROUP BY / ORDER BY

  • 為 GROUP BY、ORDER BY 使用的欄位建立索引
  • 添加覆蓋索引 (Covering Index)

優化範例

問題查詢

SELECT o.id, o.order_date, c.name
FROM orders o
JOIN customers c ON o.customer_id = c.id
WHERE o.status = 'shipped' AND c.region = 'US'
ORDER BY o.order_date DESC
LIMIT 100;

執行 EXPLAIN 發現問題

  • customers 表進行全表掃描 (type=ALL, key=NULL)
  • orders 表使用 filesort

優化方案

-- 添加索引
CREATE INDEX idx_customers_region ON customers(region);
CREATE INDEX idx_orders_status_date ON orders(status, order_date DESC);

-- 可選:覆蓋索引
CREATE INDEX idx_orders_covering ON orders(status, order_date DESC, customer_id, id);

常見問題與修復

症狀 修復方案
type=ALL 為 WHERE / JOIN 欄位添加索引
Extra 有 filesort 為 ORDER BY 欄位添加索引
Extra 有 Using temporary 重寫查詢或添加索引
rows 數量過高 重寫邏輯或拆分為子查詢
filtered < 30% 改進 WHERE 條件或在應用層預過濾

2. 快取相關

2.1 快取三大問題

快取在高並發系統中扮演重要角色,但也會遇到三大經典問題:

  1. 快取穿透 (Cache Penetration)
  2. 快取擊穿 (Hotspot Invalid)
  3. 快取雪崩 (Cache Avalanche)

2.1.1 快取雪崩 (Cache Avalanche)

現象

  • 在某個時刻,所有的快取同時過期或 Redis 服務失效
  • 導致大量請求直接打到資料庫
  • 當流量巨大時,資料庫可能被打掛

解決方案

  • 隨機過期時間:為每個快取 key 設定隨機的過期時間,避免同時失效
  • 互斥鎖:快取不存在時使用鎖機制,只允許一個執行緒查詢資料庫
  • 後台定期更新快取:在快取過期前自動更新
  • 服務熔斷或請求限流:避免資料庫過載
  • Redis 叢集:提高可用性

2.1.2 快取擊穿 (Hotspot Invalid)

現象

  • 某個熱門的快取 key 過期
  • 高並發集中在此 key,流量直接打到資料庫

解決方案

  • 將熱點 key 設為永不過期
  • 互斥鎖:在應用層加鎖,確保只有一個執行緒查詢資料庫並更新快取
    • 缺點:會降低系統吞吐量,阻礙其他執行緒

2.1.3 快取穿透 (Cache Penetration)

現象

  • 客戶端請求的資料既不存在於快取,也不存在於資料庫
  • 每次請求都會穿過快取直接打到資料庫
  • 例如:查詢 id=-1 的資料,但資料庫從 id=1 開始

解決方案

  • 過濾非法請求:在應用層驗證請求參數的合法性
  • 快取空值或預設值:將查詢不到的資料也快取起來(設定較短的過期時間)
  • 布隆過濾器 (Bloom Filter):判斷請求的 key 是否可能存在於資料集中
    • 存在則查詢 Redis
    • 不存在則直接返回預設訊息

2.2 Redis 分散式鎖

在分散式系統中,Redis 鎖用於確保同一時刻只有一個程序能訪問特定資源,防止競態條件 (race condition) 和資料不一致。

核心概念

  1. 原子性:Redis 支援原子操作,鎖的獲取和釋放不可中斷
  2. 過期時間:設定 TTL (Time To Live),避免死鎖
  3. 分散式:可跨多個實例或微服務使用

2.2.1 SETNX (Set if Not Exists)

最簡單的鎖實現方式:

import redis
r = redis.Redis()

lock_key = "lock:my_resource"

# 嘗試獲取鎖
if r.setnx(lock_key, 1):
    try:
        # 取得鎖,執行臨界區程式碼
        pass
    finally:
        # 釋放鎖
        r.delete(lock_key)
else:
    # 未取得鎖,稍後重試
    pass

問題:如果程式在釋放鎖之前崩潰,會造成死鎖

2.2.2 SET with Expiration (推薦)

使用 SET 命令搭配 NXEX 選項,原子地設定鎖和過期時間:

import redis
r = redis.Redis()

lock_key = "lock:my_resource"
lock_timeout = 10  # 鎖在 10 秒後自動過期

# 嘗試獲取鎖
if r.set(lock_key, 1, nx=True, ex=lock_timeout):
    try:
        # 取得鎖,執行臨界區程式碼
        pass
    finally:
        # 釋放鎖
        r.delete(lock_key)
else:
    # 未取得鎖,稍後重試
    pass

2.2.3 Redlock 演算法

用於多個 Redis 實例的分散式鎖,提供容錯能力:

from redis import Redis
from redlock import Redlock

# 連接多個 Redis 實例
redis_instances = [Redis(host='localhost', port=6379) for _ in range(5)]
dlock = Redlock(redis_instances)

lock_key = "lock:my_resource"
lock = dlock.lock(lock_key, 10000)  # 鎖在 10 秒後過期

if lock:
    try:
        # 執行任務
        pass
    finally:
        dlock.unlock(lock)
else:
    # 未取得鎖
    pass

工作原理

  • 嘗試在多個 Redis 節點 (如 5 個) 上獲取鎖
  • 當大多數節點 (如 3 個) 成功獲取鎖時,才認為鎖獲取成功
  • 提高可靠性,即使部分節點失效也能正常工作

最佳實踐

  1. 設定合理的過期時間:避免死鎖,但要足夠完成任務
  2. 確保釋放鎖:使用 try-finally 確保鎖被釋放
  3. 重試邏輯:使用指數退避策略 (exponential backoff)
  4. 原子性和隔離性:確保被鎖保護的操作是原子的
  5. 超時和失敗處理:考慮使用 Lua 腳本確保鎖和操作的原子性

2.3 Spring Boot + Redis 快取實踐

快取模式

模式 說明
Read-Through 應用先查快取,若未命中則查資料庫,並填充快取
Write-Through 寫入時同時更新快取和資料庫
Write-Behind (Async) 先寫快取,稍後非同步寫入資料庫(風險較高)
Cache-Aside (Lazy Load) 應用讀取快取,未命中時查資料庫並更新快取(最常見)

Spring Boot 快取實現 (Cache-Aside)

@Service
public class UserService {

    @Autowired
    private UserRepository userRepository;

    // 查詢時先查快取,未命中則查資料庫並快取結果
    @Cacheable(value = "user", key = "#userId")
    public User getUserById(Long userId) {
        return userRepository.findById(userId).orElseThrow();
    }

    // 更新資料後清除快取
    @CacheEvict(value = "user", key = "#user.id")
    public void updateUser(User user) {
        userRepository.save(user);
    }

    // 更新資料並同時更新快取
    @CachePut(value = "user", key = "#user.id")
    public User saveUser(User user) {
        return userRepository.save(user);
    }
}

快取一致性策略

策略 實現方式 優點 缺點
寫入時清除 (@CacheEvict) 更新資料庫後刪除快取項 簡單、一致性好 下次讀取需重新載入快取
寫入時更新 (@CachePut) 同時寫入資料庫和快取 讀取快速 可能部分失敗導致不一致
訊息佇列 (如 Kafka) 透過事件流非同步同步 解耦、可擴展 複雜度高、可能有延遲
雙寫事務 事務性地更新兩者 可靠 回滾管理困難

常見問題與解決方案

1. 快取風暴 (Cache Stampede)

問題:快取過期時大量請求同時打到資料庫

解決方案:

  • 使用隨機化的 TTL
  • 使用互斥鎖 (SETNX)
  • 使用 Redisson 或 Caffeine 等函式庫

2. 快取不一致

問題:資料庫已更新,但快取未更新

解決方案:

  • 使用 @CacheEvict@CachePut
  • 避免並行更新,使用鎖或防抖

3. 冷啟動 (Cold Start)

問題:重啟後快取為空

解決方案:

  • 啟動時預熱快取 (可選的批次載入器)

4. 序列化格式

Redis 需要快速的序列化,建議使用 JSONString 而非 Java 原生序列化:

@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
    RedisTemplate<String, Object> template = new RedisTemplate<>();
    template.setConnectionFactory(factory);
    template.setValueSerializer(new Jackson2JsonRedisSerializer<>(Object.class));
    return template;
}

3. 分散式系統

3.1 全域事務 (Global Transaction)

在分散式系統中,全域事務用於確保跨多個服務或資料庫的操作具有 ACID 特性。

3.1.1 兩階段提交 (2PC - Two-Phase Commit)

適用場景:強一致性的分散式資料庫

工作流程

  • 階段 1 - 準備 (Prepare):協調者詢問所有參與節點是否準備好提交
  • 階段 2 - 提交 (Commit):如果所有節點回覆「是」,協調者發出提交指令;否則回滾

優點

  • ✅ 強一致性 (ACID)
  • ✅ 中央協調

缺點

  • ❌ 阻塞協定,可能造成鎖競爭
  • ❌ 單點故障(協調者失效)
  • ❌ 在網路分區時可用性差
  • ❌ 不適合雲原生 / 微服務架構

3.1.2 Saga 模式 (推薦用於微服務)

適用場景:現代微服務和事件驅動架構

工作原理

  • 一系列本地事務,每個服務更新自己的資料並發布事件觸發下一步
  • 如果某一步失敗,執行補償事務 (Compensating Transaction) 回滾之前的步驟

範例(訂票流程)

  • 步驟 1:預訂飯店 → 步驟 2:預訂機票 → 步驟 3:付款
  • 如果步驟 2 失敗,執行補償動作取消步驟 1 的飯店預訂

實現方式

方式 說明 範例工具
編排式 Saga (Orchestration) 中央服務控制流程 Netflix Conductor, Temporal
編舞式 Saga (Choreography) 服務對事件做出反應,自行管理轉換 Apache Kafka + EventBridge

優點

  • ✅ 最終一致性
  • ✅ 高可用性和回應性
  • ✅ 適合鬆散耦合的系統

缺點

  • ❌ 補償邏輯複雜
  • ❌ 難以除錯和追蹤

注意事項

  • 確保每個服務的操作具有冪等性 (idempotency)
  • 使用相關 ID (correlation ID) 進行跨系統追蹤
  • 應用超時和斷路器 (circuit breaker) 提高韌性

3.1.3 TCC (Try-Confirm-Cancel)

適用場景:資源可以預留的系統(如庫存、訂票)

工作流程

  • Try:預留資源(如鎖定庫存)
  • Confirm:確認操作,提交事務
  • Cancel:取消操作,釋放資源

優點

  • ✅ 對操作有細粒度控制
  • ✅ 顯式的補償模型

缺點

  • ❌ 所有服務必須實現 try/confirm/cancel 邏輯
  • ❌ 實現複雜度高

3.1.4 最終一致性 + 事件驅動架構

適用場景:可以容忍一致性延遲的系統

優點

  • ✅ 非同步
  • ✅ 高度可擴展
  • ✅ 服務解耦

缺點

  • ❌ 失敗處理複雜
  • ❌ 需要冪等操作
  • ❌ 難以除錯和追蹤

3.1.5 可靠訊息 + Outbox 模式

適用場景:確保事件/訊息以事務方式傳遞

優點

  • ✅ 確保資料庫和訊息的原子性
  • ✅ 服務解耦

缺點

  • ❌ 需要額外基礎設施 (Kafka, Debezium 等)
  • ❌ 運維和基礎設施開銷較高

模式比較表

模式 一致性 可擴展性 複雜度 失敗處理 理想使用場景
Saga 最終一致 ✅ 高 ⚠️ 中等 需要補償邏輯 長時間運行、可分解的業務流程
2PC 強一致 (ACID) ❌ 低 ⚠️ 高 協調者是瓶頸 需要嚴格一致性的金融操作
TCC 強一致 ⚠️ 中等 ❌ 高 顯式取消邏輯 訂票系統、可預留資源
最終一致性 (EDA) 最終一致 ✅ 非常高 ⚠️ 中等 重試邏輯、冪等性 容忍延遲的微服務
Outbox 模式 最終一致 ✅ 高 ⚠️ 中等 可靠訊息傳遞 確保訊息和資料庫一致性

選擇建議

需求 最佳選擇
需要強一致性 2PC, TCC
高可擴展性 Saga, Outbox
需要資源預留 TCC
容錯能力 Saga, Outbox
簡單性 > 一致性 手動對帳

3.2 ZooKeeper 分散式鎖

ZooKeeper 是一個分散式協調服務,常用於實現分散式鎖、配置管理、服務發現等功能。

基本概念

  • 使用臨時順序節點 (Ephemeral Sequential Node) 實現鎖
  • 客戶端創建節點,序號最小的獲得鎖
  • 其他客戶端監聽前一個節點,等待鎖釋放

優點

  • 支援阻塞式鎖
  • 自動釋放(客戶端斷線時自動刪除臨時節點)
  • 避免羊群效應 (thundering herd)

缺點

  • 需要部署和維護 ZooKeeper 叢集
  • 性能不如 Redis

4. 網路與協定

4.1 TCP vs UDP

TCP (Transmission Control Protocol)

特性

  • 面向連接的協定
  • 提供可靠的資料傳輸
  • 錯誤檢查和重傳功能
  • 確保資料不會丟失

三次握手 (Three-Way Handshake)

  1. Client 向 Server 發送連接請求封包 (SYN)
  2. Server 接收並確認,回傳確認封包 (SYN-ACK)
  3. Client 收到後再回傳確認封包 (ACK),連接建立

特性

  • 建立連接後,所有封包都會加上序號
  • 保證完整性、順序性、重傳處理
  • 使用滑動窗口提升傳輸效率

適用場景

  • 網頁瀏覽 (HTTP/HTTPS)
  • 電子郵件 (SMTP, IMAP)
  • 檔案傳輸 (FTP)
  • 需要資料完整性的應用

UDP (User Datagram Protocol)

特性

  • 面向非連接的協定
  • 不保證資料傳輸的可靠性
  • 傳輸速度快,通訊引擎簡單
  • 無需確認機制,表頭資料較少

適用場景

  • 串流服務 (影片、音訊)
  • 線上遊戲
  • DNS 查詢
  • 即時通訊

TCP vs UDP 比較

特性 TCP UDP
連接 面向連接 無連接
可靠性 可靠 不可靠
速度 較慢 較快
順序 保證順序 不保證順序
錯誤檢查 有但簡單
重傳 支援 不支援
表頭大小 較大 (20 bytes) 較小 (8 bytes)
使用場景 需要完整性的應用 需要速度的應用

4.2 HTTP 相關

HTTP, TCP, Socket 關係

  • TCP/IP:傳輸控制協定/網路協定,指一系列協定族
  • HTTP:基於 TCP 的應用層協定,用於 Web 伺服器與瀏覽器之間傳輸超文本
  • Socket:TCP/IP 網路的 API,隱藏了複雜的 TCP/IP 協定細節

關係總結

  • 需要 IP 協定來連接網路
  • TCP 是一種安全傳輸資料的機制
  • HTTP 使用 TCP 協定來傳輸資料
  • Socket 可以用來建立 TCP 連接

HTTP 長連接與短連接

短連接(HTTP/1.0 預設):

  • 瀏覽器和伺服器每進行一次 HTTP 操作,就建立一次連接
  • 任務結束後立即中斷連接
  • 每個資源(HTML、CSS、JS、圖片)都需要獨立的連接

長連接(HTTP/1.1 預設):

  • 使用 Keep-Alive 保持連接
  • 網頁開啟完成後,TCP 連接不會關閉
  • 客戶端再次訪問相同伺服器時,繼續使用已建立的連接
  • 有保持時間限制,可在伺服器配置中設定

實質:HTTP 協定的長短連接,實質上是 TCP 協定的長短連接

HTTP 304 (Not Modified)

  • 含義:已讀取過的資源,由瀏覽器快取 (cache) 中讀取
  • 作用:節省頻寬,加快頁面載入速度
  • 觸發條件
    • 客戶端發送帶有 If-Modified-SinceIf-None-Match 的請求
    • 伺服器判斷資源未修改,返回 304 狀態碼
    • 瀏覽器使用本地快取的版本

4.3 JWT (JSON Web Token)

什麼是 JWT?

JWT 全名為 JSON Web Token,是一種基於 JSON 的開放標準(RFC 7519)。

結構:由三部分組成,以 . 分隔

  • Header:包含 token 類型和加密演算法
  • Payload:包含聲明 (claims),如用戶 ID、過期時間等
  • Signature:使用 Header 中指定的演算法 (HMAC、RSA、ECDSA) 對 Header 和 Payload 進行簽名

範例

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

JWT 的優點

  • ✅ 無狀態:伺服器不需要存儲 session
  • ✅ 可擴展:適合分散式系統和微服務
  • ✅ 跨域支援:可在多個網域間使用
  • ✅ 自包含:Token 本身包含用戶資訊

JWT 的缺點

  • Cross-site 攻擊:可能遭受 XSS 攻擊
  • Local storage 不安全:存儲在 Local Storage 中容易被竊取
  • 無法單獨銷毀:Token 在過期前無法撤銷,除非使用黑名單機制

使用時機

  • Token 生命期較短:讓擁有此 Token 的用戶能在時間內完成特定操作(如登入、下載檔案)
  • Token 僅單次使用:任何 Token 只用於一次後就會被拋棄,不存在於任何持久化狀態

安全建議

  • 使用 HTTPS 傳輸 JWT
  • 設定合理的過期時間
  • 敏感資訊不要放在 Payload 中(因為 Base64 可解碼)
  • 考慮使用 Refresh Token 機制
  • 實現 Token 黑名單(如 Redis)用於撤銷

5. 應用架構

5.1 Middleware (中介層)

什麼是 Middleware?

Middleware 是位於傳入請求和核心應用邏輯之間的程式碼層,用於攔截、修改或處理請求和回應,而不改變主要業務邏輯。

流程

Client → Middleware → App → Middleware → Response → Client

為什麼使用 Middleware?

Middleware 讓你可以將常見關注點從核心應用邏輯中解耦:

使用案例 說明
認證 (Authentication) 驗證 token、session、API key
日誌 (Logging) 記錄請求/回應詳情
速率限制 (Rate Limiting) 基於 IP/用戶防止濫用
CORS / Headers 添加/修改 header 處理跨域請求
錯誤處理 (Error Handling) 捕獲並回應異常
請求解析/驗證 確保資料乾淨且安全

Flask 範例

from flask import Flask, request

app = Flask(__name__)

# 請求前的日誌記錄
@app.before_request
def log_request():
    print(f"[{request.method}] {request.path}")

# 請求後添加自訂 header
@app.after_request
def add_custom_header(response):
    response.headers['X-App-Version'] = '1.0.0'
    return response

@app.route("/hello")
def hello():
    return "Hello, World!"

Flask 認證 Middleware 範例

from flask import Flask, request, jsonify, abort

app = Flask(__name__)

# 模擬的 token 儲存(生產環境應使用資料庫或外部認證服務)
VALID_API_TOKENS = {
    "token123": "user_a",
    "token456": "user_b"
}

@app.before_request
def authenticate():
    # 定義不需要認證的公開路徑
    public_paths = ['/health', '/login']

    if request.path in public_paths:
        return  # 跳過認證檢查

    auth_header = request.headers.get("Authorization")
    if not auth_header or not auth_header.startswith("Bearer "):
        abort(401, description="Missing or malformed Authorization header")

    token = auth_header.split("Bearer ")[1]
    user = VALID_API_TOKENS.get(token)
    if not user:
        abort(401, description="Invalid or expired token")

    # 將用戶資訊附加到全域 request context
    request.user = user

@app.route("/health")
def health_check():
    return {"status": "ok"}

@app.route("/protected")
def protected_resource():
    return {"message": f"Hello, {request.user}. You accessed a protected route."}

if __name__ == "__main__":
    app.run(debug=True)

Express (Node.js) 範例

const express = require('express');
const app = express();

// 日誌 middleware
app.use((req, res, next) => {
  console.log(`${req.method} ${req.url}`);
  next(); // 傳遞控制權給下一個 middleware 或路由
});

// 認證 middleware
app.use((req, res, next) => {
  if (!req.headers['authorization']) {
    return res.status(401).send('Unauthorized');
  }
  next();
});

app.get('/api/data', (req, res) => {
  res.send({ message: 'Protected data' });
});

核心概念

  • Middleware 通常是無狀態的,獨立處理每個請求
  • Middleware 可以短路請求(如在到達路由前返回 401)
  • 通常是堆疊或組合的,形成管道
  • 可以是自訂的或來自框架/函式庫

各框架的 Middleware

框架 Middleware 術語
Express (Node.js) app.use() handlers
Flask (Python) @before_request, WSGI middleware
Django MIDDLEWARE setting in settings.py
ASP.NET Core Middleware pipeline with UseXXX()
Ruby on Rails Rack middleware

5.2 ORM (Object-Relational Mapping)

什麼是 ORM?

ORM 是一種程式設計技術,用於在關聯式資料庫和物件導向程式語言之間建立映射關係。

核心概念

  • 將資料庫表映射為類別 (Class)
  • 將表中的行映射為物件實例 (Object Instance)
  • 將欄位映射為物件屬性 (Attribute)

優點

  • ✅ 減少 SQL 程式碼,提高開發效率
  • ✅ 資料庫無關性,易於切換資料庫
  • ✅ 型別安全,減少 SQL 注入風險
  • ✅ 易於維護和測試

缺點

  • ❌ 性能可能不如原生 SQL(特別是複雜查詢)
  • ❌ 學習曲線(需要理解 ORM 框架)
  • ❌ 可能產生低效的 SQL(N+1 問題)

常見 ORM 框架

  • Java: Hibernate, JPA, MyBatis
  • Python: SQLAlchemy, Django ORM
  • Node.js: Sequelize, TypeORM, Prisma
  • .NET: Entity Framework

5.3 MVC vs MVVM

MVC (Model-View-Controller)

架構

  • Model:資料和業務邏輯
  • View:使用者介面
  • Controller:處理用戶輸入,協調 Model 和 View

流程

User → Controller → Model → Controller → View → User

優點

  • ✅ 職責分離
  • ✅ 易於測試
  • ✅ 適合傳統 Web 應用

缺點

  • ❌ Controller 可能變得臃腫
  • ❌ View 和 Model 可能存在耦合

MVVM (Model-View-ViewModel)

架構

  • Model:資料和業務邏輯
  • View:使用者介面
  • ViewModel:View 的抽象,處理 View 的展示邏輯和狀態

流程

User → View ↔ ViewModel ↔ Model

特點

  • View 和 ViewModel 之間通過資料綁定 (Data Binding) 自動同步

優點

  • ✅ View 和業務邏輯完全分離
  • ✅ 易於單元測試(ViewModel 可獨立測試)
  • ✅ 適合現代前端框架(Vue.js, Angular, React)

缺點

  • ❌ 學習曲線較陡
  • ❌ 過度使用資料綁定可能影響性能

6. 常見面試題

6.1 如何建立全域事務?

回答要點

  • 說明 2PC 適用於強一致性需求
  • 說明 Saga 適用於微服務架構
  • 提到 TCC、Outbox 模式等替代方案
  • 強調需考慮一致性、可用性、性能的權衡

詳見 3.1 全域事務


6.2 建立分散式 Redis 鎖時的考量?

回答要點

  1. 原子性:使用 SET key value NX EX timeout 確保鎖的獲取和過期時間設定是原子的
  2. 過期時間:設定合理的 TTL,避免死鎖
  3. 鎖的釋放:確保在 finally 塊中釋放鎖
  4. 重試策略:使用指數退避 (exponential backoff)
  5. 唯一標識:使用 UUID 作為鎖的值,釋放時驗證是否為自己持有的鎖
  6. Redlock 演算法:多 Redis 實例場景下使用 Redlock 提高可靠性

詳見 2.2 Redis 分散式鎖


6.3 如何優化 SQL 查詢?如何使用執行計畫?

回答要點

  1. 使用 EXPLAINEXPLAIN ANALYZE 查看執行計畫
  2. 識別昂貴的操作:全表掃描、filesort、using temporary
  3. 確保 WHERE 和 JOIN 使用索引
  4. 避免 SELECT *,只查詢需要的欄位
  5. 分析 JOIN 順序,將過濾條件前置
  6. 為 ORDER BY 和 GROUP BY 欄位建立索引

詳見 1.3 SQL 查詢優化與執行計畫


6.4 Spring Boot Cloud 主要組件有哪些?

常見組件

  • Eureka:服務註冊與發現
  • Ribbon:客戶端負載平衡
  • Feign:聲明式 REST 客戶端
  • Hystrix:斷路器,實現服務降級和熔斷
  • Zuul / Gateway:API 閘道
  • Config Server:集中式配置管理
  • Sleuth + Zipkin:分散式追蹤

6.5 如何確保資料庫資料一致性?

回答要點

  1. 事務隔離級別:根據需求選擇合適的隔離級別
  2. 樂觀鎖 vs 悲觀鎖
    • 悲觀鎖:SELECT ... FOR UPDATE
    • 樂觀鎖:版本號或時間戳
  3. 分散式事務:使用 2PC、Saga、TCC 等模式
  4. 冪等性:確保操作可重複執行而不影響結果
  5. 補償機制:失敗時執行補償邏輯

詳見 1.1 資料庫隔離級別3.1 全域事務


6.6 如何使用 Redis 快取和資料庫(MySQL),當 Redis 可能失效時?

回答要點

  1. 快取模式:使用 Cache-Aside 模式
  2. 快取一致性
    • 寫入時使用 @CacheEvict 清除快取
    • 或使用 @CachePut 同時更新快取
  3. 快取失效場景
    • 快取穿透:使用布隆過濾器或快取空值
    • 快取擊穿:熱點資料設為永不過期或使用互斥鎖
    • 快取雪崩:使用隨機過期時間或 Redis 叢集
  4. 降級策略:Redis 不可用時直接查詢資料庫

詳見 2.1 快取三大問題2.3 Spring Boot + Redis 快取實踐


6.7 Interface、Class 在編譯時、執行時的使用,以及與 Bytecode 的關係?

回答要點

編譯時 (Compile Time)

  • Interface 定義契約,Class 實現契約
  • 編譯器檢查型別安全、方法簽名等
  • Java 編譯器將 .java 檔案編譯為 .class bytecode 檔案

執行時 (Runtime)

  • JVM 載入 bytecode 並執行
  • 使用多型 (Polymorphism) 時,JVM 根據實際物件類型調用對應方法
  • Interface 在執行時通過虛擬方法表 (Virtual Method Table) 實現動態綁定

與 Bytecode 的關係

  • Interface 編譯後產生 .class 檔案,包含方法簽名但無實現
  • Class 編譯後產生包含實現的 bytecode
  • Bytecode 指令如 invokevirtualinvokeinterface 用於方法調用

參考資料


最後更新日期: 2026-02-14