頁面 A 執行耗時 10 秒的 PHP 腳本並寫入 Session 進度;頁面 B 輪詢讀取 Session 顯示進度。預期每秒更新,實際 **10 秒後一次性顯示 complete**。
PHP 預設 Session 處理器 (files) 以 **獨佔鎖** 開啟 Session 檔案:
session_start() → flock(LOCK_EX) 獲取寫鎖。session_start() 會 **阻塞等待鎖釋放**。session_write_close() → 釋放鎖。<!-- session.html -->
<button onclick="handle()">開始</button>
<div id="result"></div>
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script>
function handle() {
$.get("handle.php"); // 觸發長任務
setInterval(() => $.get("getstep.php").done(d => {
$('#result').html('步驟: ' + d);
if (d === 'complete') clearInterval(timer);
}), 500);
}
</script>
<?php // handle.php
session_start();
for ($i=1; $i<=10; $i++) {
$_SESSION['step'] = $i;
sleep(1);
}
$_SESSION['step'] = 'complete';
?>
<?php // getstep.php
session_start();
echo $_SESSION['step'] ?? '';
?>
| 方案 | 原理 | 優點 | 缺點 |
|---|---|---|---|
| session_write_close() | 提前寫入並釋放鎖,後續邏輯不再寫 Session | 零依賴、即時生效 | 後續無法寫入 Session |
| Redis/Memcached Session | 分散式 Session,通常無全域鎖或鎖粒度更細 | 高併發、支援叢集 | 需額外基礎設施 |
| session.save_handler = user | 自定義讀寫鎖邏輯 (如樂觀鎖、行級鎖) | 完全可控 | 開發複雜度高 |
| 異步任務隊列 | 長任務放入隊列,前端輪詢任務狀態 (不依賴 Session) | 架構正交、擴展性最佳 | 引入隊列基礎設施 |
<?php // handle.php 優化版
session_start();
$_SESSION['step'] = 0;
session_write_close(); // 釋放鎖!
for ($i=1; $i<=10; $i++) {
// 需更新進度時,重新開啟、寫入、關閉 (極短暫持有鎖)
session_start();
$_SESSION['step'] = $i;
session_write_close();
sleep(1);
}
session_start();
$_SESSION['step'] = 'complete';
session_write_close();
PHP Session 檔案鎖是隱形效能殺手。長耗時請求 **務必** 在進入耗時邏輯前呼叫 session_write_close(),或將進度寫入 Redis/資料庫/暫存檔,避免阻塞同用戶其他請求。架構層面採用異步任務隊列徹底解耦,是高併發系統的標準解法。