Ключевые слова:mysql, image, binary, php, sql, (найти похожие документы)
Date: Thu, 04 Jul 2002 16:44:53 +0600
From: Nickolay Kondrashov <niq@relinfo.ru>
Newsgroups: fido7.su.dbms.sql
Subject: MySQL и хранение картинок в базе
> Прошу совета как быть по быстродействию обработки базы ...
> Суть: создаю галерею картинок .. связка php + mysql + apache ..
> Т.е. надо хранить картинки! Я вот думаю где их лучьше хранить: в базе или на
> диске как отдельное файло? Интересует вопрос по быстродействию ...
>
> Т.Е. что будет быстрее обрабатываться: база в которой картинки храняться, или
> база в которой хранятся ссылки на картинки (url тобишь ..)?
> P.S. учитываю что картинок будет больше 300mb в jpg`ах ...
У этих двух подходов есть свои плюсы и минусы. Попробую их перечислить.
Значит, хранение в БД.
+ Файлы проекта хранятся централизовано (что особенно хорошо при командной разработке).
+ Доступ к файлам в любом случае будет осуществляться через
скрипт-загрузчик, что дает возможность более гибко контролировать этот процесс.
- Доставка файла скриптом из БД будет с большей вероятностью проходить
медленнее чем апачем из файловой системы.
- В целом дамп БД будет слишком большим и, возможно, репликация будет
требовать широких каналов между удаленными серверами. Также такой дамп будет
трудно читаем по F3, т.к. бинарные данные будут смешаны с другими данными.
Решается настройкой репликации и продумыванием структуры БД, т.е. нужно все
бинарные данные засунуть в несколько таблиц (в твоем случае только не в
одну). Сделай так чтобы, например, таблица MyBinariesXXX создавалась, когда
в кол-во записей в таблице MyBinaries(XXX-1) превысило знчение YYY. Hапиши
классик, который инкапсулировал бы доступ к этим разбросанным данным как к
одной куче. Тогда MySQL будет шарить по этим таблицам почти также быстро как
и апач по файловой системе и 300М ему будет не помеха. В этом же случае дамп
БД будет легко читаем, т.к. бинарные данные расположены в специализированных
таблицах.
Хранение в файловой системе.
+ Высокая скорость доступа и меньшая нагрузка на сервер.
Hекоторые товарищи, говорили, что выигрыш в скорости значителен, если
количество файлов в папке не превышает порядка нескольких тысяч. Эту цифру,
конечно, можно узнать получше, если реализовать схему подбную той, что
описывалась выше, только не с таблицами а с папками.
- Проблемы с целостностью данных, т.е. синхронизацией БД и файловой системы.
Такие проблемы часто возникают если над проектом работает несколько
разработчиков.
Вывод:
Оба подхода имеют право на существование при условии грамотной реализации
доступа к данным и организации самих данных.
Обычно я предпочитаю первый подход, т.к. объем данных и проигрыш в
быстродействии незначительны. А указанные плюсы очень важны.
Вот пример реализации универсального интерфейса доступа к бинарным данным.
Используемые библиотеки: PHPlib для доступа к БД, PEAR для протоколирования.
СУБД - MySQL.
______________________________
схема
______________________________
create table binaries
(
id int(14) auto_increment not null,
bin_data mediumblob,
mime_type varchar(100),
primary key (id)
);
______________________________
lib/binlib/BinariesIO.php - класс инкапсулирующий доступ к данным
______________________________
<?php
class BinariesIO
{
var $mConfig;
var $mDb;
function BinariesIO(&$config, &$db)
{
$this->mConfig=&$config;
$this->mDb=&$db;
$GLOBALS["gAppLog"]->log("bin_folder:
".$this->mConfig["BINARIES_FOLDER"]);
$GLOBALS["gAppLog"]->log("binscript_uri:
".$this->mConfig["BINSCRIPT_URI"]);
if(!file_exists($this->mConfig["BINARIES_FOLDER"]))
{
$GLOBALS["gAppLog"]->log("bin_folder is not exist");
mkdir($this->mConfig["BINARIES_FOLDER"],0777);
}
}
function IsExists($id)
{
$GLOBALS["gAppLog"]->log("IsExists('".$id."')");
$sql="SELECT id, mime_type FROM binaries WHERE id='$id'";
$this->mDb->query($sql);
return $this->mDb->next_record();
}
function &GetBinaryObject($id)
{
$GLOBALS["gAppLog"]->log("GetBinaryObject('".$id."')");
$sql="SELECT id, mime_type FROM binaries WHERE id='$id'";
$this->mDb->query($sql);
$bo=&new BinaryObject($this);
if($this->mDb->next_record())
{
$bo->mId=$this->mDb->f("id");
$bo->mMimeType=$this->mDb->f("mime_type");
}
else
{
return null;
}
return $bo;
}
function &CreateBinaryObject()
{
$GLOBALS["gAppLog"]->log("CreateBinaryObject()");
$sql="INSERT INTO binaries (mime_type, bin_data) VALUES(NULL,NULL)";
$this->mDb->query($sql);
$sql="SELECT MAX(id) from binaries";
$this->mDb->query($sql);
$this->mDb->next_record();
$bo=new BinaryObject($this);
$bo->mId=$this->mDb->f(0);
return $bo;
}
function &RemoveBinaryObject($id)
{
$bo=$this->GetBinaryObject($id);
if($bo==null) return;
$path=$bo->GetPath();
if($path!=null)
if(file_exists($path))
unlink($path);
$sql="DELETE FROM binaries WHERE id='$id'";
$this->mDb->query($sql);
}
function &GetBinaryObjectList()
{
$GLOBALS["gAppLog"]->log("GetBinaryObjectsList()");
$sql="SELECT id, mime_type FROM binaries";
$this->mDb->query($sql);
for($i=0;$this->mDb->next_record();$i++)
{
$bo=&new BinaryObject($this);
$bo->mId=$this->mDb->f("id");
$bo->mMimeType=$this->mDb->f("mime_type");
$boList[$i]=$bo;
}
return $boList;
}
}
class BinaryObject
{
var $mId;
var $mMimeType;
var $mBio;
function BinaryObject(&$bio)
{
$this->mBio=&$bio;
}
function GetAccessUrl()
{
return $this->mBio->mConfig["BINSCRIPT_URI"]."?id=".$this->mId;
}
function GetPath()
{
return $this->mBio->mConfig["BINARIES_FOLDER"]."/".$this->mId.".bin";
}
function SetData(&$data)
{
$GLOBALS["gAppLog"]->log("SetData(data)");
if($this->IsFile())
{
$h=fopen($this->GetPath(), "w");
fwrite($h,&$data,strlen(&$data));
fclose($h);
}
else
{
$sql="UPDATE binaries SET bin_data='".addslashes($data)."'";
$this->mBio->mDb->query(&$sql);
}
}
function &GetData()
{
$GLOBALS["gAppLog"]->log("GetData()");
if($this->IsFile())
{
$GLOBALS["gAppLog"]->log("loading from '".$this->GetPath()."'
size=".filesize($this->GetPath()));
$h=fopen($this->GetPath(), "r");
$data=&fread($h,filesize($this->GetPath()));
fclose($h);
return ($data);
}
else
{
$GLOBALS["gAppLog"]->log("loading from binaries db");
$sql="SELECT bin_data FROM binaries WHERE bin_data IS NOT NULL AND
id='".$this->mId."'";
$this->mBio->mDb->query($sql);
if($this->mBio->mDb->next_record())
{
return ($this->mBio->mDb->f("bin_data"));
}
else
{
return null;
}
}
}
function SetDataFromMultipartRequest($fieldName)
{
$GLOBALS["gAppLog"]->log("SetDataFromMultipartRequest('".$fieldName."')");
if($fieldName=="none")
return;
$h=fopen($fieldName,"r");
$data=&fread($h,filesize($fieldName));
fclose($h);
if($this->IsFile())
{
$h=fopen($this->GetPath(), "w");
fwrite($h,&$data,strlen(&$data));
fclose($h);
}
else
{
$sql="UPDATE binaries SET bin_data='".addslashes($data)."' WHERE
id='".$this->mId."'";
$this->mBio->mDb->query(&$sql);
}
}
function GetMimeType()
{
$GLOBALS["gAppLog"]->log("GetMimeType()");
$sql="SELECT mime_type FROM binaries WHERE id='".$this->mId."'";
$this->mBio->mDb->query($sql);
if($this->mBio->mDb->next_record())
{
return stripslashes($this->mBio->mDb->f("mime_type"));
}
else
{
return null;
}
}
function SetMimeType($mimeType)
{
$GLOBALS["gAppLog"]->log("SetMimeType('$mimeType')");
$sql="UPDATE binaries SET mime_type='".addslashes($mimeType)."'";
$this->mBio->mDb->query(&$sql);
}
function ToFile()
{
$GLOBALS["gAppLog"]->log("ToFile()");
if($this->IsFile()) return;
$data=&$this->GetData();
$h=fopen($this->GetPath(), "w");
fwrite($h,&$data,strlen(&$data));
fclose($h);
$sql="UPDATE binaries SET bin_data=NULL WHERE id='".$this->mId."'";
$this->mBio->mDb->query(&$sql);
}
function ToDb()
{
$GLOBALS["gAppLog"]->log("ToDB()");
if(!$this->IsFile()) return;
$data=&$this->GetData();
$sql="UPDATE binaries SET bin_data='".addslashes($data)."' WHERE
id='".$this->mId."'";
$this->mBio->mDb->query(&$sql);
$path=$this->GetPath();
if($path!=null)
if(file_exists($path))
unlink($path);
}
function IsFile()
{
$GLOBALS["gAppLog"]->log("IsFile(".($this->mBio->mConfig["BINARIES_FOLDER"].
"/".$this->mId.".bin").")=".file_exists($this->mBio->mConfig["BINARIES_FOLDE
R"]."/".$this->mId.".bin"));
return
file_exists($this->mBio->mConfig["BINARIES_FOLDER"]."/".$this->mId.".bin");
}
function IsDataPresent()
{
$GLOBALS["gAppLog"]->log("IsDataPresent()");
if($this->IsFile())
{
return
filesize($this->mBio->mConfig["BINARIES_FOLDER"]."/".$this->mId.".bin")!=0;
}
else
{
$sql="SELECT id FROM binaries WHERE bin_data IS NOT NULL AND
id='".$this->mId."'";
$this->mBio->mDb->query($sql);
return $this->mBio->mDb->next_record();
}
}
function GetSize()
{
$GLOBALS["gAppLog"]->log("GetSize()");
if($this->IsDataPresent())
{
if($this->IsFile())
{
return
filesize($this->mBio->mConfig["BINARIES_FOLDER"]."/".$this->mId.".bin");
}
else
{
$sql="SELECT LENGTH(bin_data) FROM binaries WHERE id='".$this->mId."'";
$this->mBio->mDb->query($sql);
$this->mBio->mDb->next_record();
return $this->mBio->mDb->f(0);
}
}
return 0;
}
function ToString()
{
return "[BinaryObject: mId='$this->mId', mMimeType='$this->mMimeType']";
}
}
?>
______________________________
lib/binlib/bin.php - выводит бинарный объект в стандартный вывод
______________________________
<?php
require_once "../../init_pear.php";
require_once "binlib/BinariesIO.php";
include("phplib/prepend.php3");
$bio_db=&new DB_Example;
$bio=&new BinariesIO($BinariesIOConfig, $bio_db);
$bo=$bio->GetBinaryObject($id);
if($bo==null)
{
echo "no data present";
exit;
}
if($bo->GetMimeType()!="") Header("Content-type: ".$bo->GetMimeType());
echo ($bo->GetData());
?>
______________________________
lib/binlib/testing/up_action.php - загрузка файла в систему
______________________________
<?php
require_once "../../../init_pear.php";
require_once "binlib/BinariesIO.php";
include("phplib/prepend.php3");
if($myfile!="")
{
$GLOBALS["gAppLog"]->log("file: '$myfile'");
$bio_db=&new DB_Example;
$bio=&new BinariesIO($BinariesIOConfig, $bio_db);
$bo=$bio->GetBinaryObject($id);
if($bo==null)
{
$bo=$bio->CreateBinaryObject();
}
$bo->SetDataFromMultipartRequest($myfile);
$bo->SetMimeType($mime);
}
$d="Location: up_view.php?id=".$bo->mId;
header($d);
exit;
?>
______________________________
lib/binlib/testing/up_convert.php - смена типа хранилища БД-файл/файл-БД
______________________________
<?
require_once "../../../init_pear.php";
require_once "binlib/BinariesIO.php";
include("phplib/prepend.php3");
$GLOBALS["gAppLog"]->log("*** SCRIPT START ***");
$bio_db=&new DB_Example;
$bio=&new BinariesIO($BinariesIOConfig, $bio_db);
$bo=$bio->GetBinaryObject($id);
if($bo->IsFile())
$bo->ToDb();
else
$bo->ToFile();
$GLOBALS["gAppLog"]->log($bo->ToString());
$GLOBALS["gAppLog"]->log("*** SCRIPT END ***");
header("Location: up_view.php?id=$id");
exit;
?>
______________________________
lib/binlib/testing/up_delete.php - удаление бинарного объекта
______________________________
<?
require_once "../../../init_pear.php";
require_once "binlib/BinariesIO.php";
include("phplib/prepend.php3");
$GLOBALS["gAppLog"]->log("*** SCRIPT START ***");
$bio_db=&new DB_Example;
$bio=&new BinariesIO($BinariesIOConfig, $bio_db);
$bo=$bio->RemoveBinaryObject($id);
$GLOBALS["gAppLog"]->log("*** SCRIPT END ***");
header("Location: up_view.php");
exit;
?>
______________________________
lib/binlib/testing/up_view.php - управление списком бинарных объектов
______________________________
<?php
require_once "../../../init_pear.php";
require_once "binlib/BinariesIO.php";
include("phplib/prepend.php3");
$bio_db=&new DB_Example;
$bio=&new BinariesIO($BinariesIOConfig, $bio_db);
$bo=$bio->GetBinaryObject($id);
if($bo==null)
{
$data="no data present";
}
else
{
$mime=$bo->GetMimeType();
if($bo->IsDataPresent())
{
$data=&$bo->GetData();
}
else
{
$data="no data present";
}
}
?>
<?if($data!="no data present"){?><img
src="<?=$BinariesIOConfig["BINSCRIPT_URI"]?>?id=<?=$id?>"><?}?>
<form method="POST" action="up_action.php" enctype="multipart/form-data">
<input type="hidden" name="id" value="<?=$id?>">
<input type="file" name="myfile"><br>
MIME Type: <input type="text" name="mime" value="<?=$mime?>"><br>
<input type="submit" value="Upload">
</form>
<table border="1">
<tr>
<td>ID</td>
<td>Type</td>
<td>Size</td>
<td>Store</td>
<td>Convert</td>
<td>Delete</td>
</tr>
<?php
$boList=$bio->GetBinaryObjectList();
for($i=0,$s=sizeof($boList);$i<$s;$i++)
{
?>
<tr>
<td><a
href="up_view.php?id=<?=$boList[$i]->mId?>"><?=$boList[$i]->mId?></a></td>
<td><?=$boList[$i]->mMimeType==""?"UNKNOWN":$boList[$i]->mMimeType?></td>
<td><?=$boList[$i]->GetSize()?></td>
<td><?=$boList[$i]->IsFile()?"FILE":"DB"?></td>
<td><a
href="up_convert.php?id=<?=$boList[$i]->mId?>"><?=$boList[$i]->IsFile()?"TO
DB":"TO FILE"?></a></td>
<td><a href="up_delete.php?id=<?=$boList[$i]->mId?>">delete</a></td>
</tr>
<?
}
?>
</table>
<a href="up_view.php">add new</a>
______________________________
init_pear.php
______________________________
<?
$DOCUMENT_ROOT=eregi_replace("(.*)/$","\\1",$DOCUMENT_ROOT);
$include_path_old=@ini_get("include_path");
if($DOCUMENT_ROOT[0]=='/')
@ini_set("include_path",$include_path_old.":".$DOCUMENT_ROOT."/pear".":".$DO
CUMENT_ROOT.":".$DOCUMENT_ROOT."/lib");
else
@ini_set("include_path",$include_path_old.";".$DOCUMENT_ROOT."/pear".":".$DO
CUMENT_ROOT.":".$DOCUMENT_ROOT."/lib");
require_once "log/MyLog.php";
//
**GLOBAL*CONFIGURATION****************************************************
//
$gAppLog=&new MyLog();
$gAppLog->setLogging(true);
$gAppLog->setEcho(false);
$BinariesIOConfig["BINARIES_FOLDER"]=$GLOBALS["DOCUMENT_ROOT"]."/binaries";
$BinariesIOConfig["BINSCRIPT_URI"]="/lib/binlib/bin.php";
?>
______________________________
log/MyLog.php - класс протокола
______________________________
<?
require_once "Log.php";
require_once "Log/file.php";
class MyLog
{
var $mLog;
var $mLogging;
var $mEcho;
function MyLog()
{
$this->mLog=new Log_file($GLOBALS["DOCUMENT_ROOT"]."/app.log");
$this->mLogging=true;
$this->mEcho=false;
}
function setEcho($onTrueOffFalse)
{
$this->mEcho=$onTrueOffFalse;
}
function setLogging($onTrueOffFalse)
{
$this->mLogging=$onTrueOffFalse;
}
function log($message)
{
$this->debug($message);
}
function debug($message)
{
if($this->mLogging)
$this->internalLog($message,"debug");
}
function error($message)
{
if($this->mLogging)
$this->internalLog($message,LOG_ERR);
}
function info($message)
{
if($this->mLogging)
$this->internalLog($message,"info");
}
function warning($message)
{
if($this->mLogging)
$this->internalLog($message,LOG_INFO);
}
function fatal($message)
{
if($this->mLogging)
$this->internalLog($message,LOG_CRIT);
}
function internalLog(&$message, $priority)
{
if($this->mEcho) echo "<b>[$message]</b><br>\n";
$this->mLog->log("[".$GLOBALS["PHP_SELF"]."] ".$message);
}
}
?>
Около пяти лет профессионально занимаюсь созданием подобных вещей. Имхо, автор абсолютно не интересовался данным вопросом, хотя бы так как это реализовано в больших коммерческих проектах.
Подскажи как реализовано в больших комерческих проектах. Очень интересная тема и будет полезна многим. Очень хочется увидеть как делают професионалы, дабы было с чем сравнивать.
Imxo метод 1 в крупных проекта будет нагружать БД. Лучше 2-ой метод. Картинки - не настолько важная информация, чтобы контролировать жестко доступ. Думаю, в полне можно обойтись одной папкой с общим доступом на чтение.
столкнулся с проблемой, в таблице 50000 картинок общим объемом 300MB, select count(*) from image; выполняется 30 сек, это нормально или у меня что-то не так с MySQL?
у тебя что то не так с головой
И вообще хранение бинарников в базе (таких как картинки) для веб проектов может придумать только человек срочно нуждающийся в медицинской помощи.