8

これは、MySQL PDO プリペアド ステートメントを使用した場合と、単純なクエリを使用した場合のパフォーマンスの低下を簡単に把握するために実行した簡単なテストです。person テーブルには 2801 行あります。MySQL バージョン 5.5.28 および PHP バージョン 5.3.15。デフォルトのパラメーターが何であれ、バニラのインストール。テストは 8GB の iMac で実行されます。

$pdo = new PDO('mysql:host=localhost;dbname=cwadb_local', 'root', "");
$start = microtime(true);
for ($i = 0; $i < 200; $i++) {
    $pdo->query("select * from person where name_last = 'smith' or true");
}
echo "<p>query: " . (microtime(true) - $start);

$start = microtime(true);
for ($i = 0; $i < 200; $i++) {
    $stmt = $pdo->prepare("select * from person where name_last = :last or true");
    $stmt->execute(array('last' => 'smith'));
}
echo "<p>prepare/execute: " . (microtime(true) - $start);

これが出力でした:

query: 21.010436058044

prepare/execute: 20.74036192894

これはまったくペナルティを示しません。可能性:

  • 準備されたステートメントのキャッシュは実際に機能しています。(準備関数をループ内に保持していることに注意してください。)

  • 単純すぎるので、偽のテストです。

  • 準備/実行を遅くすべきという理論的な理由はありません。また、絶え間ない批判にうんざりして、MySQL/PDO/PHP の開発者は、私たち全員を黙らせようとして、より速くするために多大な努力を払ってきました。

  • 他の?

プリペアド ステートメントを使用する方がクエリを使用するよりも安全であり、PDO の名前付きパラメーター (Mysqli にはありません) を使用すると、パラメーターを処理するのが非常に便利であると、ここで何度も言われています。ただし、ステートメントを実行するたびにステートメントを準備する必要がある場合、パフォーマンスが低下することもよく指摘されています。

それで、誰かが私の単純なテストと矛盾するいくつかのテストを提供できますか? それとも、プリペアード ステートメントを使用しない理由がないことを認めましょうか?

4

2 に答える 2

11

言及すべき小さなことが1つあります。デフォルトでは、PDO は準備済みステートメントをエミュレートするだけです。
そして、エミュレーションモードでは、実際に単一のステートメントを準備せずに同じ古いクエリを実行します:)

ですから、まず第一に、

$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, FALSE);

実際の準備済みステートメントをオンにします。

パフォーマンスのペナルティがあることはよく知られています

言及する別の小さなことがあります。悲しいことに、世界には真の知識は
ほとんどありません。特に Q&A サイトの世界では。人は、自分が読んで妥当だと思った情報を繰り返す傾向があります。証明するためのテストを実行せずに、または手を差し伸べることさえせずに。したがって、「よく指摘される」というのは、信頼できる情報源とはみなされません。

話に戻りますが、多少のペナルティはあるはずですが、たいていは取るに足らないものです。そうであれば、システムを調整する必要があります。

とにかく、エミュレーションモードでは、「高速」で安全です。

更新
さて、私のデータでテストを実行した後、大きなデータセットで 3 倍の差がある場合、データベースに問題があると言わざるを得ません。

ライトニングクエリの場合

select title from Board where id = 1

結果は

emulation   on      off
query      0.07    0.130
prepare    0.075   0.145

一方、非常に面倒なクエリについては

select title from Board where id > 1

結果は

emulation   on      off
query      0.96    0.96
prepare    0.96    1.00

したがって、ご覧のとおり、大規模なデータセットでは違いが目立たなくなります。

ライトニング クエリには多少の違いがありますが、(単一のクエリに対して) 0,0003 秒しかかからないため、「無関心」という言葉の完璧な例だと思います。

query()/prepare() の間で同等の結果を得るには、1 つのアイデアしかありません。PDO は、バインディングのないものであっても、すべてのクエリに対して準備/実行を使用します。

次に、エンコードの問題に進みます。

はい、奇妙な GBK の問題は 5.3.3 より前のバージョンの PDO に影響します。これらのバージョンには、適切なエンコーディングを設定する方法がなく、(エミュレーション モードで) 脆弱性が避けられませんでした。しかし、5.3.3 以降の PDO は DSN でのエンコーディングの設定をサポートしており、今ではすべて問題ありません。
mysqli の場合mysqli_set_charset()、まさにこの目的のために使用する必要があり、まったく同じ (不可解な) 結果が得られます。

mysqli に基づく私自身のクラスでは、独自のプレースホルダー実装を使用しており、準備済みステートメントをまったく使用していません。パフォーマンス上の理由ではなく、信頼性の向上のためです。

于 2013-01-05T00:16:44.963 に答える
4

私はあなたの方法論にいくつかの問題があります:

  1. スクリプトと同時にサーバー上で実行されているものがまったくない場合を除いて、基本的なタイマーはCPUスケジューリングの気まぐれの影響を受けます。これに対処するには、2つの別個のスクリプトを記述し、*nixのtimeコマンドを使用してそれらを実行します。time php myscript.php
  2. スクリプトの順序を逆にすると、mySQLがクエリをキャッシュするため、同じ結果が生成される場合があります。
  3. それぞれ1つのテストは診断を行いません。各スクリプトを数百回または数千回実行してから、結果を平均して、よりバランスの取れた結果を取得してください。

ただし、すべての入力を常に厳密に検証し、SQLインジェクションの可能性がある場合を除いて、非静的クエリの場合にプリペアドステートメントを使用しない理由はありません。

于 2013-01-04T23:59:56.730 に答える