9

PHP で書かれた e コマース ソフトウェア プロジェクトを継承しました。コードベースを調べていると、コード全体に多くの SQL ステートメントが見つかりました。Product、Category、User、Customer などの多くのクラスがあり、すべてのクラスには多くのデータベース クエリがあります。

この状況をどのように処理すればよいかわからなかったので、1 回のページ訪問の合計クエリ数をカウントすることにしました。MySQL クエリ関数をカプセル化し、カウンターを増やしました。

この結果には少しビックリしました。インデックス ページだけにアクセスするには、1633 (!) 回の MySQL 選択クエリが実行されました。あるカテゴリの製品をリストすると、約 2000 のクエリがトリガーされました。

クエリをテキスト ファイルにパイプして分析しました。90% 以上が、おそらく 1 つまたは 2 つの値の単一選択ステートメントです。では、この混乱を解消するにはどうすればよいでしょうか。あなたのアドバイスは何ですか?MySQL サーバーでキャッシュを有効にしました。ページの読み込みには約 490 ミリ秒かかります。

追加の詳細

たとえば、Product というクラスがあります。このクラス内には、単一の小さな SQL 選択ステートメントが 8 つあります。

製品を表示するためにカテゴリ リストを開くと、元のプログラマは 1 つの select ステートメントを使用して必要な製品のリストを取得し、それぞれの製品オブジェクトを作成しました。

この結果から 20 個の製品が得られたとします。

select id from products where price <= 10;

次に、結果を繰り返し処理し、エントリごとに製品オブジェクトを作成します。

$qresult = query("select id from products where price <= 10");
$products = array();
foreach ($qresult as $prod) {
  $products[] = new Product($prod['id']);
}

これだけでも、製品に対して 20 * 8 個の SQL クエリが生成されます。また、同じメソッドが他のクラス (User、Customer、Category など) にも使用されます。

少し前に

さて、数週間または数か月が経過した後、これまでに行った解決策を共有したいと思いました.

ページへのアクセスごとにクエリを 50 以下に削減し、ページの読み込み時間を 400 ミリ秒未満に短縮できました。

とても簡単にできました。ホットスポットを特定し、テーブル キャッシュ クラスを構築しようとしました。この静的クラスにアクセスするたびに、テーブルのコンテンツ全体がメモリに読み込まれ、それ以降の各テーブル リクエストは、静的クラスからメモリから提供されます。非常に汚れていて、それほど良くはありませんが、機能し、高速であり、クエリの合計を減らし、サーバー ハードウェアを節約します。

これまでのようにユーザー数がその比率で増加する限り、ハードウェアもこの問題に直面すると思います。

アプリケーションを別のアプリケーションに置き換える場合は、間違いなくデータベース-クエリ-ナイス ソリューションに向かいます。

皆さんのアドバイスに感謝します

4

4 に答える 4

1

まず最初に特定する必要があるのは、php レルムにあり、これらの呼び出しのうち何回以上実行されているかです。それだけで数が減ります。

それ以外にできることは2つ。

  1. 同じパラメーター (productId など) を使用する複数のクエリがあり、製品テーブル、製品カテゴリ テーブルなどを使用する場合、いつでもそれらのクエリを結合して、必要なものすべてを含む 1 つの結果を取得できます (または、最も一般的なクエリが結合され、それで動作します)
  2. 設定テーブルがある場合、最善の方法はすべてのテーブルをメモリにロードし、そこからすべての値を読み取ることです。これにより、設定が必要なときにいつでも DB にアクセスする必要がなくなります。
于 2013-02-27T13:44:52.670 に答える
1

あなたが心配するのは正しいと思います。2,000 クエリは多いようです。

クエリの量とページの応答時間という 2 つのリファクタリングの正当な理由を既に特定しました。どちらも測定可能であり、リファクタリングに適しています。

確かに MySQL にはクエリ キャッシュがあり、キャッシュからクエリを実行できる場合、通常は基礎となるデータを毎回クエリするわけではありませんが、MySQL と通信するためのネットワーク コストが依然として存在する可能性があります。

値をメモリに保存したり、セッション変数を使用したりすることを考えたことがありますか?

<?php
session_start();
// store session data
$_SESSION['sharedvalue']= "result of common MySQL query"
?>

< html>
< body>

< ?php
//retrieve session data - now each time you do this it isn't asking MySQL again
echo "Common Value=". $_SESSION['sharedvalue'];
?>

< /body>
< /html>

別の解決策は、独自のキャッシュを持ち、そこから共通の値をクエリして、古いデータを更新するためにそれらを変更または期限切れにすることです。これは、実際にはアプリケーションのサイズとユーザー ベースに依存します。

于 2013-02-27T13:45:34.447 に答える
1

私が取り組んでいるレガシーアプリでも同じ問題があり、同じレベルのクエリの非効率性があります! 短期的には、問題にハードウェアを投入するだけですが、少なくとも今後の新しい作業はより適切に作成されます。必要な機能のないカスタム ORM、または最適な方法で使用されていない ORM のように聞こえます。

これをリファクタリングしてみてください:

$products = array();
foreach ($products as $prod) {
  $products[] = new Products($prod['id']);
}

これに:

// Assuming products is an array of arrays, with each inner array
// containing all the values that make up an array
$products = array();
foreach ($products as $prod) {
  $products[] = Products::convertToObject($prod['id']);
}

これにより、ループごとに N 個のクエリを節約できます。Products各 ORM クラス (など) が次のようなものを確実に継承するBaseORM場合、これを行うための共通コードは 1 回だけ記述できます。

結合の方法があまりないことがわかった場合は、現在のコードを分岐して、自作の ORM を Propel や Doctrine などに置き換えてみてください。あなたが知らない ORM システムを使用するには学習曲線があることに注意してください。

于 2013-02-27T14:36:15.957 に答える
0

おそらく、反復ごとに mysql_query を呼び出す 1 つ (または複数) のループ (foreach または while など) があります。

そのループを見つけたら、それを最適化する方法を決定できます。たとえば、レコードを一度に配列にロードすると、毎回データベースを呼び出すのではなく、ループが配列で機能します。

于 2013-02-27T13:42:58.617 に答える