2

以下のコードは、完全に構築されたページとデータベースクエリの両方をキャッシュするための許容可能な方法を示していますか?

ビルドされたページのキャッシュは__construct、コントローラーので開始され、で終了し__destructます。この例では、すべてのページがデフォルトで15分間ファイルにキャッシュされます。

クエリのキャッシュはで行われ、apcクエリごとに指定された時間メモリに保存されます。実際のサイトには、必要に応じて変更できるように、apcキャッシュ用の別のクラスがあります。

私の目的は、可能な限り最も単純なMVCを構築することでしたが、失敗したのでしょうか、それとも正しい方向に進んでいるのでしょうか。

コントローラ

//config
//autoloader
//initialiser - 

class controller {

    var $cacheUrl;

    function __construct(){

        $cacheBuiltPage = new cache();
        $this->cacheUrl = $cacheBuiltPage->startFullCache();
    }

    function __destruct(){

        $cacheBuiltPage = new cache();
        $cacheBuiltPage->endFullCache($this->cacheUrl);
    }
}

class forumcontroller extends controller{

    function buildForumThread(){

        $threadOb = new thread();
        $threadTitle = $threadOb->getTitle($data['id']);

        require 'thread.php';
    }
}

モデル

class thread extends model{

    public function getTitle($threadId){

        $core = Connect::getInstance();
        $data = $core->dbh->selectQuery("SELECT title FROM table WHERE id = 1");

        return $data;
    }
}

データベース

class database {

    public $dbh;
    private static $dsn  = "mysql:host=localhost;dbname=";
    private static $user = "";
    private static $pass = '';  
    private static $instance;

    private function __construct () {
        $this->dbh = new PDO(self::$dsn, self::$user, self::$pass);
    }

    public static function getInstance(){
        if(!isset(self::$instance)){
            $object =  __CLASS__;   
            self::$instance = new $object;
        }
        return self::$instance;
    }

    public function selectQuery($sql, $time = 0) {

        $key = md5('query'.$sql);

        if(($data = apc_fetch($key)) === false) {

            $stmt = $this->dbh->query($sql);
            $data = $stmt->fetchAll();

            apc_store($key, $data, $time);
        }
        return $data;
    }
}

キャッシュ

class cache{

    var url;

    public function startFullCache(){

        $this->url = 'cache/'.md5($_SERVER['PHP_SELF'].$_SERVER['QUERY_STRING']);   

        if((@filesize($this->url) > 1) && (time() - filectime($this->url)) < (60 * 15)){
            readfile($this->url);
            exit;
        }

        ob_start();

        return $this->url;
    }

    public function endFullCache($cacheUrl){

        $output = ob_get_contents();
        ob_end_clean();

        $output = sanitize_output($output);

        file_put_contents($cacheUrl, $output);

        echo $output;
        flush();
    }

}

意見

<html>
<head>
<title><?=$threadTitle[0]?> Thread - Website</title>
</head>
<body>

    <h1><?=$threadTitle[0]?> Thread</h1>

</body>
</html>
4

4 に答える 4

8

キャッシングを「アウトソーシング」する

まず、GETリクエストのキャッシュは通常インターネット全体で行われることを理解する必要があります。特に、ユーザーが何らかのプロキシを介して接続している場合。

また、有効期限を長く設定して、ユーザーのブラウザがHTMLページやメディアファイルのキャッシュを実行できるようにする機能もあります。

これを調べ始めるには、これら 2つの記事を読む必要があります。

あなたの目標は何ですか?

キャッシュの追加を開始する前に、実際にキャッシュが必要であることを確認してください。ベンチマークを実行して、ボトルネックを調べます。最適化のための最適化は無意味であり、しばしば有害です。

アプリケーションがクエリキャッシングにジャンプするのではなく、SQLクエリを多すぎることを知っている(そしてそれをバックアップするデータがある場合..それは「感じ」ではない)場合は、それらのクエリが何のためにあるかを調べることから始める必要があります。

例:タグクラウド
を生成するためだけに各ページビューで低速クエリを実行している場合は、代わりに、すでに完成したタグクラウドを(HTMLフラグメントとして)保存し、何かが変更された場合にのみ更新する必要があります。

また、パフォーマンスを向上させる場合、 「キャッシュの追加」が最初のステップになることはありません。クエリが遅い場合は、を使用EXPLAINして、インデックスが適切に使用されているかどうかを確認してください。同じデータを複数回クエリしていないことを確認してください。また、クエリが実際に意味があるかどうかも確認してください。

これはMVCではありません

どこでこのように書くことを学んだのかわかりませんが、MVCの要点をすべて見逃しているようです。

  • 「ビュー」はテンプレートではありません
  • 「モデル」はデータベースの抽象化ではありません
  • 「コントローラー」はアプリケーションロジックではありません

また、「レイヤー」という言葉の意味が欠けているようです。「クラス」の同義語ではありません。レイヤーは、同様の状況で再利用可能な再利用可能なコンポーネントのグループです[1]

あなたはこれこの投稿を読むことから利益を得るかもしれません。これらは、このアーキテクチャパターンの基本を理解するのに役立つはずです。

MVCアーキテクチャのどこにキャッシュしますか?

MVC(またはMVCに触発された)アーキテクチャーを使用する場合、キャッシングを実行できるポイントは基本的に2つあります。ビューと永続性ロジックです。

ビューのキャッシュは、ほとんどの場合、一度レンダリングされたテンプレートの再利用を伴います(MVCの各ビューは、複数のテンプレートと関連するUIロジックをジャグリングします)。前のタグクラウドの例を参照してください。

永続化ロジックでのキャッシュは、実装によって異なります。

  • 次のサービスを使用して、ドメインオブジェクトに渡されることになっているデータをキャッシュできます。

    注:実際のアプリケーションでは、newインスタンスはここにはありません。代わりに、いくつかの工場を使用します

    $user = new User;
    $user->setId( 42 );
    
    $cache = new Cache;
    
    if ( !$cache->fetch( $user ))
    {
        $mapper = new UserMappper;
        $mapper->fetch( $user );
    }
    
    $user->setStatus( User::STATUS_BANNED );
    $cache->store( $user );
    $mapper->store( $user );
    
    // User instance has been populated with data
    
  • 単純なマッパーの使用を超えて永続性レイヤーを拡張する場合、キャッシュのもう1つのポイントはリポジトリーやIDマップです。簡単なコード例で説明するのは難しいでしょう。代わりに、Patterns of EnterpriseApplicationArchitectureの本を読む必要があります。

..コード内の他のいくつかの悪い習慣:

  • DB接続を確立するためのシングルトンの使用を停止してください。単体テストを作成できなくなり、クラスの特定の名前と緊密に結合します。代わりに、このアプローチを使用して、それを必要とするクラスにDB接続を挿入することをお勧めします。

  • また、クエリにパラメータがない場合は、準備しても意味がありません。プリペアドステートメントはSQLにデータを渡すためのものですが、どの例にもパラメータはありません。

    この問題は主に、魔法のデータベースクラスが原因で発生します。代わりに、複数のデータマッパーで永続ロジックを分離する必要があります。このようにして、すべてのクエリに対して単一のメソッドを使用するという自己負担の問題に直面することはありません。

  • キーワードはPHP4のvarアーティファクトです。最近は、、を使用publicprivateprotectedいます。

  • コードに接続の詳細をハードコーディングしています。これは基本的にOCPの違反です(ダムダウンバージョン:ここ)。

于 2014-01-15T09:46:44.063 に答える
3

アップデート

これは、PHP mvcでクエリとビルドページの両方をキャッシュするための許容可能な方法ですか?

ビルドされたページのキャッシュは、コントローラーの__constructで開始され、次に__destructで終了します。この例では、すべてのページがデフォルトで15分間ファイルにキャッシュされます。

すべての参照が解放されたとき、またはスクリプトが終了したときに、デストラクタが呼び出されます。これは、スクリプトが正しく終了したことを意味すると思います。重大な例外は、デストラクタが呼び出されることを保証するものではないと思います。

例えば

 class A 
 {
    public function __construct()
    {
       echo "Construct\n";
    }

    public function __destruct()
    {
      echo "Destruct\n";
    }
 }

テストコード:

   $test = new A();
    die( "Dead\n");  // Will output Construct; dead; Destruct

   $test = new A();
   throw new Exception("Blah\n"); // Construct, Fatal error (no destruct)

   $test = new A();
   require_once( 'invalid_file.php'); // Construct, Fatal error (no destruct)

確実に実行するには、 register_shutdown_function()を使用することをお勧めします。しかし注意してください

register_shutdown_function()を複数回呼び出すことができ、それぞれが登録されたのと同じ順序で呼び出されます。1つの登録済みシャットダウン関数内でexit()を呼び出すと、処理は完全に停止し、他の登録済みシャットダウン関数は呼び出されません。」他のシャットダウン関数が実行された場合にシャットダウン関数が呼び出される保証はありません。

私見ですが、あなたは車輪の再発明を行っています。すでに多くのフレームワークがあります。シンプルで小さいものが欲しいですか?さてSilexがあります

これはSymfonyを作成したのと同じ人から作られています。あなたがあなたの時間を無駄にするだろうと私を信じてください。

または、Laravelを使用してみませんか?必要なパッケージのみを選択できます。すべてのフレームワークがすでに適切に設計されたキャッシングシステムを備えているわけではありません。http://laravel.com/docs/cacheを参照してください。

MVCスタイルでキャッシングがどのように行われるかを理解するために、Symfony2のドキュメントには非常に良い説明があります

この質問も読んでくださいMVCフレームワークでのキャッシュ戦略

これがどれほど必要かという点では、それは本当にあなたの状況に依存します。私の経験では、多数のユーザー(たとえば、同時にサイトに数十人以上)がいることを期待しているのでない限り、キャッシュは必要ありません。一方、StackOverflowのようなサイトは、非常によく考えられたキャッシュ戦略なしでは機能できません。


How would you create a build function to cache the entire built page?

私があなたの質問から理解したように、あなたはページ全体をキャッシュしたいですか?

How would you create a build function to cache the entire built page?

これを実現するには、出力バッファリングを使用するだけです。

例として、index.phpコードがある場合があります。

<?php

 ob_start();   

 // All your mvc and application logic here

 $output = ob_get_contents();
 ob_end_clean();

ob_start()とob_get_contents()の間のページ出力全体が$outputにキャプチャされるようになりました

あなたはそれをファイルに保存して、あなたがやりたいことを何でもすることができます

PHP Webサイトでob_get_contents()の詳細を読むことができます

于 2014-01-15T00:47:43.743 に答える
2

それはすべて、パフォーマンスの問題がどこにあるかによって異なります。それらがDBクエリ内にある場合は、それらをキャッシュします。ただし、もちろん、コントローラーはダーティデータに備える必要があります。

コントローラレイヤーでデータをキャッシュする場合は、パフォーマンスは向上しますが、キャッシュするデータはおそらく多くなります(DBデータはHTMLよりも小さくなります)。そしてもちろん、ユーザーはダーティデータを見る準備をする必要があります。

残念ながら、ソリューションごとに要件が異なるため、厳密なルールを設定することはできません。私のアドバイスは、本当に必要なときにだけデータのキャッシュを開始することです。パフォーマンスのボトルネックがどこにあるかを詳しく調べて、キャッシュを適切に実行し、上向き(マシンの仕様を増やす)だけでなく、外向き(より多くのマシン)にスケーリングできることを確認します。

于 2013-02-21T22:33:30.180 に答える
1

CodingInsaneが投稿したものに追加します!

彼はまさにこのようにして、あなた自身の小さなキャッシュメカニズムを簡単に作ることができます。

$ outputのコンテンツ(CodingInsaneの投稿内)をthe_file.php.cacheのようなファイルに書き込み、次にthe_file.phpでthe_file.php.cacheのコンテンツを読み取り、ユーザーに表示して終了します。

ただし、キャッシュメカニズムには、更新(ページの再構築)の方法が必要です。これを行うには、コンテンツが変更されたかどうかを追跡するか、コンテンツが最後に変更された時刻を確認して、the_file.php.cacheが最後に変更されたときと比較する必要がありますfilemtime()。

幸運を!

于 2014-01-15T01:21:08.003 に答える