技術 7 分鐘閱讀
MySQL 查詢優化:讓你的資料庫跑得更快
前言
很多接案專案一開始跑得好好的,等資料量一大就開始變慢。問題通常出在資料庫查詢沒有優化。這篇文章分享一些我在實務中常用的 MySQL 優化技巧。
善用 EXPLAIN 分析查詢
優化的第一步是了解查詢到底怎麼執行的:
EXPLAIN SELECT * FROM orders
WHERE user_id = 123
AND status = 'completed'
ORDER BY created_at DESC;
重點看這幾個欄位:
- type:最好是
ref或const,如果是ALL代表全表掃描 - key:實際使用的索引
- rows:預估掃描的行數
- Extra:注意有沒有
Using filesort或Using temporary
在 Laravel 中可以這樣使用:
// 印出查詢的 SQL
DB::enableQueryLog();
$orders = Order::where('user_id', 123)->get();
dd(DB::getQueryLog());
索引設計原則
複合索引的欄位順序很重要
MySQL 的索引遵循「最左前綴原則」:
-- 建立複合索引
ALTER TABLE orders ADD INDEX idx_user_status_date
(user_id, status, created_at);
-- 會使用索引
SELECT * FROM orders WHERE user_id = 123;
SELECT * FROM orders WHERE user_id = 123 AND status = 'completed';
-- 不會使用索引(跳過了 user_id)
SELECT * FROM orders WHERE status = 'completed';
在 Laravel Migration 中建立索引
Schema::create('orders', function (Blueprint $table) {
$table->id();
$table->foreignId('user_id')->constrained();
$table->string('status');
$table->decimal('total', 10, 2);
$table->timestamps();
// 複合索引
$table->index(['user_id', 'status', 'created_at']);
});
避免過多索引
索引不是越多越好。每個索引都會:
- 佔用磁碟空間
- 減慢 INSERT、UPDATE、DELETE 的速度
- 增加資料庫維護成本
一般來說,一張表的索引數量控制在 5-6 個以內比較合理。
N+1 查詢問題
這是 Laravel 專案中最常見的效能殺手:
// N+1 問題:假設有 100 筆訂單,會產生 101 次查詢
$orders = Order::all();
foreach ($orders as $order) {
echo $order->user->name; // 每次迴圈都查一次 users 表
}
// 使用 Eager Loading 解決:只需要 2 次查詢
$orders = Order::with('user')->get();
foreach ($orders as $order) {
echo $order->user->name; // 不再額外查詢
}
我建議在開發環境安裝 Laravel Debugbar,它會自動偵測 N+1 問題:
composer require barryvdh/laravel-debugbar --dev
查詢優化技巧
只選需要的欄位
// 不好:選出所有欄位
$users = User::all();
// 好:只選需要的欄位
$users = User::select('id', 'name', 'email')->get();
使用分頁取代全部載入
// 不好:載入全部資料
$products = Product::all();
// 好:分頁載入
$products = Product::paginate(20);
// 更好:使用 cursor pagination 處理大量資料
$products = Product::cursorPaginate(20);
善用 chunk 處理大量資料
// 不好:一次載入 10 萬筆資料到記憶體
$users = User::all();
foreach ($users as $user) {
// 處理邏輯
}
// 好:分批處理
User::chunk(1000, function ($users) {
foreach ($users as $user) {
// 處理邏輯
}
});
使用 Raw Query 處理複雜統計
$stats = DB::select('
SELECT
DATE(created_at) as date,
COUNT(*) as order_count,
SUM(total) as revenue
FROM orders
WHERE created_at >= ?
GROUP BY DATE(created_at)
ORDER BY date DESC
', [now()->subDays(30)]);
慢查詢日誌
開啟 MySQL 的慢查詢日誌,找出需要優化的查詢:
# my.cnf
slow_query_log = 1
slow_query_log_file = /var/log/mysql/slow.log
long_query_time = 1
這會記錄所有執行時間超過 1 秒的查詢,是線上環境除錯的好工具。
快取策略
對於不常變動的資料,使用快取減少資料庫壓力:
// 快取 60 分鐘
$categories = Cache::remember('categories', 3600, function () {
return Category::with('children')->get();
});
// 資料更新時清除快取
Cache::forget('categories');
實際案例分享
最近有個電商專案,商品列表頁載入時間高達 5 秒。經過分析發現:
- 有 N+1 問題(商品圖片關聯)
- 缺少
category_id的索引 - 排序欄位沒有索引
修正後載入時間降到 200 毫秒以內,效果非常顯著。
結語
資料庫優化不需要很高深的技術,掌握好索引設計和查詢寫法就能解決大部分問題。建議在開發階段就養成好習慣,不要等到上線後才來救火。定期檢查慢查詢日誌,及早發現並處理效能瓶頸。
#MySQL #資料庫 #效能優化 #Laravel