web-dev-qa-db-ja.com

PHP / MySQLアプリケーションでマルチコアCPUをどのように活用しますか?

カスタムビルドのCMSのようなアプリケーションを管理しています。

ドキュメントが送信されるたびに、いくつかのタスクが実行されます。これらのタスクは、大まかに次のカテゴリに分類できます。

  1. MySQLクエリ。
  2. HTMLコンテンツの解析。
  3. 検索インデックスの更新。

カテゴリ1には、ドキュメントのコンテンツに関連するさまざまなMySQLテーブルの更新が含まれます。

カテゴリ2には、いくつかの自動アンカータグ変換を実行するためのMySQL LONGTEXTフィールドに格納されたHTMLコンテンツの解析が含まれます。このタスクにはかなりの計算時間が費やされていると思います。

カテゴリ3には、ドキュメントに対応する少数のフィールドのみを使用した、単純なMySQLベースの検索インデックスの更新が含まれます。

ドキュメントの提出が完了したと見なされるには、これらのタスクをすべて完了する必要があります。

このアプリケーションをホストするマシンには、デュアルクアッドコアXeonプロセッサ(合計8コア)が搭載されています。ただし、ドキュメントが送信されるたびに、実行されるすべてのPHPコードは、コアの1つで実行される単一のプロセスに制限されます。

私の質問:

PHP/MySQL Webアプリケーションの処理負荷を複数のCPUコアに分割するために使用したスキームはありますか?私の理想的なソリューションは、基本的にいくつかのプロセスを生成し、それらをいくつかのコアで並列に実行し、すべてのプロセスが完了するまでブロックします。

関連する質問:

お気に入りのPHP=パフォーマンスプロファイリングツール?

32
jkndrkn

PHPはマルチスレッド向けではありません。既にお気づきのように、各ページは1つのPHPプロセスによって処理されます。クエリはデータベースサーバーで実行されます。

残念ながら、それに対してできることはあまりありません。それはPHPが機能する方法です。


それでも、いくつかの考えがあります。

  • まず、サーバー上に一度に1人以上のユーザーがいる可能性があります。つまり、同時に複数のページを提供します。つまり、複数のPHPプロセスとSQLクエリを同時に実行しています...これは、サーバーのいくつかのコアが使用されることを意味します。
    • 各PHPプロセスは、1人のユーザーのリクエストに応じて1つのコアで実行されますが、Apacheのサブプロセスがいくつか並行して実行されます(リクエストごとに1つ、構成に応じて数十または数百)
    • MySQLサーバーはマルチスレッドです。つまり、複数の異なるコアを使用して、複数の同時リクエストに応答できます-各リクエストを複数のコアで処理できない場合でも。

したがって、実際には、サーバーの8コアが使用されることになります;-)


そして、ページの生成に時間がかかりすぎると思われる場合、考えられる解決策は、計算を2つのグループに分けることです。

  • 一方では、ページを生成するために行わなければならないこと:それらのために、あなたができることはあまりありません
  • 一方で、時々実行する必要があるが、必ずしもすぐに実行する必要はない
    • たとえば、いくつかの統計計算について考えています。最新の状態にしたいのですが、数分遅れている場合は、通常は大丈夫です。
    • 電子メールの送信についても同じです。とにかく、ユーザーがメールを受信/閲覧するまでに数分かかるため、すぐに送信する必要はありません。

私の2番目のポイントのような状況では、あなたはそれらのことをすぐに行う必要はありません...まあ、ただすぐにそれらをしないでください;-)
私がよく使用する解決策は、キューイングメカニズムです。

  • Webアプリケーションは、「todoリスト」に物事を保存します
  • そして、その "todo-list"は、cronjobを介して頻繁に実行されるいくつかのバッチによってデキューされます

また、他のいくつかの操作については、X分ごとに実行したいだけです。ここでも、cronjobは完璧なツールです。

37
Pascal MARTIN

前書き

PHPには完全な マルチスレッド がサポートされており、さまざまな方法で最大限に活用できます。さまざまな例でこのマルチスレッド機能を実証できました。

クイック検索 は追加のリソースを提供します。

カテゴリー

1:MySQLクエリ

MySQLは完全にマルチスレッド であり、オペレーティングシステムがそれらをサポートしている場合、複数のCPUを使用します。パフォーマンス用に適切に構成されていれば、システムリソースを最大化します。

スレッドのパフォーマンスに影響するmy.iniの典型的な設定は次のとおりです。

thread_cache_size = 8

thread_cache_size は、多くの新しい接続がある場合にパフォーマンスを向上させるために増やすことができます。通常、これは、適切なスレッド実装がある場合、顕著なパフォーマンスの改善を提供しません。ただし、サーバーが毎秒数百の接続を認識している場合、通常、thread_cache_sizeを十分に高く設定して、ほとんどの新しい接続がキャッシュされたスレッドを使用するようにします。

Solaris を使用している場合は、次を使用できます。

thread_concurrency = 8 

thread_concurrency を使用すると、アプリケーションはスレッドシステムに、同時に実行する必要のあるスレッドの数に関するヒントを与えることができます。

この変数はMySQL 5.6.1で廃止され、MySQL 5.7で削除されました。 Solaris 8以前の場合を除いて、MySQL構成ファイルからこれを削除する必要があります。

InnoDB:

Innodb を使用している場合、このような制限はありません

innodb_thread_concurrency //  Recommended 2 * CPUs + number of disks

innodb_read_io_threadsinnodb_write_io_threadsを確認することもできます。デフォルトは4で、ハードウェアに応じて64まで増やすことができます

その他:

見るべき他の構成には、key_buffer_sizetable_open_cachesort_buffer_sizeなどがあります。これらはすべて、より良いパフォーマンスをもたらします。

PHP:

Pure PHPでは、各クエリが別々のPHPスレッドで実行されるMySQLワーカーを作成できます

$sql = new SQLWorker($Host, $user, $pass, $db);
$sql->start();

$sql->stack($q1 = new SQLQuery("One long Query")); 
$sql->stack($q2 = new SQLQuery("Another long Query"));

$q1->wait(); 
$q2->wait(); 

// Do Something Useful

SQLWorkerの完全な動作例

2:HTMLコンテンツの解析

このタスクにはかなりの計算時間が費やされていると思います。

すでに問題を知っている場合は、イベントループ、ジョブキュー、またはスレッドを使用して簡単に解決できます。

1つのドキュメントを1つずつ処理することは、非常に、非常に遅く、痛みを伴うプロセスです。 @ ka ajaxを使用して複数のリクエストを呼び出す方法をハックすると、一部のクリエイティブマインドは pcntl_fork を使用してプロセスを分岐しますが、windowsを使用している場合pcntlを利用することはできません

ウィンドウとUnixシステムの両方をサポートするpThreadsを使用すると、このような制限はありません。 ..と同じくらい簡単です。100ドキュメントを解析する必要がある場合100スレッドを生成する...シンプル

HTMLスキャン

// Scan my System
$dir = new RecursiveDirectoryIterator($dir, RecursiveDirectoryIterator::SKIP_DOTS);
$dir = new RecursiveIteratorIterator($dir);

// Allowed Extension
$ext = array(
        "html",
        "htm"
);

// Threads Array
$ts = array();

// Simple Storage
$s = new Sink();

// Start Timer
$time = microtime(true);

$count = 0;
// Parse All HTML
foreach($dir as $html) {
    if ($html->isFile() && in_array($html->getExtension(), $ext)) {
        $count ++;
        $ts[] = new LinkParser("$html", $s);
    }
}

// Wait for all Threads to finish
foreach($ts as $t) {
    $t->join();
}

// Put The Output
printf("Total Files:\t\t%s \n", number_format($count, 0));
printf("Total Links:\t\t%s \n", number_format($t = count($s), 0));
printf("Finished:\t\t%0.4f sec \n", $tm = microtime(true) - $time);
printf("AvgSpeed:\t\t%0.4f sec per file\n", $tm / $t);
printf("File P/S:\t\t%d file per sec\n", $count / $tm);
printf("Link P/S:\t\t%d links per sec\n", $t / $tm);

出力

Total Files:            8,714
Total Links:            105,109
Finished:               108.3460 sec
AvgSpeed:               0.0010 sec per file
File P/S:               80 file per sec
Link P/S:               907 links per sec

使用クラス

Sink

class Sink extends Stackable {
    public function run() {
    }
}

LinkParser

class LinkParser extends Thread {

    public function __construct($file, $sink) {
        $this->file = $file;
        $this->sink = $sink;
        $this->start();
    }

    public function run() {
        $dom = new DOMDocument();
        @$dom->loadHTML(file_get_contents($this->file));
        foreach($dom->getElementsByTagName('a') as $links) {
            $this->sink[] = $links->getAttribute('href');
        }
    }
}

実験

スレッドのない8,714リンクを持つ105,109ファイルを解析して、どれくらい時間がかかるかを確認してください。

より良いアーキテクチャ

あまりにも多くのスレッドを生成することは、実稼働環境で行うのは賢明なことではありません。より良いアプローチは、 Pooling を使用することです。定義のプールを持っている Workers then stack with Task

パフォーマンスの改善

結構です、上の例はまだ改善できます。システムが単一のスレッドですべてのファイルをスキャンするのを待つ代わりに、複数のスレッドを使用してシステムでファイルをスキャンし、データを処理のためにワーカーにスタックすることができます

3:検索インデックスの更新

これは最初の回答でほぼ回答されていますが、パフォーマンスを改善する方法は非常に多くあります。イベントベースのアプローチを検討したことがありますか?

イベントの紹介

@ rdlowrey 引用1:

よくこのように考えてください。 Webアプリケーションで同時に接続された10,000のクライアントにサービスを提供する必要があると想像してください。従来の要求ごとのスレッドまたは要求ごとのプロセスサーバーarenスレッドがどれほど軽量であっても、一度に10,000個のスレッドを開いたままにできないので、オプションではありません。

@ rdlowrey 引用2:

一方、すべてのソケットを単一のプロセスに保持し、それらのソケットが読み取り可能または書き込み可能になるのをリッスンする場合、サーバー全体を単一のイベントループ内に配置し、読み取り/書き込みが必要な場合にのみ各ソケットを操作できます。

問題に対するevent-drivennon-blocking I/Oアプローチを試してみませんか。 PHP has libevent は、アプリケーションを過給します。

この質問はすべてMulti-Threadingですが、時間があれば、これを見ることができます Nuclear Reactor written in PHP by @ igorw

最後に

考慮

いくつかのタスクでCacheJob Queueの使用を検討すべきだと思います。あなたは簡単に言うメッセージを持つことができます

Document uploaded for processing ..... 5% - Done   

その後、バックグラウンドですべての時間を無駄にします。同様のケーススタディについては、 大きな処理ジョブを小さくする をご覧ください。

プロファイリング

プロファイリングツール? Xdebug から Yslow までのWebアプリケーション用の単一のプロファイルツールはありません。これらはすべて非常に便利です。例えば。 Xdebugはサポートされていないため、スレッドに関しては役に立ちません。

お気に入りがありません

57
Baba

Webサーバーのスケールアウトは、マルチコアCPUへのアクセスに関して、MySQLを1インチ動かしません。どうして?まず、MySQLの2つの主要なストレージエンジンを検討します。

MyISAM

このストレージエンジンは、複数のコアにアクセスしません。それは決して持っていないし、決してしません。 INSERT、UPDATE、およびDELETEごとにテーブル全体をロックします。 MyISAMで何かをするために複数のWebサーバーからクエリを送信すると、ボトルネックになります。

InnoDB

MySQL 5.1.38より前では、このストレージエンジンは1つのCPUのみにアクセスしていました。 1台のマシンでMySQLを複数回実行して、コアを強制してMySQLの異なるインスタンスを処理する のような奇妙なことをしなければなりませんでした。次に、WebサーバーのDB接続を複数のインスタンス間で負荷分散します。これは古い学校です(特に、MySQl 5.1.38より前のバージョンのMySQLを使用している場合)。

MySQL 5.1.38以降では、新しいInnoDBプラグインをインストールします。 InnoDBが複数のCPUにアクセスするために調整する必要がある機能があります。これについてはDBA StackExchangeで書きました。

これらの新機能は、MySQL 5.5/5.6およびPercona Serverでも完全に利用可能です。

警告

カスタムCMSがFULLTEXTインデックス作成/検索を使用している場合、InnoDBがFULLTEXTインデックス作成/検索をサポートするようになったため、MySQL 5.6にアップグレードする必要があります。

MySQL 5.6にインストールしても、CPUは自動的に動作しません。構成を変更しないと、古いバージョンのMySQLが新しいバージョンを実行し、新しいバージョンをアウトアウトする可能性があるため、調整する必要があります。

4
RolandoMySQLDBA

これはあなたが探している質問への答えではないかもしれませんが、あなたが求める解決策はスレッド化を扱っています。スレッド化はマルチコアプログラミングに必要であり、スレッド化はnotPHPで実装されています。

しかし、ある意味では、オペレーティングシステムのマルチタスク機能に依存することで、PHPのスレッドを偽造することができます。 PHP のマルチスレッド戦略により、必要なものを達成するための戦略を開発できます。

デッドリンク: PHPのマルチスレッド化戦略

2

考えているときに皆さんに知らせる: "poor PHPにはマルチスレッドがありません"

まあ... Pythonには実際のマルチスレッドもありませんNodeJSはマルチスレッドをサポートしていません 。 Javaにはマルチスレッドのようなものがありますが、それでも コードによってはマシン全体が停止する

しかし、1つの事柄を大々的にプログラミングしない限り、それは無関係です。多くのリクエストがページにヒットし、各リクエストは独自の単一スレッドで独自のプロセスを生成するため、すべてのコアが使用されます。

1
Toskan