PHPのマルチスレッド化について

採用はこちら

PHPのマルチスレッド化とは、複数の処理を同時並行で実行し、処理時間の短縮や待ち時間の有効活用を目指す考え方です。

ただし、PHPはもともと、JavaやGoのようにアプリケーションコード内でスレッドを積極的に扱う前提の言語ではありません。

一般的なPHPのWebアプリケーションでは、1つのリクエストを1つの実行単位として処理し、複数リクエストの並列処理はPHP-FPMやWebサーバー側のワーカーによって行われることが多いです。

そのため、PHPで処理を速くしたい場合でも、すぐにマルチスレッド化を考えるのではなく、処理の内容に応じて、キュー処理、マルチプロセス化、非同期処理、外部ワーカーなどを使い分けることが重要です。

目次

PHPではマルチスレッド化より並列処理の設計が重要

PHPで「マルチスレッド化」と言う場合、実際にはいくつかの異なる技術が混同されやすいです。

厳密なマルチスレッドだけでなく、マルチプロセス、キュー処理、非同期I/O、バックグラウンド処理なども、広い意味では「複数の処理を並行して進める方法」として扱われます。

マルチスレッド

マルチスレッドは、1つのプロセス内で複数のスレッドを動かす方法です。

PHPで本格的なスレッド処理を行う場合は、主にparallel拡張が候補になります。

ただし、ZTS有効のPHPビルドが必要で、一般的なレンタルサーバーや通常のPHP-FPM環境では導入しにくい場合があります。

マルチプロセス

マルチプロセスは、複数のPHPプロセスを起動して処理を分担する方法です。

PHPではpcntl_fork()を使った方法が代表的です。

これはマルチスレッドではありませんが、CLIバッチや大量データ処理では実用的な選択肢になります。

キュー処理

キュー処理は、重い処理をジョブとして登録し、バックグラウンドワーカーに処理させる方法です。

Webアプリケーションでは、最も実用的な並列処理の方法です。

メール送信、PDF生成、画像変換、外部API連携、CSV処理などをキュー化することで、ユーザーへのレスポンスを速くできます。

非同期処理

非同期処理は、外部API通信やファイルI/Oなどの待ち時間を有効活用する方法です。

マルチスレッドのように複数CPUコアで同時に計算する仕組みとは異なりますが、HTTPリクエストやDBアクセスなど、待ち時間が多い処理では効果を発揮します。

PHPが標準でマルチスレッド向きではない理由

PHPはWeb開発に広く使われている言語ですが、標準的な実行スタイルは「リクエスト単位の処理」です。

たとえば、Webサイトにアクセスがあった場合、PHPはそのリクエストに対して処理を行い、HTMLやJSONなどのレスポンスを返します。

この処理が終わると、そのリクエストに関するPHPの実行も終了するのが一般的です。

Webサーバー側で並列処理されることが多い

PHPアプリケーションでは、アプリケーションコード自体をマルチスレッド化しなくても、PHP-FPMやWebサーバーが複数リクエストを並列に処理します。

そのため、通常のWebページ表示を高速化したい場合、PHPコード内でスレッドを作るよりも、以下のような改善の方が効果的です。

  • SQLの最適化
  • N+1問題の解消
  • ページキャッシュの導入
  • OPcacheの有効化
  • CDNの利用
  • 画像の軽量化
  • 不要なプラグインや処理の削減
  • PHP-FPMのワーカー数調整

特にWordPressや一般的なCMSでは、PHPのマルチスレッド化よりも、キャッシュやサーバー設定の見直しの方が現実的です。

スレッドセーフな環境が必要になる

PHPでスレッドを扱う場合、ZTS、つまりZend Thread Safetyが有効なPHPビルドが必要になることがあります。

たとえば、parallel拡張を使うにはZTS有効のPHPが必要です。

ZTSは後から設定ファイルで簡単に有効化できるものではなく、PHPのビルド時に決まるものです。

そのため、一般的なレンタルサーバーや共有サーバーでは、PHPのスレッド処理を自由に導入できないことがあります。

ライブラリや拡張機能の扱いに注意が必要

PHPのすべての拡張機能やライブラリが、スレッド環境で安全に使えるとは限りません。

DB接続、ファイルハンドル、外部APIクライアント、グローバル変数、共有オブジェクトなどをスレッド間で安易に共有すると、予期しないエラーやデータ競合が発生する可能性があります。

PHPで並列処理を行う場合は、「処理を共有する」のではなく、「処理単位を分けて、それぞれ独立して実行する」設計が基本になります。

PHPで使える主な並列処理の方法

PHPで複数処理を並行して実行する方法には、いくつかの選択肢があります。

それぞれに向いている用途や注意点があるため、目的に合わせて選ぶことが大切です。

parallel拡張を使う方法

PHPで本格的にスレッドベースの並列処理を行いたい場合、現在の代表的な選択肢はparallel拡張です。

parallelでは、parallel\Runtimeを使って別の実行環境を作り、その中で処理を実行できます。

実行した処理の結果は、parallel\Futureを通じて後から受け取ることができます。

parallel拡張の基本的な考え方

parallelでは、メインの処理とは別にRuntimeを作成し、そのRuntime上で別の処理を実行します。

たとえば、重い計算処理や時間のかかる処理を別Runtimeに渡し、メイン処理側では別の作業を進めることができます。

<?php

$runtime = new parallel\Runtime();

$future = $runtime->run(function () {
    sleep(2);
    return 100 + 200;
});

echo "別の処理を実行中...\n";

$result = $future->value();

echo "結果: " . $result . "\n";

この例では、$runtime->run()で別Runtimeに処理を渡し、$future->value()で結果を受け取っています。

複数の処理を並列実行する例

複数のRuntimeを作成すれば、複数の処理を並列に実行できます。

<?php

$runtimes = [];
$futures = [];

for ($i = 1; $i <= 4; $i++) {
    $runtimes[$i] = new parallel\Runtime();

    $futures[$i] = $runtimes[$i]->run(function ($number) {
        sleep(2);
        return $number * 10;
    }, [$i]);
}

foreach ($futures as $i => $future) {
    echo "Task {$i}: " . $future->value() . PHP_EOL;
}

sleep()のような待ち時間中心の処理であれば、並列化によって合計時間を短縮しやすくなります。

ただし、CPUを多く使う処理では、CPUコア数、メモリ、スレッド生成コスト、外部サービスやDBの制限によって効果が変わります。

並列化すれば必ず速くなるわけではありません。

parallel拡張のメリット

parallelのメリットは、PHPの中でスレッドベースの並列処理を扱えることです。

処理の戻り値をFutureで受け取れるため、非同期タスクの結果管理がしやすい点も特徴です。

また、parallel\Channelを使えば、タスク間でデータをやり取りすることもできます。

parallel拡張の注意点

parallelは便利ですが、導入ハードルは高めです。

まず、ZTS有効のPHPが必要です。

一般的なPHP-FPM環境やレンタルサーバーでは、そのまま使えないことがあります。

また、Runtime::run()に渡すクロージャには制約があります。

通常のPHPコードを何でもそのまま別スレッドに渡せるわけではありません。

参照渡し、参照のuse、一部の内部オブジェクト、リソース、DB接続、ファイルハンドルなどは注意が必要です。

実務では、親側で作成したDB接続や外部サービスのクライアントをそのまま渡すのではなく、各Runtime内で必要な接続を作り直す設計にする方が安全です。

pthreads拡張を使う方法

以前は、PHPのマルチスレッド化というとpthreads拡張が使われることがありました。

しかし、現在の新規開発では基本的におすすめできません。

pthreadsは現在では選びにくい

pthreadsは過去にPHPでスレッド処理を行うために使われていた拡張ですが、現在はメンテナンス状況に不安があります。

また、Webサーバー環境では使えず、CLIアプリケーション向けの拡張でした。

そのため、通常のWebアプリケーションで利用する選択肢にはなりにくいです。

新規開発ではparallelを検討する

現在、PHPでスレッドベースの並列処理を検討するなら、pthreadsではなくparallelを検討する方が自然です。

ただし、parallelも導入環境を選ぶため、Webアプリケーションではキュー処理やマルチプロセス化を優先した方がよいケースが多いです。

pcntl_forkによるマルチプロセス化

PHPで実務的に使われる並列処理の方法として、pcntl_fork()によるマルチプロセス化があります。

これはマルチスレッドではありません。

親プロセスから子プロセスを作り、複数のPHPプロセスで処理を分担する方法です。

pcntl_forkの基本例

<?php

$pid = pcntl_fork();

if ($pid == -1) {
    die("forkに失敗しました\n");
}

if ($pid) {
    echo "親プロセスです。子プロセスID: {$pid}\n";
    pcntl_wait($status);
    echo "子プロセスが終了しました\n";
} else {
    echo "子プロセスです\n";
    sleep(2);
    echo "子プロセスの処理が完了しました\n";
    exit;
}

この例では、親プロセスから子プロセスを作成し、子プロセス側で別の処理を実行しています。

複数プロセスで処理する例

<?php

$tasks = range(1, 10);
$children = [];
$maxProcesses = 3;

foreach ($tasks as $task) {
    while (count($children) >= $maxProcesses) {
        $endedPid = pcntl_wait($status);
        unset($children[$endedPid]);
    }

    $pid = pcntl_fork();

    if ($pid == -1) {
        die("forkに失敗しました\n");
    }

    if ($pid) {
        $children[$pid] = true;
    } else {
        echo "タスク {$task} を処理中\n";
        sleep(2);
        echo "タスク {$task} が完了\n";
        exit(0);
    }
}

while (count($children) > 0) {
    $endedPid = pcntl_wait($status);
    unset($children[$endedPid]);
}

この例では、最大3プロセスまで同時に実行するように制御しています。

並列処理では、同時実行数の制限が非常に重要です。

大量のタスクを一気に子プロセス化すると、CPU、メモリ、DB接続数、外部APIアクセス数が急増し、サーバー全体が不安定になる可能性があります。

pcntl_forkが向いている処理

pcntl_fork()は、LinuxなどUnix系環境のCLIバッチで使うのに向いています。

具体的には、以下のような処理で検討できます。

  • 大量CSVの分割処理
  • 画像変換
  • ログ解析
  • 大量ファイルの処理
  • APIデータの一括取得
  • クローラー
  • バッチ処理の高速化

pcntl_forkの注意点

pcntl_fork()はWebリクエスト中に安易に使うものではありません。

通常のWebアクセス処理の中で子プロセスを作ると、プロセス管理、タイムアウト、ログ、DB接続、リソース解放、エラー処理が複雑になります。

また、親子プロセス間でメモリは直接共有されません。

処理結果を集約するには、DB、ファイル、Redis、メッセージキュー、標準出力、ソケットなどを使う必要があります。

DB接続についても注意が必要です。

fork前に作ったDB接続を子プロセスでそのまま使うと、予期しない問題が起きることがあります。

子プロセス側で接続を作り直す設計にする方が安全です。

外部プロセスとしてPHPコマンドを実行する方法

PHPから別のPHPコマンドを起動し、複数プロセスで処理を分担する方法もあります。

たとえば、LaravelのArtisanコマンドやSymfony Consoleコマンドを複数起動して、処理対象を分割する方法です。

外部プロセス化の考え方

たとえば、CSVファイルを複数に分割し、それぞれを別コマンドで処理します。

php import.php part_001.csv
php import.php part_002.csv
php import.php part_003.csv

このように処理単位を分ければ、OS側で複数プロセスとして並列実行できます。

execを使う場合の注意点

PHPにはexec()などの外部コマンド実行関数があります。

ただし、安易に使うと以下のような問題が発生します。

  • コマンドインジェクションのリスクがある
  • エラー検知が難しい
  • ログ管理が雑になりやすい
  • 実行中プロセスの監視が難しい
  • 多重起動制御が必要になる
  • サーバー環境によっては制限される
  • Webサーバーユーザー権限で実行される

そのため、実務ではexec()を直接多用するより、Symfony Process、Laravel Queue、Supervisor、systemd、cron、Docker、Kubernetes Jobsなどで管理する方が安全です。

キュー処理によるバックグラウンド化

Webアプリケーションで重い処理を分離したい場合、最も実用的な選択肢はキュー処理です。

キュー処理は厳密にはマルチスレッドではありませんが、複数のワーカーを起動することでジョブを並列に処理できます。

キュー処理の基本的な仕組み

キュー処理では、時間のかかる処理をその場で実行せず、ジョブとしてキューに登録します。

その後、バックグラウンドで動いているワーカーがジョブを取り出して処理します。

たとえば、ユーザー登録時に以下の処理があるとします。

  • DBにユーザー情報を保存する
  • ユーザーへメールを送る
  • 管理者へ通知する
  • 外部CRMに連携する
  • Slackに通知する

これらをすべてWebリクエスト中に実行すると、ユーザーの待ち時間が長くなります。

そこで、メール送信や外部API連携などをキューに入れ、バックグラウンドで処理させます。

これにより、ユーザーには素早くレスポンスを返せます。

Laravel Queueの例

Laravelでは、以下のようにジョブをキューへ投入できます。

SendWelcomeEmail::dispatch($user);
SyncUserToCrm::dispatch($user);
NotifySlack::dispatch($user);

バックグラウンド側では、キューワーカーを起動します。

php artisan queue:work

複数のワーカーを起動すれば、複数のジョブを並列に処理できます。

php artisan queue:work
php artisan queue:work
php artisan queue:work

キュー処理が向いているケース

キュー処理は、以下のような処理に向いています。

  • メール送信
  • PDF生成
  • 画像変換
  • 動画変換
  • 外部API連携
  • CSVインポート
  • CSVエクスポート
  • 通知処理
  • レポート生成
  • クローラー
  • 予約投稿
  • データ同期

Webアプリケーションでは、重い処理をキュー化することで、ユーザー体験を改善しながらサーバー負荷を調整しやすくなります。

キュー処理の注意点

キュー処理でも、ワーカー数を増やせば必ず安全に速くなるわけではありません。

ワーカー数を増やすと、DB接続数、Redisへのアクセス、外部APIへのリクエスト数、CPU使用率、メモリ使用量が増えます。

また、同じデータを複数ジョブが同時に更新する場合は、排他制御が必要です。

実務では、以下のような設計が重要です。

  • ジョブのタイムアウト設定
  • リトライ回数の設定
  • 失敗ジョブの管理
  • 同時実行数の制御
  • Supervisorなどによるワーカー監視
  • ログ管理
  • 重複実行の防止
  • DBロックやRedisロックの活用

非同期処理を使う方法

PHPでは、ReactPHP、Amp、Swoole、OpenSwooleなどを使って非同期処理を実装することもできます。

非同期処理はマルチスレッドとは異なります。

複数の処理をCPU上で同時に実行するというより、I/O待ちの時間を有効活用する仕組みです。

非同期処理とマルチスレッドの違い

非同期処理は「同時に待てる」仕組みです。

たとえば、外部APIに10件のリクエストを送る場合、同期処理では1件目のレスポンスを待ってから2件目を送ります。

一方、非同期処理では複数のリクエストを同時に投げ、返ってきたものから処理できます。

これにより、外部APIやネットワーク通信の待ち時間を短縮できます。

非同期処理が向いている処理

非同期処理は、以下のようなI/O待ちが多い処理に向いています。

  • 外部APIアクセス
  • HTTPリクエスト
  • DB問い合わせ
  • ファイルI/O
  • ソケット通信
  • WebSocket
  • チャットサーバー
  • リアルタイム通知

CPU負荷が高い処理には向かない場合がある

非同期処理は、待ち時間の多い処理には効果的です。

しかし、画像変換、大量計算、暗号化、動画処理など、CPUを使い続ける処理では、非同期処理だけでは十分に速くならない場合があります。

CPU負荷が高い処理では、マルチプロセス、複数ワーカー、別サーバー、コンテナ分散などを検討する方が適しています。

Fiberを使う方法

PHP 8.1以降にはFiberがあります。

Fiberは、処理を途中で中断し、後から再開できる仕組みです。

ただし、Fiber自体はマルチスレッドではありません。

Fiberの基本例

<?php

$fiber = new Fiber(function () {
    echo "処理A\n";

    Fiber::suspend("一時停止しました");

    echo "処理B\n";

    return "完了";
});

$value = $fiber->start();

echo $value . PHP_EOL;

$result = $fiber->resume();

echo $result . PHP_EOL;

この例では、Fiber::suspend()で処理を一時停止し、後からresume()で再開しています。

Fiberは直接的な並列処理ではない

Fiberは、複数CPUコアで同時に処理を走らせる仕組みではありません。

どちらかというと、非同期処理ライブラリやイベントループの内部で使われる低レベルな仕組みです。

そのため、Fiberを使えばPHPが自動的にマルチスレッド化されるわけではありません。

PHPのマルチスレッド化・並列処理が向いているケース

PHPで並列処理を検討する価値があるのは、処理に時間がかかり、なおかつ処理単位を分割しやすいケースです。

大量の外部API取得

外部APIを大量に呼び出す処理では、並列処理や非同期処理が有効です。

たとえば、100件の商品データを外部APIから取得する場合、1件ずつ順番に取得すると時間がかかります。

複数件を同時に取得できれば、全体の処理時間を短縮できる可能性があります。

ただし、外部APIにはレート制限があります。

並列数を増やしすぎると、429 Too Many Requestsなどのエラーが発生することがあります。

画像処理

大量の画像をリサイズする、サムネイルを作成する、WebPに変換する、といった処理も並列化の対象になります。

ただし、画像処理はCPUやメモリを多く使います。

並列数を上げすぎると、サーバー全体の負荷が高くなるため注意が必要です。

CSVインポート

数十万行、数百万行のCSVを処理する場合、ファイルを分割して複数プロセスや複数ワーカーで処理できます。

ただし、DBへの書き込みがボトルネックになることも多いです。

単純に並列数を増やすだけでなく、バルクインサート、インデックス設計、トランザクション、重複チェック、ロック制御も見直す必要があります。

クローラー・スクレイピング

複数ページを取得するクローラーでは、並列処理の効果が出やすいです。

ただし、対象サイトへの負荷、robots.txt、アクセス間隔、利用規約、IPブロック、法的リスクには注意が必要です。

メール送信

大量メール送信も並列化できます。

ただし、SMTPサーバーやメール配信サービスには送信数の制限があります。

実務では、PHPで直接大量送信するよりも、キュー処理とメール配信サービスを組み合わせる方が安全です。

PHPのマルチスレッド化・並列処理が向いていないケース

並列化は便利ですが、どのような処理にも向いているわけではありません。

むしろ、並列化によって複雑さや負荷が増え、逆効果になるケースもあります。

通常のWebページ表示

通常のWebページ表示では、PHPコード内でマルチスレッド化する必要はほとんどありません。

WebサーバーやPHP-FPMがリクエスト単位で並列処理しているため、1リクエスト内でスレッドを作るよりも、以下の改善を優先すべきです。

  • SQLの最適化
  • キャッシュの導入
  • 画像の軽量化
  • CDNの利用
  • PHP-FPMの設定見直し
  • 不要な処理の削減
  • 外部API呼び出しの削減

WordPressの通常運用

WordPressサイトで「PHPをマルチスレッド化したい」と考える場合、多くは方向性が違います。

WordPressでは、PHPのスレッド化よりも以下の改善が現実的です。

  • ページキャッシュ
  • オブジェクトキャッシュ
  • OPcache
  • CDN
  • 画像圧縮
  • 遅延読み込み
  • 不要プラグインの削除
  • データベース最適化
  • PHP-FPMのチューニング
  • サーバースペックの見直し

短時間で終わる処理

処理自体が軽い場合、並列化のオーバーヘッドの方が大きくなることがあります。

スレッドやプロセスの生成、タスク分割、通信、結果集約、エラー処理にはコストがかかります。

数ミリ秒から数十ミリ秒で終わる処理であれば、並列化しない方が効率的な場合もあります。

PHPで並列処理を行うメリット

PHPで適切に並列処理を導入すると、処理時間の短縮やユーザー体験の改善につながります。

処理時間を短縮できる

並列処理の大きなメリットは、全体の処理時間を短縮できる可能性があることです。

たとえば、1件あたり2秒かかるAPI取得を50件行う場合、直列処理では単純計算で100秒かかります。

これを10並列で処理できれば、理論上は大幅に短縮できる可能性があります。

ただし、実際にはAPI制限、通信速度、CPU、メモリ、DB、ネットワーク帯域などの影響を受けます。

待ち時間を有効活用できる

外部API、DB、ファイル読み込み、ネットワーク通信などでは、処理の待ち時間が発生します。

並列処理や非同期処理を使うことで、その待ち時間に別の処理を進められます。

大量データ処理に対応しやすくなる

CSV、ログ、商品データ、アクセス解析データなどを大量に処理する場合、処理単位を分割して並列実行することで、バッチ処理の時間を短縮できます。

夜間バッチが朝までに終わらない、管理画面でCSVインポートがタイムアウトする、といった問題の改善に役立ちます。

PHPで並列処理を行うデメリット

並列処理にはメリットだけでなく、デメリットもあります。

設計や運用が複雑になるため、導入前にリスクを理解しておく必要があります。

実装が複雑になる

並列処理では、直列処理にはない問題が発生します。

たとえば、以下のような要素を考える必要があります。

  • 処理順序
  • 排他制御
  • データ競合
  • ロック
  • タイムアウト
  • リトライ
  • エラー集約
  • ログ管理
  • 子プロセスの監視
  • メモリ使用量
  • ゾンビプロセス対策

単純な処理であれば、並列化しない方が保守しやすい場合もあります。

デバッグが難しくなる

並列処理では、エラーが再現しにくくなります。

ある時は成功し、ある時は失敗するという問題が起きやすくなります。

そのため、ログにはタスクID、プロセスID、対象データID、開始時刻、終了時刻、処理時間、エラー内容、リトライ回数などを残すことが重要です。

サーバー負荷が増える

並列数を増やすと、CPU、メモリ、DB接続数、ネットワーク帯域、外部APIアクセス数が増えます。

「10並列で速くなったから100並列にする」といった単純な増やし方は危険です。

並列数は、サーバースペック、DB性能、外部APIの制限、キューの処理能力を見ながら慎重に調整する必要があります。

環境依存が増える

parallelpcntlなどは、利用できる環境が限られます。

共用レンタルサーバーでは、必要な拡張機能が使えないこともあります。

VPS、クラウドサーバー、Docker、自社管理サーバーの方が導入しやすいです。

PHPで並列処理を設計するときのポイント

PHPで並列処理を行う場合は、単に処理を同時に実行するだけでなく、安全に運用できる設計にする必要があります。

処理単位を小さく分ける

並列化するには、処理を分割できる形にする必要があります。

たとえば、以下のように分けると処理しやすくなります。

  • 商品IDごと
  • ユーザーIDごと
  • CSVの行範囲ごと
  • 画像ファイルごと
  • 日付ごと
  • 店舗ごと
  • カテゴリごと

1つの巨大な処理にすべてのロジックが詰まっていると、並列化しにくくなります。

同時実行数を制限する

並列処理では、同時実行数の制限が非常に重要です。

API取得は最大5並列、画像変換は最大2並列、DB書き込みは最大3並列など、処理の性質に応じて上限を決めます。

並列数を上げるほど速くなるとは限りません。

ボトルネックがDBや外部APIにある場合、並列数を増やすことで逆に遅くなることもあります。

処理状態を管理する

並列処理では、一部のタスクだけ失敗することがあります。

そのため、各タスクの状態を管理できるようにしておくと安全です。

たとえば、以下のようなステータスを持たせます。

pending
processing
completed
failed
retrying

このように管理すれば、失敗したタスクだけを再実行できます。

共有リソースを慎重に扱う

並列処理では、複数の処理が同じDBレコード、同じファイル、同じキャッシュキーを同時に更新する可能性があります。

このような場合、データ競合が発生します。

対策として、以下のような方法があります。

  • DBトランザクション
  • 行ロック
  • ユニーク制約
  • Redisロック
  • 楽観的ロック
  • ジョブの重複実行防止
  • 処理対象の分割

基本的には、複数の処理が同じデータを同時に更新しない設計にするのが理想です。

リトライ設計を入れる

外部APIやネットワーク処理では、一時的な失敗が起きることがあります。

そのため、一定回数リトライできる設計にしておくと安定します。

ただし、短時間に何度もリトライすると、外部サービスに負荷をかけたり、レート制限に引っかかったりします。

リトライ間隔を徐々に広げる設計が望ましいです。

PHPで並列化する前に確認すべきこと

並列化は、遅い処理を自動的に速くする魔法の方法ではありません。

まずは、どこがボトルネックになっているかを確認する必要があります。

ボトルネックを特定する

並列化する前に、以下を確認します。

  • 遅い原因はPHPの計算処理か
  • DBクエリが遅いのか
  • N+1問題が発生していないか
  • 外部APIの待ち時間が長いのか
  • ファイルI/Oが遅いのか
  • CPU使用率は高いのか
  • メモリは足りているのか
  • キャッシュで解決できないか
  • Webリクエストから切り離せないか

原因がSQLのN+1問題であれば、並列化しても根本解決になりません。

むしろDBへの同時アクセスが増えて、悪化する可能性もあります。

まずは基本的な高速化を行う

通常のWebサイトやWebアプリでは、並列処理より先に以下を見直すべきです。

  • SQL最適化
  • インデックス設計
  • キャッシュ
  • OPcache
  • CDN
  • 画像最適化
  • 不要な外部API呼び出しの削減
  • 重い処理のキュー化
  • PHP-FPM設定
  • サーバースペック

並列化は、これらの改善を行ったうえで、なお処理時間が問題になる場合に検討するのが現実的です。

実務での選び方

PHPで並列処理を行う場合は、目的に応じて適切な方法を選ぶ必要があります。

通常のWebサイトやCMSの場合

通常のコーポレートサイト、メディアサイト、WordPressサイト、LPなどでは、PHPのマルチスレッド化はほとんど不要です。

優先すべきなのは、キャッシュ、DB最適化、画像軽量化、CDN、サーバー設定の見直しです。

LaravelなどのWebアプリの場合

LaravelやSymfonyなどのWebアプリケーションでは、まずキュー処理を検討します。

メール送信、外部API連携、CSV処理、PDF生成、通知処理などはジョブ化し、複数ワーカーで処理するのが実務的です。

CLIバッチの場合

Linuxサーバー上でCLIバッチを動かす場合は、pcntl_fork()によるマルチプロセス化や、複数コマンドによる分散実行が候補になります。

大量データ処理では、対象データを分割し、同時実行数を制限することが重要です。

外部API待ちが多い場合

外部APIやHTTP通信の待ち時間が多い処理では、非同期処理が向いている場合があります。

複数のAPIリクエストを同時に投げ、返ってきたものから処理することで、待ち時間を短縮できます。

CPU負荷が高い場合

画像変換、大量計算、動画処理などCPU負荷が高い処理では、非同期処理だけでは不十分です。

この場合は、マルチプロセス、複数ワーカー、別サーバー、コンテナ分散などを検討します。

本格的なスレッド処理が必要な場合

どうしてもPHP内でスレッドベースの並列処理を行いたい場合は、parallel拡張を検討します。

ただし、ZTS有効のPHPが必要で、環境構築や運用の難易度が上がります。

一般的なWeb制作案件やCMS案件では、最初の選択肢にする必要はあまりありません。

PHPのマルチスレッド化に関するまとめ

PHPでも、マルチスレッドや並列処理は可能です。

ただし、一般的なWebアプリケーションでPHPコードを直接マルチスレッド化するのは、あまり標準的な方法ではありません。

現在、PHPで本格的なスレッド処理を行うならparallel拡張が候補になります。

ただし、ZTS有効のPHPが必要で、導入できる環境は限られます。

過去に使われていたpthreadsは、現在の新規開発では基本的に避けるべきです。

実務では、Webアプリならキュー処理、CLIバッチならpcntl_fork()によるマルチプロセス化、API待ちが多い処理なら非同期処理、大量データ処理なら複数ワーカーや外部プロセス化を検討するのが現実的です。

重要なのは、何でもマルチスレッド化することではありません。

PHPで処理を高速化したい場合は、まずボトルネックを特定し、SQL、キャッシュ、I/O、外部API、CPU、メモリのどこに問題があるのかを確認することが大切です。

そのうえで、処理の性質に合わせて、スレッド、プロセス、キュー、非同期処理を適切に使い分けることが、安定したPHPアプリケーション開発につながります。

以上、PHPのマルチスレッド化についてでした。

最後までお読みいただき、ありがとうございました。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!
目次