2025年9月29日 星期一

資料庫設計與優化題庫(適用 MySQL / Laravel)

 

資料庫設計與優化題庫(適用 MySQL / Laravel)

在現代 Web 應用開發中,資料庫設計與優化是資深後端工程師的核心技能,尤其在使用 MySQLLaravel 框架時。本文將提供的題庫擴展為一篇技術文章,分為四大類別。每個問題不僅列出,還附上深入解析、程式碼範例、實務應用建議與潛在陷阱,幫助面試官評估候選人深度,也讓求職者系統化準備。這些題目聚焦於 MySQL 的資料庫原則與 Laravel 的 ORM 整合,涵蓋從基礎設計到高併發優化的實戰議題。


一、資料庫設計與正規化

資料庫設計強調資料完整性與效率,正規化是核心原則,但需權衡效能。這類題目檢視候選人對資料結構的理解。

1. 請說明資料庫正規化的三個主要階段(1NF、2NF、3NF),並舉例說明其目的。

解析

  • 1NF (First Normal Form):確保每個欄位原子性(不可再分),消除重複群組。目的:避免資料重複與更新異常。
    • 範例:將「興趣」欄位從逗號分隔轉為單一值,或使用關聯表。
  • 2NF (Second Normal Form):基於1NF,消除部分依賴(非主鍵欄位僅依賴完整主鍵)。目的:減少冗餘資料。
    • 範例:在訂單表中,若主鍵為 (訂單ID, 產品ID),則產品價格應移至產品表,避免依賴部分主鍵。
  • 3NF (Third Normal Form):基於2NF,消除傳遞依賴(非主鍵欄位不依賴其他非主鍵)。目的:防止資料不一致。
    • 範例:在員工表中,若部門地址依賴部門名稱,則移至部門表。

實務應用:正規化減少儲存空間與維護成本,但過度可能導致查詢複雜。在 Laravel 中,使用 migration 定義正規化結構。

潛在陷阱:忽略 BCNF 或更高形式,可能在複雜關聯中產生異常。

2. 在什麼情況下你會選擇「反正規化」?請舉一個實務案例。

解析

  • 反正規化:有意引入冗餘以提升讀取效能,犧牲寫入一致性。適用於讀多寫少、查詢頻繁的場景,如報告系統或大數據分析。
  • 實務案例:在電商平台,產品表冗餘儲存「類別名稱」(本應在類別表),避免頻繁 JOIN,提升首頁產品列表速度。使用觸發器或 Laravel event 同步更新。

優點與風險:加速查詢,但增加儲存與更新複雜度。監控以避免資料不一致。

3. 如何設計一個支援多語系的產品資料表?

解析

  • 設計:使用翻譯表 (translations) 與多對一關聯。產品表儲存通用欄位,翻譯表儲存語言特定欄位(如名稱、描述),以 locale 作為複合鍵。
  • 考慮:支援 fallback 語言、索引 locale + product_id。

範例結構 (MySQL)

CREATE TABLE products (
    id INT PRIMARY KEY AUTO_INCREMENT,
    sku VARCHAR(50) UNIQUE
);

CREATE TABLE product_translations (
    product_id INT,
    locale VARCHAR(5),
    name VARCHAR(255),
    description TEXT,
    PRIMARY KEY (product_id, locale),
    FOREIGN KEY (product_id) REFERENCES products(id) ON DELETE CASCADE
);

Laravel 實作:使用 Translatable trait 或自訂 scope 查詢當前語言。

4. 請設計一個訂單系統的資料表結構,包含訂單、訂單項目、付款紀錄,並說明關聯方式。

解析

  • 結構:orders (訂單主表)、order_items (訂單項目,一對多)、payments (付款紀錄,一對多)。
  • 關聯:orders.id 作為 order_items.order_id 與 payments.order_id 的外鍵。使用 ON DELETE CASCADE 確保一致性。

範例結構 (MySQL)

CREATE TABLE orders (
    id INT PRIMARY KEY AUTO_INCREMENT,
    user_id INT,
    total DECIMAL(10,2),
    status ENUM('pending', 'paid', 'shipped')
);

CREATE TABLE order_items (
    id INT PRIMARY KEY AUTO_INCREMENT,
    order_id INT,
    product_id INT,
    quantity INT,
    price DECIMAL(10,2),
    FOREIGN KEY (order_id) REFERENCES orders(id) ON DELETE CASCADE
);

CREATE TABLE payments (
    id INT PRIMARY KEY AUTO_INCREMENT,
    order_id INT,
    amount DECIMAL(10,2),
    method VARCHAR(50),
    status ENUM('success', 'failed'),
    FOREIGN KEY (order_id) REFERENCES orders(id) ON DELETE CASCADE
);

實務考量:添加 timestamps、軟刪除。Laravel 中使用 hasMany 關聯。

5. 請說明 Laravel 的 migration 如何支援資料表版本控制與 rollback。

解析

  • Migration:使用 PHP 類別定義 schema 變更,支持 up() 建立、down() 還原。版本控制透過 migrations 表追蹤執行記錄。
  • Rollback:php artisan migrate:rollback 執行 down() 方法,還原最近批次。

範例

class CreateUsersTable extends Migration {
    public function up() {
        Schema::create('users', function (Blueprint $table) {
            $table->id();
            $table->string('name');
            $table->timestamps();
        });
    }
    public function down() {
        Schema::dropIfExists('users');
    }
}

實務:使用 seeders 填充資料,版本控制整合 Git。


二、查詢效能與索引策略

效能優化聚焦索引與查詢分析,這類題目評估候選人對 MySQL 內部機制的理解。

1. 請說明索引的種類(BTREE、HASH、FULLTEXT)與適用場景。

解析

  • BTREE:平衡樹,支援範圍查詢、排序。適用:主鍵、日期欄位 (e.g., WHERE date > '2023-01-01')。
  • HASH:雜湊表,快速等值查詢。不支援範圍。適用:URL 或 ID 查找 (InnoDB 不支援獨立 HASH)。
  • FULLTEXT:全文檢索,支援自然語言搜尋。適用:文章搜尋 (e.g., MATCH AGAINST)。

實務:MySQL 預設 BTREE;FULLTEXT 需要特定引擎如 InnoDB (MySQL 5.6+)。

2. 如何分析慢查詢?請說明 EXPLAIN 的使用方式與常見指標。

解析

  • 分析:啟用 slow_query_log,設定 long_query_time。使用 EXPLAIN 檢視執行計劃。
  • EXPLAIN:EXPLAIN SELECT ... 返回 type (ALL/index/range)、rows (掃描行數)、key (使用索引)、Extra (Using filesort 等)。
  • 常見指標:type=ALL 表示全表掃描;rows 高表示低效;Extra=Using temporary 表示需優化。

範例

EXPLAIN SELECT * FROM users WHERE email = 'test@example.com';

實務:整合 pt-query-digest 工具分析 log。

3. 在 Laravel 中如何避免 N+1 問題?請舉例說明。

解析

  • N+1:循環查詢關聯資料導致額外查詢。解決:使用 eager loading (with()) 預載關聯。

範例

// N+1 問題
$users = User::all();
foreach ($users as $user) {
    echo $user->posts->count();  // 每次 loop 查詢 posts
}

// 解決
$users = User::with('posts')->get();
foreach ($users as $user) {
    echo $user->posts->count();  // 單一查詢預載
}

4. 請說明覆蓋索引(Covering Index)是什麼?如何提升效能?

解析

  • 覆蓋索引:索引包含所有 SELECT 欄位,無需回表讀取資料。提升:減少 I/O。
  • 實作:複合索引涵蓋 WHERE 與 SELECT 欄位。

範例

CREATE INDEX idx_name_email ON users (name, email);
EXPLAIN SELECT name, email FROM users WHERE name = 'John';  // Extra: Using index

5. 請說明資料庫分區(Partitioning)與分表(Sharding)的差異與應用場景。

解析

  • Partitioning:單一資料庫內部分割表 (e.g., 依日期 RANGE)。適用:歷史資料歸檔,MySQL 原生支援。
  • Sharding:跨多資料庫分割資料 (e.g., 依用戶 ID 模數)。適用:水平擴展,高流量系統。

差異:Partitioning 易管理但單點;Sharding 複雜但可分散負載。Laravel 使用 Vitess 或自訂 middleware 實作 Sharding。


三、交易與一致性

確保資料完整性在高併發環境至關重要。

1. 請說明 ACID 是什麼?每個特性在實務中如何體現?

解析

  • Atomicity:交易全成或全敗 (e.g., 轉帳扣款+加款)。
  • Consistency:交易前後資料符合規則 (e.g., 餘額不負)。
  • Isolation:並行交易不干擾 (e.g., 鎖定避免髒讀)。
  • Durability:提交後持久化 (e.g., WAL 日誌)。

實務:InnoDB 引擎支援 ACID

2. 如何在 Laravel 中使用 DB Transaction?請舉例說明 rollback 的情境。

解析

  • 使用 DB::transaction() 包裝操作,異常時自動 rollback。

範例

DB::transaction(function () {
    $user->update(['balance' => DB::raw('balance - 100')]);
    $recipient->update(['balance' => DB::raw('balance + 100')]);
    // 若異常,rollback
});

情境:轉帳失敗 rollback 避免不一致。

3. 請說明資料庫鎖定(Locking)機制:Row Lock vs Table Lock。

解析

  • Row Lock:鎖定單行 (InnoDB),高併發友好。
  • Table Lock:鎖定整表 (MyISAM),簡單但低併發。

實務SELECT FOR UPDATE 使用 row lock。

4. 如何處理高併發下的資源競爭?請舉例說明 optimistic lock 的實作方式。

解析

  • 樂觀鎖:假設無衝突,更新時檢查版本。

範例

-- 加 version 欄位
UPDATE products SET stock = stock - 1, version = version + 1
WHERE id = 1 AND version = 5;  // 若版本變,更新失敗

Laravel:使用 optimistic locking trait 或自訂 check。

5. 請說明 Laravel 的 queue 任務中如何確保資料一致性與重試機制。

解析

  • 一致性:任務中使用 transaction;失敗 rollback。
  • 重試:設定 --tries,failed_jobs 表記錄,重試或通知。

範例

class ProcessOrder implements ShouldQueue {
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
    public $tries = 3;  // 重試 3 次
}

四、資料庫與 Laravel 整合

聚焦框架整合,提升開發效率。

1. Laravel 的 Eloquent ORM 與 Query Builder 有何差異?何時選用哪一種?

解析

  • Eloquent:物件導向,模型代表表,支援關聯。適合複雜邏輯。
  • Query Builder:流暢 API,直接建構 SQL。適合簡單或自訂查詢。

選用:Eloquent 日常;Builder 性能關鍵或原始 SQL。

2. 如何在 Laravel 中設計多資料庫連線?請舉例說明。

解析

範例

'connections' => [
    'mysql' => [...],
    'mysql_read' => [...]  // 讀寫分離
];
User::on('mysql_read')->get();

3. 請說明 Laravel 的 model observer 與 event 在資料變動時的應用。

解析

  • Observer:監聽模型事件 (saving, deleted),集中邏輯。
  • Event:廣播事件,解耦 (e.g., UserCreated)。

範例 Observer

class UserObserver {
    public function creating(User $user) {
        $user->uuid = Str::uuid();
    }
}

4. 如何在 Laravel 中實作資料快取(Cache)以減少 DB 查詢?

解析

  • 使用 Cache::remember() 快取查詢結果,支持 tags 失效。

範例

$users = Cache::remember('users', 3600, function () {
    return User::all();
});

5. 請說明 Laravel 的 pagination 如何運作,並如何優化大量資料的分頁效能。

解析

  • 運作:paginate() 自動計算 offset/limit,生成 Paginator 物件。
  • 優化:使用 cursor pagination (游標式) 避免 offset 效能衰退;索引排序欄位。

範例

$users = User::paginate(15);  // 標準
$users = User::cursorPaginate(15);  // 優化大資料

總結

這份題庫與解析涵蓋資料庫設計、優化、一致性與 Laravel 整合的核心知識,強調深度理解與實務應用。面試時,可追問範例細節評估經驗;求職者則可透過練習提升技能。在高流量系統中,平衡正規化與效能是關鍵。

沒有留言:

張貼留言

熱門文章