Files
CFDivePlatform/docs/analysis/2026-06-11-performance-optimization-plan.md
T
a620906209 7ca91da138
Run Tests / test (pull_request) Successful in 2m7s
docs: 操作體驗優化計畫——實測定位 OPcache 停用 + bind mount 為主因
實測:API 快取命中仍 2.49s、容器測試比本機慢 14 倍;
容器內 opcache 已載入但 disabled。計畫分三階段,O1.1 啟用
OPcache 為最高投報率項目。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-11 17:55:56 +08:00

100 lines
4.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# CFDivePlatform 操作體驗優化計畫
> 撰寫日期:2026-06-11
> 背景:使用者反映本機操作體驗差、回應慢。本文以實測數據定位根因,並依投資報酬率排序優化項目。
---
## 一、實測基準(2026-06-11,本機 Docker 環境)
| 量測項目 | 數值 | 備註 |
|---------|------|------|
| `GET /api/diving-offers`(冷) | **7.98 秒** | 經 nginx:8080 → php-fpm |
| 同端點第 2 次 | 6.07 秒 | |
| 同端點第 3 次(Redis 快取命中) | **2.49 秒** | 快取命中仍要 2.5 秒 → 瓶頸不在查詢 |
| 容器內全測試套件 | 64.4 秒 | 同套件本機 PHP 僅 4.5 秒(**14 倍差距** |
**判讀**:Redis 快取命中後仍需 2.5 秒,代表時間幾乎都花在「PHP 啟動 + 載入框架程式碼」,而非業務邏輯或資料庫。瓶頸在環境層,不在程式碼。
## 二、根因分析(已驗證)
1. **OPcache 已載入但被停用**(容器內實測 `opcache_get_status` 回報 disabled`docker/php/local.ini` 無任何 opcache 設定)。每個 HTTP 請求都重新編譯 Laravel 全框架數千個 PHP 檔。
2. **Windows bind mount `./:/var/www`**docker-compose.yml:16)。每次檔案讀取都跨越 Windows↔Linux 檔案系統邊界(Docker Desktop 的檔案橋接是已知效能黑洞),與根因 1 疊加:每請求數千次跨界 I/O。
3. 次要因素:
- `cfdive-nginx` healthcheck 持續失敗(`wget http://localhost/` connection refused),雖不影響服務但遮蔽真正的健康狀態。
- `SESSION_DRIVER=database``QUEUE_CONNECTION=database`queue worker 輪詢 MySQLAPI 多為 stateless 影響小,列為觀察項。
- `APP_DEBUG=true`:開發環境正常設定,影響相對小。
## 三、優化計畫
### Phase 1 — 立即(0.5 天內,預期把 API 壓到 0.5 秒以下)
**O1.1 啟用 OPcache(投報率最高的一項)**
`docker/php/local.ini` 加入開發友善設定後 rebuild app 容器:
```ini
opcache.enable=1
opcache.enable_cli=0
opcache.memory_consumption=192
opcache.max_accelerated_files=20000
; 開發環境:仍偵測檔案變更,存檔即生效
opcache.validate_timestamps=1
opcache.revalidate_freq=0
```
驗收:`GET /api/diving-offers` 快取命中請求 < 500ms(對照基準 2.49s)。
**O1.2 修復 nginx healthcheck**
診斷 `wget http://localhost/` 為何 connection refused(常見原因:nginx 只 listen 特定 server_name、或 healthcheck 應改打 `127.0.0.1`),修正 docker-compose.yml:63 的 healthcheck。驗收:`docker compose ps` 顯示 nginx healthy。
**O1.3 `composer dump-autoload -o`**
寫入 Dockerfile / entrypoint,減少 autoload 時的檔案系統探測。
### Phase 2 — 結構性(若 Phase 1 後仍 > 1 秒才執行,0.5~1 天)
**O2.1 消除 Windows bind mount I/O**
選項(擇一,建議 a 先試):
- (a) **vendor/ 改用 named volume**`- vendor-data:/var/www/vendor`,框架程式碼(I/O 大宗)留在 Linux 原生檔案系統,原始碼仍可即時編輯。需在容器內跑 `composer install`
- (b) **整個 repo 搬入 WSL2 檔案系統**`\\wsl$\Ubuntu\...`),IDE 透過 WSL remote 開啟。I/O 改善最徹底(10~50 倍),但改變現有 laragon 工作流程,遷移成本較高。
**O2.2 SESSION/QUEUE driver 評估**
Redis 已在跑,`SESSION_DRIVER=redis``QUEUE_CONNECTION=redis` 可移除 queue worker 對 MySQL 的輪詢壓力。屬順手改,不是主瓶頸。
### Phase 3 — 應用層體感(與環境無關,部署後使用者也受益,1~2 天)
**O3.1 課程封面圖未壓縮**
聊天圖片已有 `scaleDown(2048) + toJpeg(85)` 處理(`BookingMessageController.php:115`),但 `CourseImageController` 上傳的封面/圖庫**原檔直存**。手機拍攝的 5~10MB 原圖會直接進課程列表頁。建議:套用與聊天圖片相同的縮圖管線,列表用圖另出 ~600px 縮圖;前端 `<img>``loading="lazy"`
**O3.2 無分頁的列表端點**
`GET /api/member/bookings``/api/provider/bookings``/api/admin/bookings` 皆為 `->get()` 全量撈取。目前資料量小無感,預約數成長後會線性變慢。建議在金流上線前(資料開始累積)補分頁。
**O3.3 前端載入體感**
- 路由已全面 lazy-load(已驗證,不需處理)。
- 列表頁加 skeleton 載入佔位,消除「白屏等待」體感。
- 搜尋輸入加 debounce(300ms),減少連發請求。
### 生產環境註記(部署時順手做)
- `php artisan optimize`config / route / view cache
- 生產 OPcache 改 `validate_timestamps=0`(部署時重啟 php-fpm 刷新)
## 四、執行順序與驗收
```
第 1 步 O1.1 OPcache → 重測基準(預期 2.49s → <0.5s
第 2 步 O1.2 + O1.3 順手修
第 3 步 視重測結果決定是否做 O2.1(>1s 才做)
第 4 步 O3.1 圖片壓縮(開 openspec change,影響 course-image-upload 規格)
第 5 步 O3.2 / O3.3 排入一般 backlog
```
每步驗收一律以第一節的 curl 量測方式對照,數據寫回本文件。