如何最小化对数据库的调用量?

时间:2014-06-09 17:33:26

标签: php mysql oop

我有很多图像进入mysql数据库,这些图像被绘制成:<img src="/PrintImage.php?$id&width&height&quality" />

问题是,对于每个图像,我通过类调用数据库。为了更好地解释它,这里是代码:

已编辑:完全发布课程代码

HTML视图(示例):

<img src="/PrintImage.php?id=10&width=120&height=120&quality=100" />
<img src="/PrintImage.php?id=20&width=120&height=120&quality=100" />

PrintImage.php

<?php
include_once($_SERVER['DOCUMENT_ROOT'].'/php/classes/Galerias.php');
$a = new \Galerias();

$i = $_GET['i'];
$w = $_GET['w'];
$h = $_GET['h'];
$q = $_GET['q'];

$a->Pintar($i, $w, $h, $q);

unset($a);

Gallery.php

<?php
namespace Galerias;
include_once 'Database.php';
include_once 'Utils.php';
use \Database;
use \Utils;

class Galerias {

    private $img_max_width = 1024;
    private $img_max_height = 768;
    private $img_quality = 85;

    function __construct(){
        $this->Database = new Database();
        $this->Utils = new Utils();
    }

    public function Pintar($id, $width, $height, $quality){
        $query = "select (select titulo from imagenes where id=$id) as titulo, (select imagen from imagenes where id=$id) as imagen";
        $data = $this->Database->GetData($query);
        $titulo = $data[0]['titulo'];
        $tmp = $data[0]['imagen'];
        $dataimg = $this->Utils->formatImage("string", $tmp, $width, $height, false);
        $mime = $dataimg[1];
        header("Content-type: $mime");
        header("Content-Disposition: inline; filename=$titulo");
        imagejpeg($dataimg[0], null, $quality);
    }
}

database.php中

    <?php
namespace Database;
include_once 'Utils.php';
use \Utils;

class Database {

    private $host = "localhost";
    private $user = "user";
    private $pass = "pass";
    private $daba = "database";
    private $link;
    public $Utils;

    function __construct(){
        $this->Open();
        $this->Utils = new Utils();
    }

    function __destruct(){
        error_log('Database destruct');
    }

    private function Open(){
        $this->link = mysql_connect($this->host, $this->user, $this->pass);
        error_log('open succeeded');
        mysql_select_db($this->daba, $this->link) or $this->Utils->newEx("Class Database->Open(): ".mysql_error());
    }
    private function Close(){
        mysql_close($this->link) or $this->Utils->newEx("Class Database->Close(): ".mysql_error());
        error_log('close succeeded');
    }

    public function GetData($query){
        $data = array();
        $query = mysql_query($query) or $this->Utils->newEx("Class Database->GetData(): ".mysql_error(), true);
        while( $result = mysql_fetch_array($query) ){
            $data[] = $result;
        }
        $this->FreeResults($query);
        return $data;
    }

    public function InsertData($query){
        if( !mysql_query($query) ){
            $this->Utils->newEx("Class Database->InsertData(): ". mysql_error(), true);
            return false;
        }
        return true;
    }

    public function DeleteData($query){
        if( !mysql_query($query) ){
            $this->Utils->newEx("Class Database->DeleteData(): ". mysql_error(), true);
            return false;
        }
        return true;
    }

    public function FreeResults($res = null){
        if( !is_null($res) ) mysql_free_result($res);
    }
}

我正在多次调用数据库,因为每个图库需要加载~50 / 100个图像。 问题是,我该如何有效地完成这项任务?提前致谢

2 个答案:

答案 0 :(得分:2)

您真正的问题是:如何最大限度地减少对数据库的调用量。

数据存储在数据库中,因此必须进行调用以检索数据。但是你需要问自己,我真的每次都需要问这个问题吗?

在当前对象模型中,仅在创建对象时检索每张图片的唯一数据。每次您想要阅读信息时,这将添加一个电话。这是一个非常常见的错误,并且(正如您已经意识到的那样)根本无法扩展。

您需要的是在调用之前使对象已拥有数据。有很多方法可以做到这一点。只要有活跃的开发人员,对象工厂,缓存对象,内存缓存,redis列表就可以了。 我邀请你试着在盒子外思考并找到解决方案。因为如果您了解问题并解决问题,您将更好地掌握对象模型和陷阱。

让我们再拿一遍,你确定你需要在构造对象时进行数据库调用吗?如果您知道所需数据的子集,则应批量询问。这将删除对数据库的大量查询。

也许Galerias可以在一次抓取大量Pintar的情况下发挥作用?

我知道这不是“做这个”的答案,可能会被投票。但至少尝试:)。

另外:永远不要直接使用查询参数$ _GET $ _POST而不先清理它们!

答案 1 :(得分:1)

最后,我得到了逻辑系统来减少 - 相当于 - 对数据库的调用量。之前,我使用PHP文件作为图像src直接从数据库打印图像(带有标题等)。这是一个可怕的失败。要解决的问题是:

  1. 我需要使用PHP文件作为图像src,传递ID,宽度,高度和质量。当然,它必须是一个友好的uri。所以我不能使用base64编码来打印图像。它必须是一个PHP文件(其他文件,其他进程,它没有连接到第一个文件)。

  2. 我使用的是共享主机,加载像memcache这样的扩展程序的想法是不可行的。

  3. 问题2告诉我,我无法在任何地方保存图像(或其他数据)以便在网站上使用。 (这是错的)。

  4. 我所做的(在思考,搜索和思考之后......)是使用$_SESSION存储,以前序列化所有图像。所以,我解决了所有问题。现在,必须寻找一个有效的逻辑来填充代码。结果如下:

    // Session Cache Class (static)
    
    namespace SessionCache;
    ini_set('memory_limit', '256M');  // this is the best issue of this system
    session_start();
    
    class SessionCache {
    
        // todo: add the possible to expire the session
    
        private static $SessionName = 'SessionCache';
    
        public static function Check($name){
            if( empty($_SESSION[self::$SessionName]) ) return false;
            if( empty($_SESSION[self::$SessionName][$name]) ) return false;
            else return true;
        }
    
        public static function Set($name, $data){
            if( self::Check($name) ) return;
            $data = serialize($data);
            $_SESSION[self::$SessionName][$name] = $data;
        }
    
        public static function Get($name){
            if( self::Check($name) ){
                $data = unserialize($_SESSION[self::$SessionName][$name]);
                return $data;
            }
            else return null;
        }
    
        public static function Flush($name){
            if( self::Check($name) ) unset($_SESSION[self::$SessionName][$name]);
        }
    
        public static function Destroy(){
            session_destroy();
        }
    }
    

    现在,(当前)逻辑:

    // Images Class. Here I use some extra stuff. Feel free to read the comments 
    
    namespace AdminPanel;
    include_once 'SessionCache.php';
    include_once 'Database.php';
    include_once 'Utils.php';
    use \AdminPanel\Database;
    use \AdminPanel\Utils;
    use SessionCache\SessionCache;
    use stdClass;
    
    class Patrocinios {
    
        private $cache_name = 'Patrocinadores';
        private $img_width = 225;
        private $img_height = 70;
        private $img_quality = 100;
    
        public function __construct(){
            $this->Database = new \AdminPanel\Database();
            $this->Utils = new \AdminPanel\Utils();
        }
        private function CreateImageCache(){
            if( SessionCache::Check($this->cache_name) ) return null;
            $query = "select * from patrocinadores";
            $this->Database->Open();
            $data = $this->Database->GetData($query);
            $this->Database->Close();
            $patros = new stdClass();
            if( count($data) > 0 ){
                for( $i=0; $i<count($data); $i++ ){
                    $id = $data[$i]['id'];
                    $nombre = $data[$i]['nombre'];
                    $uri = $data[$i]['web'];
                    $mimetype = $data[$i]['tipo'];
                    $imagedata = $data[$i]['imagen'];
                    $patros->patro[$id] = new stdClass();
                    $patros->patro[$id]->id = $id;
                    $patros->patro[$id]->nombre = $nombre;
                    $patros->patro[$id]->uri = $uri;
                    $patros->patro[$id]->mimetype = $mimetype;
                    $patros->patro[$id]->data = $imagedata;     // the image BLOB
                }
            }
            SessionCache::Set($this->cache_name, $patros);
        }
    
        public function GetPatrocinadores(){    // this method is the only one called from the main view
            $this->CreateImageCache();
            $patros = SessionCache::Get($this->cache_name);
            return $patros;
        }
    
        public function Pintar($id, $width, $height, $quality){     // this method is called from the PHP file used to print the images
            if( !SessionCache::Check($this->cache_name) ) $patros = $this->GetPatrocinadores();
            else $patros = SessionCache::Get($this->cache_name);
            $dataimg = $this->Utils->formatImage("string", $patros->patro[$id]->data, $width, $height);     // creates new image with desired measures and quality
            header('Content-Type: '.$patros->patro[$id]->mimetype);
            header('Content-Length: '.strlen($patros->patro[$id]->data));
            imagejpeg($dataimg[0], null, $quality);
        }
    }
    

    我只有一个带有会话对象名称的变量($cache_name)。首先,我检查是否存在(来自之前的通话)。如果没有,我使用数据库中的信息填充stdClass()对象并将其存储在会话中。

    <ul id="ulPatrocinadores">
        <?php
        include_once $_SERVER['DOCUMENT_ROOT'].'/php/classes/Patrocinios.php';
        $p = new \AdminPanel\Patrocinios();
        $patros = $p->GetPatrocinadores();
        $str = '';
        foreach( $patros as $value ){
            foreach( $value as $patro ){
                $id = $patro->id;
                $nombre = str_replace(" ", "_", $patro->nombre);
                $web = $patro->uri;
                $str .= '<li>';
                $str .= '<a href="'.$web.'" title="'.$nombre.'" target="_blank"><img src="/'.$nombre.'_pat-i='.$id.'&w=225&h=70&q=85" alt="'.$nombre.'" /></a>';
            }
        }
        echo $str;
        ?>
    </ul>
    

    以上是打印图像的主视图。请注意,锚点和图像源转到调用方法Pintar()的PHP文件。我使用RewriteRule重定向它。

    <?php
    include_once '../scripts/PrintPartner.php';
    $a = new \AdminPanel\Patrocinios();
    
    $i = $_GET['i'];
    $w = $_GET['w'];
    $h = $_GET['h'];
    $q = $_GET['q'];
    
    $a->Pintar($i, $w, $h, $q);
    
    unset($a);
    

    所以,我终于实现了它。我不知道这是否是一个好的系统,因为$_SESSION与php memory_limit有关系,但经过两天的思考,我无法获得更好的东西。

    我取得的成就:

    1. 之前:对数据库的每个图像进行一次查询。现在:对一个会话(或我需要的时间)的所有图像进行一次查询。

    2. 保持友好的uris,根据需要动态创建图像文件,仍然使用PHP文件作为图像src

    3. 使用共享主机减少对数据库的调用的良好系统。

    4. 希望这种体验有助于某人。