Publicidade

Tiago Souza Ribeiro

[Resolvido] Armazenar imagens em banco de dados ou diretórios?

Patrocínio:

Olá, minha dúvida agora é coisa simples: meu site que estou fazendo terá um sistema de uploads de imagens e eu gostaria de saber, qual o melhor método para armazenar estas imagens?

- Armazenar normalmente em diretórios

- Codificar em URI Base64 e salvar em banco de dados MySQL

Qual método me recomendam? Qual a vantagem do mesmo sobre o outro?

 

Até mais. :D

Editado por Tiago Souza Ribeiro
0

Compartilhar este post


Link para o post
Compartilhar em outros sites

envie o nome da imagem para o banco de dados.

exemplo de um script que eu tenho aqui:

 


function trocar_acentos ($imagem)
{
	$imagem = str_replace(' ','_',$imagem);
	$imagem = str_replace('á','a',$imagem);
	$imagem = str_replace('ã','a',$imagem);
	$imagem = str_replace('â','a',$imagem);
	$imagem = str_replace('ä','a',$imagem);
	$imagem = str_replace('à','a',$imagem);
	$imagem = str_replace('Á','a',$imagem);
	$imagem = str_replace('À','a',$imagem);
	$imagem = str_replace('Ã','a',$imagem);
	$imagem = str_replace('Â','a',$imagem);
	$imagem = str_replace('Ä','a',$imagem);
	$imagem = str_replace('ç','c',$imagem);
	$imagem = str_replace('Ç','c',$imagem);
	$imagem = str_replace('é','e',$imagem);
	$imagem = str_replace('É','e',$imagem);
	$imagem = str_replace('í','i',$imagem);
	$imagem = str_replace('Í','i',$imagem);
	$imagem = str_replace('ó','o',$imagem);
	$imagem = str_replace('Ó','o',$imagem);
	$imagem = str_replace('ú','u',$imagem);
	$imagem = str_replace('Ú','u',$imagem);
	$imagem = strtolower($imagem);

	return $imagem;
}



$imagem = trocar_acentos ($_FILES['imagem']['name']);
$imagem_extensao = substr($imagem,strpos($imagem,'.')+1,strlen($imagem)-strpos($imagem,'.'));
$imagem_tamanho = $_FILES['imagem']['size'];
$imagem_descricao = $_POST['imagem_descricao'];
$imagem_data = date_default_timezone_set('America/Sao_Paulo');


	$uploaddir = "/uploads/";
	$data = time();

       if ($imagem != ""){
	if (file_exists($uploaddir.$imagem))
	{
		$imagem = $data."_".$imagem;
	}
	}



move_uploaded_file($_FILES['imagem']['tmp_name'], $uploaddir . $imagem);

$query = "insert into uploads (imagem) values ('$imagem')";
$rs = mysql_query($query);

 

precisa ser aprimorado, mas ja da pra começar :)

qualquer modificação poste ai :D

Editado por lightfox
0

Compartilhar este post


Link para o post
Compartilhar em outros sites

Armazenar os arquivos em um diretório e apenas o nome do arquivo no banco de dados é a melhor opção.

 

A outra opção tem as seguintes desvantagens:

  1. Codificar em Base64 deixa a string maior.
  2. Requer cuidados com a codificação dos dados (se não for usar Base64).
  3. Se der algum problema na tabela (corrupção de dados, por exemplo), além de perder todos os dados, você perderá também os arquivos.
  4. Consome mais recursos do servidor (bem... eu nunca testei e comparei, mas acredito que seja verdade).

 

Como diriam os Titãs: o fácil é o certo. A solução mais simples é sempre a melhor.

0

Compartilhar este post


Link para o post
Compartilhar em outros sites

A seguir, uma lista de vantagens e desvantagens que elaborei de cada método.

 

Vantagens do método Base64:

- Muito mais fácil de armazenar

- Carregamento mais rápido por fugir do limite de conexões por host

- Posso impedir por PHP o uso de imagens específicas em outros sites

 

Vantagens do método tradicional:

- Não faz query's ao banco de dados

- É indexada pelo Google

- Menos chances de perda total

- Menor uso do disco

 

Desvantagens do método Base64:

- Faz mais query's ao banco de dados

- O tamanho da tabela será enorme se houver muitas imagens

- Não sei se será indexada pelo Google

 

Desvantagens do método tradicional:

- Pode-se ser usada em outros sites, sem permissão...

- Se for bloquear o uso em outros sites, por htaccess, vai bloquear todas por extensão, ou terá que modificar sempre o arquivo para incluir novas imagens separadamente

 

Então, analisando isso ae, qual é realmente vantajoso? Fiquei confuso, e podem haver outras vantagens/desvantagens que deixei passar.

0

Compartilhar este post


Link para o post
Compartilhar em outros sites

É muito fácil impedir o hotlinking de suas imagens através do htaccess.

 

Exemplo:

RewriteEngine on
RewriteCond %{HTTP_REFERER}     !^$
RewriteCond %{REQUEST_FILENAME} -f
RewriteCond %{REQUEST_FILENAME} \.(gif|jpe?g?|png)$           [NC]
RewriteCond %{HTTP_REFERER}     !^https?://([^.]+\.)?seu-dominio\.com\.br [NC]
RewriteRule \.(gif|jpe?g?|png)$

1

Compartilhar este post


Link para o post
Compartilhar em outros sites

Pois é, mas desse jeito ae, vou estar impedindo de todas imagens ao mesmo tempo. E se eu quiser bloquear apenas algumas? Não posso ficar editando htaccess direto, tem que ser tudo automático.

Ah, aproveitando que tamo falando de hotlink (que nem é bem o foco do tópico): se eu bloquear, vai impedir indexação pelo Google por exemplo?

Editado por Tiago Souza Ribeiro
0

Compartilhar este post


Link para o post
Compartilhar em outros sites

Pois é, mas desse jeito ae, vou estar impedindo de todas imagens ao mesmo tempo. E se eu quiser bloquear apenas algumas? Não posso ficar editando htaccess direto, tem que ser tudo automático.

 

É só redirecionar para um arquivo PHP então...

# As primeiras três linhas do meu exemplo anterior aqui...
RewriteCond %{REQUEST_FILENAME} /([A-Z0-9\-_])\.(gif|jpe?g|png)$ [NC]
RewriteCond %{HTTP_REFERER}     !^https?://(?:[^.]+\.)?seu-dominio\.com\.br [NC]
RewriteRule . imagem.php?filename=%1&extension=%2

 

 

Ah, aproveitando que tamo falando de hotlink (que nem é bem o foco do tópico): se eu bloquear, vai impedir indexação pelo Google por exemplo?

 

Bom... você que falou que só era possível ter controle sobre o acesso a image e impedir que outros usem sem permissão só seria possível se a imagem estivesse no banco de dados. Eu discordo. Existem muitas maneiras de prevenir hotlinking de imagens armazenadas no sistema de arquivos. Obviamente que sempre é possível burlar essas proteções, mas essa burla também vale para o armazenamento da imagem no banco de dados.

 

Creio que irá acontecer o seguinte: o Google irá criar o thumbnail da imagem e indexar normalmente, mas quando a imagem aparecer nos resultados de uma busca e o usuário clicar nela ele receberá um erro 404 ou um erro forbidden ou o que mais você programar para quando alguém tentar fazer hotlinking com a sua imagem. Já me aconteceu isso algumas vezes... é frustrante e faz o usuário perder tempo, portanto, se você for fazer isso, é melhor já bloquear logo o Googlebot-Images no seu robots.txt.

1

Compartilhar este post


Link para o post
Compartilhar em outros sites

Não acho que vai dar erro 404, já que o link vai apontar para a imagem original, no meu site mesmo, ela não vai estar em outro site, então não será hotlinking...

 

Sobre a hospedagem ae: eu acho que vou armazenar no banco de dados somente imagens que poderão ser alteradas a qualquer momento, ex: o avatar de um usuário, ele pode mudar a qualquer momento, e acho que seria mais fácil só alterar o Base64 da imagem no banco de dados, do que deletar a imagem atual no disco, e colocar a nova no lugar.... Imagens fixas, do layout, postagens, etc... vou armazenar no método tradicional mesmo, já que serão fixas, não serão alteradas, não por outras pessoas.

 

Vlw ae :D

0

Compartilhar este post


Link para o post
Compartilhar em outros sites

 

envie o nome da imagem para o banco de dados.

exemplo de um script que eu tenho aqui:

 


function trocar_acentos ($imagem)
{
	$imagem = str_replace(' ','_',$imagem);
	$imagem = str_replace('á','a',$imagem);
	$imagem = str_replace('ã','a',$imagem);
	$imagem = str_replace('â','a',$imagem);
	$imagem = str_replace('ä','a',$imagem);
	$imagem = str_replace('à','a',$imagem);
	$imagem = str_replace('Á','a',$imagem);
	$imagem = str_replace('À','a',$imagem);
	$imagem = str_replace('Ã','a',$imagem);
	$imagem = str_replace('Â','a',$imagem);
	$imagem = str_replace('Ä','a',$imagem);
	$imagem = str_replace('ç','c',$imagem);
	$imagem = str_replace('Ç','c',$imagem);
	$imagem = str_replace('é','e',$imagem);
	$imagem = str_replace('É','e',$imagem);
	$imagem = str_replace('í','i',$imagem);
	$imagem = str_replace('Í','i',$imagem);
	$imagem = str_replace('ó','o',$imagem);
	$imagem = str_replace('Ó','o',$imagem);
	$imagem = str_replace('ú','u',$imagem);
	$imagem = str_replace('Ú','u',$imagem);
	$imagem = strtolower($imagem);

	return $imagem;
}



$imagem = trocar_acentos ($_FILES['imagem']['name']);
$imagem_extensao = substr($imagem,strpos($imagem,'.')+1,strlen($imagem)-strpos($imagem,'.'));
$imagem_tamanho = $_FILES['imagem']['size'];
$imagem_descricao = $_POST['imagem_descricao'];
$imagem_data = date_default_timezone_set('America/Sao_Paulo');


	$uploaddir = "/uploads/";
	$data = time();

       if ($imagem != ""){
	if (file_exists($uploaddir.$imagem))
	{
		$imagem = $data."_".$imagem;
	}
	}



move_uploaded_file($_FILES['imagem']['tmp_name'], $uploaddir . $imagem);

$query = "insert into uploads (imagem) values ('$imagem')";
$rs = mysql_query($query);

 

precisa ser aprimorado, mas ja da pra começar :)

qualquer modificação poste ai :D

 

 

 

O cara nem leu a pergunta...

 

Em MySQL, desencana. Local físico, no banco você guarda só o caminho. Se for guardar o conteúdo das imagens em banco, sugiro Postgres ou alugm NoSQL como MongoDB.

0

Compartilhar este post


Link para o post
Compartilhar em outros sites

No MySQL, desencana mesmo, alias, eu sou totalmente contra gravar imagens em um banco de dados, por N motivos, eu prefiro deixar no FS.

Eu só guardaria imagens no banco, se por algum motivo em especial eu não tiver permissão de escrita em um diretório de armazenamento (talvez nunca aconteça, mas ...), no banco é totalmente inviável porque:

 

Se você possui select com relacionamentos, e alteração dinâmica na tabela, ou um acesso concorrente significante, você vai ter um trafego muito grande, e suas consultas podem demorar.

 

Pensando em 10.000 registros, você já começaria com alguns problemas, e claro sem falar no tamanho do buffer de saída na página, que ia ficar ENORME dependendo da quantidade de registros.

 

Outra que o propósito do banco não é bem esse, os campos BLOB, LONGBLOB, podem ser usados para esse caso, mas iria cair na mesma questão das queries, vai ser lento, e eu sinceramente acho, que isso é um erro de modelagem.

Editado por Andrey Knupp
0

Compartilhar este post


Link para o post
Compartilhar em outros sites

Obrigado a todos pelas respostas.

Bom, eu apenas pensei em salvar o conteúdo da imagem no banco de dados por ser muito mais fácil... eu apenas codificaria ele em base64 e pronto, salvaria no db, porém notei que assim usa 33% a mais de espaço em disco, segundo a página da função base64_encode no php.net.

Olhando o script do lightfox dei uma pesquisada sobre a variável $_FILES e a função move_uploaded_file e deu pra entender como fazer o upload normal, eu nunca precisei usar isso, então achei que era mais complicado, kkk.

Então, vou usar o método normal mesmo, até porque vocês crucificaram ae o outro, hehe.

Só mais uma coisa: o Evandro falou ae que seria melhor no Postgree ou algum NoSQL... porque eles sim, e o MySQL não? MySQL só serve para pequenas strings? Se for, vou ter que migrar para Postgree... estava pretendendo salvar o código fonte das postagens no banco de dados, e isso poderia ser bem grande...

0

Compartilhar este post


Link para o post
Compartilhar em outros sites

Obrigado a todos pelas respostas.

Bom, eu apenas pensei em salvar o conteúdo da imagem no banco de dados por ser muito mais fácil... eu apenas codificaria ele em base64 e pronto, salvaria no db, porém notei que assim usa 33% a mais de espaço em disco, segundo a página da função base64_encode no php.net.

Olhando o script do lightfox dei uma pesquisada sobre a variável $_FILES e a função move_uploaded_file e deu pra entender como fazer o upload normal, eu nunca precisei usar isso, então achei que era mais complicado, kkk.

Então, vou usar o método normal mesmo, até porque vocês crucificaram ae o outro, hehe.

Só mais uma coisa: o Evandro falou ae que seria melhor no Postgree ou algum NoSQL... porque eles sim, e o MySQL não? MySQL só serve para pequenas strings? Se for, vou ter que migrar para Postgree... estava pretendendo salvar o código fonte das postagens no banco de dados, e isso poderia ser bem grande...

 

A questão não é bem o tamanho e sim o tipo. MySQL é ótimo e muito performático armazenando longas cadeias de caracteres. Mas estamos falando de texto plano. Postgres cairia no mesmo problema, a ressalva é que a gestão de conteúdo binário nos campos *BLOB é otimizada neste banco.

 

NoSQL já é um outro ecossistema. Não sei nem porque cismam em querer comparar com SGDB's relacionais.

0

Compartilhar este post


Link para o post
Compartilhar em outros sites

@Evandro Oliveira, independente disso kra, eu acho errado.

Me diz um motivo pra gravar imagens no banco de dados.

Editado por Andrey Knupp
0

Compartilhar este post


Link para o post
Compartilhar em outros sites

@Evandro Oliveira, independente disso kra, eu acho errado.

Me diz um motivo pra gravar imagens no banco de dados.

 

Acesso, minimização de leitura, menos requisições de memória, apenas um ponteiro, menos recursos de processamento.

 

Imagine que o disco é um grande - enorme - corredor contendo arquivos. Seu diretório /images fica no começo desse corredor. Em um gaveta, em um dos primeiros arquivos.

 

Seu banco de dados, armazena o conteúdo - índices, tabelas, valores - lá pelo meio/final do corredor. Quem espera esses dados, te espera depois do final do corredor.

 

Você vai até o meio, para procurar em algum registro onde se encontra a imagem física, descobre, volta até o começo, pega a imagem e leva até o fim do corredor.

 

Veja: busca -> leitura -> envio -> busca -> envio

 

Armazenando tudo em um único local, podemos trabalhar um único sistema de caching, otimizar um único "sistema de arquivos" e fazer apenas um acesso ao disco

 

busca -> envio

0

Compartilhar este post


Link para o post
Compartilhar em outros sites

Certo, agora um bench que eu fiz:

<?php
      $m = new Mongo();
      $db = $m->bench ;
      /*$content = file_get_contents('jQuery.jpg');
      $content = base64_encode($content);

      for($i = 0 ; $i < 1000 ; ++$i){
           $db->images->insert(array(
               "i" => $i,
               "contentEncoded" => $content
           ));
      }*/

      $s = microtime(true);
      foreach($db->images->find() as $image){
         var_dump($image);
      }
      $e = (microtime(true) - $s);
      var_dump($e);

 

Inseri, depois comentei o código, e fiz a leitura ...

float(70.329179048538)

 

Esse resultado para 1000 copias de uma imagem de 128KB (nem todas terão 128KB, talvez você faça resize, talvez guarde a original e o thumb).

Daí depois tenta apertar CTRL+U.

 

Não vale apena ... sinceramente, você gasta alguns segundos levando em consideração o que o Evandro falou, é mais em conta do que gravar isso no banco (eu acho).

Se quiser fazer o bench sem usar o DB, simples, pega uma imagem de 128KB codifica o base 64, depois faz o looping exibindo o conteúdo..

<?php
      $content = file_get_contents('jQuery.jpg');
      $content = base64_encode($content);

      $s = microtime(true);
      for($i = 0; $i < 1000; ++$i){
           echo $content ;
      }
      $e = (microtime(true) - $s);
      var_dump($e);

 

Informe o resultado e o que você acha.

E ainda leva em consideração o tamanho do trafego de dados entre o servidor php e o banco de dados.

E lembre da concorrência, suponha uns 20 - 30 usuários, claro que você não vai trazer 1000 imagens, pode limitar sua busca a 10 (talvez uma lista), ou coisa assim.

 

Para testar a concorrência, pode usar o apache benchmark.

Não dá pra fazer todos esses testes agora, mas....

 

Lembrando também, que isso também vai aumentar o tamanho do arquivo (caso o armazenamento das imagens for no banco), e como você citou um "caching", se você guarda por 5 minutos, e exibe pra 30 usuários, você vai ler um arquivo de no mínimo 3-4 MB a cada requisição.

 

E provavelmente, qualquer consulta a nodes do documento com jQuery, ou JS Puro, irá demorar (não tenho crtz, não fiz o teste)

 

@Evandro, uma pergunta pessoal minha agora, suponha que você tenha que desenvolver um eCommerce, e na página de detalhes do produto, você mostra aquele slide com imagens do produto, normalmente essas imagens são referenciadas, e você tem (200-300) produtos, e 5 imagens em cada, você guardaria isso tudo em um banco de dados (base64 encoded) e mostraria como data-uri ? ou guardaria a referencia delas no banco ? (MySQL/MongoDB ou Cassandra)

Editado por Andrey Knupp
Adicionar informação
0

Compartilhar este post


Link para o post
Compartilhar em outros sites

Certo, agora um bench que eu fiz:

 

<?php
      $m = new Mongo();
      $db = $m->bench ;
      /*$content = file_get_contents('jQuery.jpg');
      $content = base64_encode($content);

      for($i = 0 ; $i < 1000 ; ++$i){
           $db->images->insert(array(
               "i" => $i,
               "contentEncoded" => $content
           ));
      }*/

      $s = microtime(true);
      foreach($db->images->find() as $image){
         var_dump($image);
      }
      $e = (microtime(true) - $s);
      var_dump($e);

 

Inseri, depois comentei o código, e fiz a leitura ...

float(70.329179048538)

 

 

Esse resultado para 1000 copias de uma imagem de 128KB (nem todas terão 128KB, talvez você faça resize, talvez guarde a original e o thumb).

Daí depois tenta apertar CTRL+U.

 

Não vale apena ... sinceramente, você gasta alguns segundos levando em consideração o que o Evandro falou, é mais em conta do que gravar isso no banco (eu acho).

Se quiser fazer o bench sem usar o DB, simples, pega uma imagem de 128KB codifica o base 64, depois faz o looping exibindo o conteúdo..

 

<?php
      $content = file_get_contents('jQuery.jpg');
      $content = base64_encode($content);

      $s = microtime(true);
      for($i = 0; $i < 1000; ++$i){
           echo $content ;
      }
      $e = (microtime(true) - $s);
      var_dump($e);

 

 

Informe o resultado e o que você acha.

E ainda leva em consideração o tamanho do trafego de dados entre o servidor php e o banco de dados.

E lembre da concorrência, suponha uns 20 - 30 usuários, claro que você não vai trazer 1000 imagens, pode limitar sua busca a 10 (talvez uma lista), ou coisa assim.

 

Para testar a concorrência, pode usar o apache benchmark.

Não dá pra fazer todos esses testes agora, mas....

 

Lembrando também, que isso também vai aumentar o tamanho do arquivo (caso o armazenamento das imagens for no banco), e como você citou um "caching", se você guarda por 5 minutos, e exibe pra 30 usuários, você vai ler um arquivo de no mínimo 3-4 MB a cada requisição.

 

E provavelmente, qualquer consulta a nodes do documento com jQuery, ou JS Puro, irá demorar (não tenho crtz, não fiz o teste)

 

Benchmark, por benchmark, fiz o meu, com Postgres.

 

SQL

 

CREATE TABLE with_blob (
   id Serial primary key,
   img OID
);

CREATE TABLE without_blob (
   id Serial primary key,
   img TEXT
);

CREATE TABLE with_path (
   id Serial primary key,
   img varchar(30)
);

 

 

Psql.php

 

<?php

class Psql extends PDO
{
   CONST INSERT_WITH_BLOB
       = "INSERT INTO with_blob(img) VALUES(lo_import(?));";

   CONST SELECT_WITH_BLOB
       = "SELECT img, lo_export(img, CONCAT('/tmp/', img))
        FROM with_blob LIMIT 1";

   CONST DELETE_WITH_BLOB      = "DELETE FROM with_blob;";

   CONST INSERT_WITHOUT_BLOB   = "INSERT INTO without_blob(img) VALUES(?)";

   CONST SELECT_WITHOUT_BLOB   = "SELECT img FROM without_blob LIMIT 1";

   CONST DELETE_WITHOUT_BLOB   = "DELETE FROM without_blob;";

   CONST INSERT_WITH_PATH      = "INSERT INTO with_path(img) VALUES(?)";

   CONST SELECT_WITH_PATH      = "SELECT img FROM with_path LIMIT 1";

   CONST DELETE_WITH_PATH      = "DELETE FROM with_path";

   CONST DSN = "pgsql:host=localhost;dbname=evandro";

   CONST FILEPATH = "/tmp/imasters.png";

   public function __construct()
   {
       parent::__construct(psql::DSN, 'evandro', 'root');
       $this->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
   }

   public function insertWithBlob()
   {
       $this->prepare(psql::INSERT_WITH_BLOB)->execute(array(psql::FILEPATH));
   }

   public function selectWithBlob()
   {
       return file_get_contents(
           '/tmp/' . $this->query(psql::SELECT_WITH_BLOB)->fetchColumn()
       );
   }

   public function deleteWithBlob()
   {
       $this->exec(psql::DELETE_WITH_BLOB);
   }

   public function insertWithoutBlob()
   {
       $img = base64_encode(file_get_contents(psql::FILEPATH));
       $this->prepare(psql::INSERT_WITHOUT_BLOB)->execute(array($img));
   }

   public function selectWithoutBlob()
   {
       return base64_decode(
           $this->query(psql::SELECT_WITHOUT_BLOB)->fetchColumn()
       );
   }

   public function deleteWithoutBlob()
   {
       $this->exec(psql::DELETE_WITHOUT_BLOB);
   }

   public function insertWithPath()
   {
       $path   = '/tmp/' . uniqid();
       $this->prepare(Psql::INSERT_WITH_PATH)->execute(array($path));
       copy(Psql::FILEPATH, $path);
   }

   public function selectWithPath()
   {
       return file_get_contents(
           $this->query(Psql::SELECT_WITH_PATH)->fetchColumn());
   }

   public function deleteWithPath()
   {
       $this->exec(Psql::DELETE_WITH_PATH);
   }
}

 

 

Arquivo de testes, para garantir que todas as respostas sejam idênticas

PsqlTest.php

 

<?php

require_once('Psql.php');
require_once('PHPUnit/Autoload.php');

class PsqlTest extends PHPUnit_Framework_TestCase
{
   /** @var $instance Psql */
   private $instance;
   private $filedata;

   protected function setUp()
   {
       $this->filedata = file_get_contents('/tmp/imasters.png');
       $this->instance = new Psql();
   }

   public function testSelectWithBlob()
   {
       $this->instance->insertWithBlob();
       $this->assertEquals($this->filedata,
           $this->instance->selectWithBlob());
       $this->instance->deleteWithBlob();
   }

   public function testSelectWithoutBlob()
   {
       $this->instance->insertWithoutBlob();
       $this->assertEquals($this->filedata,
           $this->instance->selectWithoutBlob());
       $this->instance->deleteWithoutBlob();
   }

   public function testSelectWithPath()
   {
       $this->instance->insertWithPath();
       $this->assertEquals($this->filedata,
           $this->instance->selectWithPath());
       $this->instance->deleteWithPath();
   }
}

 

Para executar, tendo o PHPUnit instalado, basta por os dois arquivos no mesmo diretório e rodar um

phpunit PsqlTest.php

 

O arquivo de profile do XDebug: http://minus.com/mde2sEAxv

 

Você pode abrir este arquivo pelo NetBeans, acho que pelo Eclipse, pelo PHPStorm, se usar KDE pelo kcachegrind, se usar Windows pelo wincachegrind ou usando um browser com o webgrind

 

os resultados aqui na minha máquina foram o seguinte:

 

 

insert.jpg

 

select.jpg

 

delete.jpg

 

 

 

@Evandro, uma pergunta pessoal minha agora, suponha que você tenha que desenvolver um eCommerce, e na página de detalhes do produto, você mostra aquele slide com imagens do produto, normalmente essas imagens são referenciadas, e você tem (200-300) produtos, e 5 imagens em cada, você guardaria isso tudo em um banco de dados (base64 encoded) e mostraria como data-uri ? ou guardaria a referencia delas no banco ? (MySQL/MongoDB ou Cassandra)

 

Escrever as imagens como data-uri está fora de cogitação. Nunca sacrificaria as várias formas de caching e compressão nesse cenário.

data-uri pra mim seria uma saída pra um fundo degradê, por exemplo. Não imagens complexas. Às vezes o base64 gerado consegue ser maior que a imagem! Sem contar que eu teria que embutir isso no markup, atrasando a exibição do conteúdo textual.

 

Se estamos falando de eCommerce, falamos de performance máxima, correto?? Eu optaria por uma lógica sequencial - o produto de ID 6, carregaria as imagens 6-1.jpg, 6-2.jpg, 6-3.jpg, 6-4.jpg e 6-5.jpg. E eu não procurei nada no banco. Eu sei que o ID é 6 pela URL da página. Essas imagens ficariam muito provavelmente numa rede CDN, num outro domínio, livre de cookies, servindo conteúdo compactado por gzip. Entenda, eu não toco em qualquer banco de dados para esse cenário. Porque é desnecessário.

Editado por Evandro Oliveira
0

Compartilhar este post


Link para o post
Compartilhar em outros sites

Entretanto, no final das contas resume-se a não guardar imagens no banco de dados.

Estou errado ? você de qualquer forma tem o objetivo de exibi-la, se for por data-uri, você vai ter aquela string [0...12000] na sua saída, deixando o site todo lento.

 

Os tipos de campo eu nem discuto não, sei que com BLOB é melhor, é o tipo de campo correto para esse tipo de coisa.

Mas a questão era: É bom ou NÃO guardar imagens no banco de dados.

 

Sobre o acesso das informações do produto, veja bem, você sabe o ID do produto, mas não sabe quantas fotos são, ali eu e você afirmamos que são 5, mas e se não forem ? de qualquer forma você vai consultar o banco pra pegar as informações, o que eu faço nesse caso de várias imagens, é simplesmente uma coluna TEXT que vai ficar os paths delimitados por ';', na hora eu apenas quebro a string e exibo.

 

E Evandro, seu teste tá meio errado aí não ? eu não falei sobre tipos de campo, e sim sobre o conteúdo no banco que no final das contas resume-se a exibição dele (de qualquer forma referência ou não, vai cair na tag img).

 

No post #10, eu digo "os campos BLOB, LONGBLOB, podem ser usados para esse caso".

Logo em seguida, meu teste se baseia na exibição do conteúdo desses campos.

Editado por Andrey Knupp
0

Compartilhar este post


Link para o post
Compartilhar em outros sites

[...] o que eu faço nesse caso de várias imagens, é simplesmente uma coluna TEXT que vai ficar os paths delimitados por ';', na hora eu apenas quebro a string e exibo.

[...]

 

Desculpe me intrometer, mas não resisti...

 

Isso pode até ser mais rápido em termos de benchmark (não testei...), mas eu acho muito deselegante e pouco profissional. Eu crio logo um relacionamento (1hasMany) entre a tabela 'produtos' e a tabela 'imagens'. Só de pensar que na hora de adicionar uma imagem onde já haviam umas 20 eu terei que fazer a consulta e depois concatenar a string e por fim enviar de volta ao banco de dados para que o campo seja sobrescrito eu já fico agoniado.

0

Compartilhar este post


Link para o post
Compartilhar em outros sites

Isso pode até ser mais rápido em termos de benchmark (não testei...), mas eu acho muito deselegante e pouco profissional. Eu crio logo um relacionamento (1hasMany) entre a tabela 'produtos' e a tabela 'imagens'. Só de pensar que na hora de adicionar uma imagem onde já haviam umas 20 eu terei que fazer a consulta e depois concatenar a string e por fim enviar de volta ao banco de dados para que o campo seja sobrescrito eu já fico agoniado.

Certo, da mesma forma que você vai precisar da consulta pra montar o formulário de update, você vai ter essa coluna já com ele.

E da mesma forma que você usa o explode pra poder separar os delimitadores e tem o array de retorno, você faz um push dessa imagem nesse array e um implode com o delimitador novamente.

 

Pronto, você tem sua nova lista de imagens normalmente.

Eu não tenho problemas com modelagem de banco, afinal nunca usei "de fato" essa solução das imagens que falei, apenas foi o que me veio a cabeça, disse o "faço" para o caso atual em que está sendo discutido, mas ... é questão de analisar a situação.

 

Com certeza a tabela imagens sendo separada fica muito fácil Atualizar a referencia, remover ... e adicionar, enfim.

Já tive casos em que a situação tem que ser resolvida diretamente no banco, ou seja sem alterar no PHP.

 

Se quiser, te mostro uma modelagem minha que toda a parte matemática do sistema fica acoplado no SQL, da mesma forma em que existem casos que você cria uma tabela e guarda as imagens e casos que você pensa em outra solução.

Editado por Andrey Knupp
0

Compartilhar este post


Link para o post
Compartilhar em outros sites

E Evandro, seu teste tá meio errado aí não ? eu não falei sobre tipos de campo, e sim sobre o conteúdo no banco que no final das contas resume-se a exibição dele (de qualquer forma referência ou não, vai cair na tag img).

 

Procurei não fugir do objetivo inicial do tópico. Guardar a imagem ou o caminho no BD. Se for pra usar a tag IMG não precisaria nem dessa papagaiada toda. Armazena o caminho físico mesmo por todas as desvantagens do base64 já citadas nesse tópico.

 

Para guardar a imagem, usando Postgres, temos duas formas: Long Object ou encoded.

Para guardar o caminho, eu só conheço uma forma.

 

Conhecidas todas as possibilidades, criei os testes e isolei as execuções para ter um benchmark mais preciso possível.

 

Analisando as imagens postadas, vi que havia algo muito errado. Então reiniciei o computador em modo terminal, sem X, sem acesso à Internet e executei os testes 5 vezes

$ php -d xdebug.profiler_enable=1 /usr/local/bin/phpunit --repeat 5 PsqlTest.php

 

O cachegrind gerado pode ser obtido aqui

 

Aqui as informações relevantes e a minha dedução sobre a performance:

new-benchmark.jpg

 

Usar o tipo Long Object é o menos performático. Mas se a opção existe, deve ter suas funções, talvez com imagens maiores possa ser mais rápida que codificar em base64.

 

Fato curioso é que a exclusão da imagem em base64 é sempre mais rápida. Talvez o Postgres apenas remova a referência ao campo TEXT ao invés de truncá-lo, como faz com varchar.

 

Para inserção e consulta, armazenar apenas o caminho físico e abrí-lo externamente se mostrou o menos latente. Por motivos óbvios.

 

Ao contrário da sequência que eu imaginava, armazenar um conteúdo binário no Postgres envolve um passo a mais ao invés de um passo a menos - lo_export e lo_import. Como eu disse, deve ter sua utilidade.

 

Como esperado, base64 não parece ser uma solução muito inteligente por criar a mesma camada de necessidade do tipo Long Object (lo_export -> base64_decode, lo_import -> base64_encode).

 

Excetuando-se pela exclusão, usar apenas o caminho físico do arquivo é mais rápido em todos os cenários. Usei a função copy para simular a funcionalidade de move_uploaded_file e ter um resultado mais próximo possível do real.

 

 

Sobre o acesso das informações do produto, veja bem, você sabe o ID do produto, mas não sabe quantas fotos são, ali eu e você afirmamos que são 5, mas e se não forem ? de qualquer forma você vai consultar o banco pra pegar as informações, o que eu faço nesse caso de várias imagens, é simplesmente uma coluna TEXT que vai ficar os paths delimitados por ';', na hora eu apenas quebro a string e exibo.

 

http://php.net/glob

0

Compartilhar este post


Link para o post
Compartilhar em outros sites

hehehe, sabia que você ia dizer "glob", mas aí você não contradisse o que falou acima sobre consultar o diretório atrás dos path's das imagens ... ? lá no seu exemplo do corredor, se eu entendi direito.

 

Entretanto, os testes apesar de não terem exatamente o foco do tópico, provam e respondem outras perguntas sobre performance no armazenamento de dados nesses tipos de campo, como eu não conheço muito mongodb, teria algum tipo de campo em específico para esse tipo de armazenamento ?

0

Compartilhar este post


Link para o post
Compartilhar em outros sites

hehehe, sabia que você ia dizer "glob", mas aí você não contradisse o que falou acima sobre consultar o diretório atrás dos path's das imagens ... ? lá no seu exemplo do corredor, se eu entendi direito.

 

loja.com/?produto=1

 

$fotos = glob($_GET['produto'] . '-*.jpg'); // array('1-capa.jpg','1-conteudo.jpg','1-contracapa.jpg');

 

Sem banco de dados ;)

 

A questão do corredor é que o tópico trata sobre passar o conteúdo da imagem via data-uri. Para isso precisaríamos ler o conteúdo. Para ler o conteúdo, ou ele já está no banco, ou vamos buscar o caminho no banco. Era esse o problema. Agora não tratamos mais do conteúdo, não precisamos chegar até a imagem. A ida ao corredor de arquivos é apenas uma. Devolvemos no final do corredor o caminho. Esse corredor será percorrido novamente, mas por uma nova requisição HTTP.

 

Entretanto, os testes apesar de não terem exatamente o foco do tópico, provam e respondem outras perguntas sobre performance no armazenamento de dados nesses tipos de campo, como eu não conheço muito mongodb, teria algum tipo de campo em específico para esse tipo de armazenamento ?

 

http://bsonspec.org/

Editado por Evandro Oliveira
0

Compartilhar este post


Link para o post
Compartilhar em outros sites

Pera ae, tá uma confusão danada isso aqui... está até interessante o debate ae, hehe, mas deixa só eu confirmar se entenderam exatamente o que eu pretendia fazer: era simples, apenas armazenar num campo de texto qualquer do banco de dados, o código base64, para usar como data-uri numa tag <img.... />.

Não entendi muito bem, mas parece que estão falando ae de criar o base64 da imagem pelo SQL... eu pretendia usar a função base64_encode() para criar o código, e esse seria armazenado normalmente no banco de dados, como qualquer texto comum...

 

Exemplo de tabela que iria usar [images]:

 

+---+----------+-------------------------------------------------+

| id | formato | base64 |

+---+----------+-------------------------------------------------+

| 1 | gif/jpg/png | código-base64-da-imagem-aqui |

+---+----------+-------------------------------------------------+

 

Aí criaria uma função assim:

 

function getimg($id) {
$select = mysqli_query($conexao, "SELECT formato,base64 FROM images WHERE id='".$id."'");
$get = mysqli_fetch_array($select, MYSQLI_ASSOC);
return array($get['formato'], $get['base64']);
}

 

Usaria assim:

 

<img src="data:image/<?php echo getimg(1)[0]; ?>;base64,<?php echo getimg(1)[1]; ?>" />

 

Apenas isto. E quando eu fosse salvar a imagem no db, codificaria ela com a função base64_encode() do PHP e armazenaria o resultado no campo "base64" da tabela.

 

Foi isso que entenderam?

 

Obs: já não pretendo mais usar esse método pelo tanto de desvantagens que me apontaram (33% a mais de uso do disco, query lentas...), mas a discussão ae tá boa, não conheço muita coisa de banco de dados, só o básico, então está sendo bem interessante :)

Editado por Tiago Souza Ribeiro
0

Compartilhar este post


Link para o post
Compartilhar em outros sites

Pera ae, tá uma confusão danada isso aqui... está até interessante o debate ae, hehe, mas deixa só eu confirmar se entenderam exatamente o que eu pretendia fazer: era simples, apenas armazenar num campo de texto qualquer do banco de dados, o código base64, para usar como data-uri numa tag <img.... />.

Não entendi muito bem, mas parece que estão falando ae de criar o base64 da imagem pelo SQL... eu pretendia usar a função base64_encode() para criar o código, e esse seria armazenado normalmente no banco de dados, como qualquer texto comum...

 

Exemplo de tabela que iria usar [images]:

 

+---+----------+-------------------------------------------------+

| id | formato | base64 |

+---+----------+-------------------------------------------------+

| 1 | gif/jpg/png | código-base64-da-imagem-aqui |

+---+----------+-------------------------------------------------+

 

Aí criaria uma função assim:

 

function getimg($id) {
$select = mysqli_query($conexao, "SELECT formato,base64 FROM images WHERE id='".$id."'");
$get = mysqli_fetch_array($select, MYSQLI_ASSOC);
return array($get['formato'], $get['base64']);
}

 

Usaria assim:

 

<img src="data:image/<?php echo getimg(1)[0]; ?>;base64,<?php echo getimg(1)[1]; ?>" />

 

Apenas isto. E quando eu fosse salvar a imagem no db, codificaria ela com a função base64_encode() do PHP e armazenaria o resultado no campo "base64" da tabela.

 

Foi isso que entenderam?

 

Obs: já não pretendo mais usar esse método pelo tanto de desvantagens que me apontaram (33% a mais de uso do disco, query lentas...), mas a discussão ae tá boa, não conheço muita coisa de banco de dados, só o básico, então está sendo bem interessante :)

 

Sim, entendemos perfeitamente. A minha opinião se resume a esse parágrafo:

Escrever as imagens como data-uri está fora de cogitação. Nunca sacrificaria as várias formas de caching e compressão nesse cenário.

data-uri pra mim seria uma saída pra um fundo degradê, por exemplo. Não imagens complexas. Às vezes o base64 gerado consegue ser maior que a imagem! Sem contar que eu teria que embutir isso no markup, atrasando a exibição do conteúdo textual.

 

Acredito que odebate se "acalorou" porque armazenar a imagem encoded no banco é similar a armazenar o conteúdo dela. Para isso temos outras formas.

 

O equivalente, seria você armazenar a imagem como um blob e, depois do select, imprimir o base64_encode dela.

 

Mas dá na mesma, todas as desvantagens já citadas.

Editado por Evandro Oliveira
0

Compartilhar este post


Link para o post
Compartilhar em outros sites

Ah, ok...

Mas segundo o que Andrey me falou por private, nem pra elementos do layout, como o background que você citou, vale a pena usar o data-uri :(

O css vai ficar muito grande, demorar de carregar, e fazer uma bagunça.

Sendo assim, até hoje não vi utilidade do uso de imagens por esse modo, hehe

 

Vlw a todo ae;

Dúvida resolvida *-*

Editado por Andrey Knupp
0

Compartilhar este post


Link para o post
Compartilhar em outros sites

Crie uma conta ou entre para comentar

Você precisar ser um membro para fazer um comentário

Criar uma conta

Crie uma nova conta em nossa comunidade. É fácil!


Crie uma nova conta

Entrar

Já tem uma conta? Faça o login.


Entrar Agora