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

4.9 KiB
Raw Blame History

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 回報 disableddocker/php/local.ini 無任何 opcache 設定)。每個 HTTP 請求都重新編譯 Laravel 全框架數千個 PHP 檔。
  2. Windows bind mount ./:/var/wwwdocker-compose.yml:16)。每次檔案讀取都跨越 Windows↔Linux 檔案系統邊界(Docker Desktop 的檔案橋接是已知效能黑洞),與根因 1 疊加:每請求數千次跨界 I/O。
  3. 次要因素:
    • cfdive-nginx healthcheck 持續失敗(wget http://localhost/ connection refused),雖不影響服務但遮蔽真正的健康狀態。
    • SESSION_DRIVER=databaseQUEUE_CONNECTION=databasequeue worker 輪詢 MySQLAPI 多為 stateless 影響小,列為觀察項。
    • APP_DEBUG=true:開發環境正常設定,影響相對小。

三、優化計畫

Phase 1 — 立即(0.5 天內,預期把 API 壓到 0.5 秒以下)

O1.1 啟用 OPcache(投報率最高的一項)

docker/php/local.ini 加入開發友善設定後 rebuild app 容器:

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=redisQUEUE_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 optimizeconfig / 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 量測方式對照,數據寫回本文件。