Skip to content

PHPの実行モデルと前提

PHPの実行モデルと、実務で事故を防ぐための前提条件を詳しく解説します。

実行モデルとリソースの物理的制約

Section titled “実行モデルとリソースの物理的制約”

コンピュータ資源は有限であり、性能ではなく制約を前提に設計することが基本です。

CPU・メモリよりも先に枯渇するリソース:

  1. DB・外部APIのコネクション数

    • 接続プールの上限(例: PDOのmax_connections
    • 接続リークは数時間後にシステム全体を停止させる
  2. プロセス/スレッドプール

    • PHP-FPMのプロセス数制限(pm.max_children
    • プロセス枯渇により、すべてのリクエストが処理できなくなる
  3. ファイル記述子

    • OSレベルの制限(通常1024〜65536)
    • ファイルやソケットを適切にクローズしないと枯渇
  4. メモリリーク

    • オブジェクトの参照が保持される
    • グローバル変数や静的変数の不適切な使用

実際の事故例:

10:00:00 - アプリケーション起動(接続プール: 10/10)
10:00:01 - リクエスト1受信(接続取得: 11/10 → 待機)
10:00:02 - リクエスト2受信(接続取得: 12/10 → 待機)
...
10:30:00 - 接続が解放されず、すべてのリクエストが待機状態
10:30:01 - タイムアウトエラーが大量発生
10:30:02 - システム全体が応答不能

実行モデル:

PHPコード (.php)
↓ インタープリタで実行
Zend Engine(実行時)

重要な特徴:

  1. 動的型付け: 実行時に型が決定される(PHP 8.0以降は型宣言が強化)
  2. ガベージコレクション: 自動メモリ管理(ただし、参照が保持されている場合は動作しない)
  3. リクエストごとの実行: 各リクエストが独立したプロセス/スレッドで実行される
  4. OPcache: バイトコードキャッシュによる高速化

PHPのトランザクション管理:

// PDOでのトランザクション管理
try {
$pdo->beginTransaction();
// トランザクション内の処理
$stmt = $pdo->prepare("INSERT INTO orders (user_id, amount) VALUES (?, ?)");
$stmt->execute([$userId, $amount]);
$orderId = $pdo->lastInsertId();
$stmt = $pdo->prepare("UPDATE inventory SET stock = stock - ? WHERE product_id = ?");
foreach ($items as $item) {
$stmt->execute([$item['quantity'], $item['product_id']]);
}
$pdo->commit();
} catch (Exception $e) {
$pdo->rollBack();
throw $e;
}

特徴:

  • 明示的なトランザクション境界: beginTransaction()commit()で明示
  • 手動ロールバック: エラー時に手動でロールバック
  • 例外処理: try-catchブロックでエラーハンドリング

PHPの非同期処理:

// プロセスフォークを使用
$pid = pcntl_fork();
if ($pid == -1) {
die('Could not fork');
} elseif ($pid) {
// 親プロセス
pcntl_wait($status);
} else {
// 子プロセス(非同期処理)
processOrder($orderId);
exit(0);
}

特徴:

  • プロセスフォーク: pcntl_fork()による非同期処理
  • エラーハンドリング: 親プロセスで子プロセスの状態を監視
  • 再実行: 手動で再実行を実装
環境特徴主なリスク
Serverless (Lambda/Vercel)短寿命・自動スケールコールドスタート、接続バースト、DBパンク、実行時間制限(Lambda: 15分、Vercel: 300秒)
常駐プロセス (PHP-FPM)長寿命・安定動作メモリリーク、プール断片化、デッドロック、接続リーク

制約:

// ❌ 悪い例: Serverless環境で問題のあるコード
function createOrder($orderData) {
// 問題: 長時間実行される可能性がある
// 問題: トランザクションが長時間保持される
// 問題: 接続プールが適切に管理されない
$pdo = new PDO($dsn, $user, $pass);
$stmt = $pdo->prepare("INSERT INTO orders (user_id, amount) VALUES (?, ?)");
$stmt->execute([$orderData['user_id'], $orderData['amount']]);
return $pdo->lastInsertId();
}

問題点:

  • 実行時間の制限: Lambdaは最大15分、Vercelは最大300秒
  • コールドスタート: PHPの起動に時間がかかる(500ms-2s)
  • メモリ制限: メモリ使用量に制限がある(Lambda: 128MB〜10GB)
  • 接続バースト: スケールアウト時に接続プールが急増し、DBがパンクする可能性

PHPの実行モデルと前提のポイント:

  • リソースの物理的制約: CPU・メモリよりも先に枯渇するのは、DB接続数・プロセスプール・ファイル記述子・メモリリーク
  • PHP: 動的型付け、ガベージコレクション、リクエストごとの実行、OPcache
  • トランザクション境界: beginTransaction()commit()で明示、手動ロールバック
  • 非同期処理: プロセスフォークによる制御、エラーハンドリング、手動再実行
  • Serverless環境: 実行時間制限、コールドスタート、メモリ制限、接続バースト
  • 常駐プロセス環境: 長時間実行可能、接続プール、キャッシュ(メモリリーク・接続リークに注意)

重要な原則: 性能ではなく制約を前提に設計する。リソースの垂れ流しは数時間後にシステム全体を停止させる。