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();
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 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 - выводит бинарный объект в стандартный вывод
______________________________