Atitude das empresas web

Confesso que não compreendo a atitude de algumas das empresas de desenvolvimento web em Portugal. Não conhecendo todas as empresas da área, falo de algumas que conheço, com que já lidei ou tive a oportunidade de analisar os seus trabalhos.

Noto nestas uma enorme preocupação em atingir os seus objectivos comerciais mas, pelo outro lado, vejo pouco ou nenhum investimento para melhorarem a forma como fazem o que fazem: projectos web.

A web não pára de evoluir e fá-lo com uma rapidez absurda. O que é norma hoje, é feito de uma nova forma amanhã. Por isso as empresas têm que se empenhar acompanhando a evolução das tecnologias existentes e estudando o que de novo aparece, para poderem responder da melhor forma aos desafios que lhes propõem todos os dias.

É fundamental que estas empresas tenham na sua equipa alguém responsável por fazer investigação para determinar que caminho a mesma deve seguir no que diz respeito a tecnologia. Da mesma forma que existe, por exemplo, um director de marketing, que gere a imagem da empresa, deverá também existir alguém dedicado a gerir a empresa em termos técnicos.

Acompanhar as novidades do meio, respeitar os standards web, estudar frameworks de desenvolvimento, adoptar ferramentas para melhorar o trabalho em equipa… são apenas algumas das acções que as empresas desta área devem ter na sua lista de tarefas.

Só assim poderão contribuir para uma web melhor, pondo cá fora mais e melhores projectos.

5 boas razões para respeitar os web standards

Os web standards já cá estão há algum tempo, mas ainda é normal ouvirmos palavras de discórdia por parte de alguns programadores/designers web em relação aos mesmos. O que é certo é que eles (os standards) vieram para ficar e quem não dedicar algum tempo a tentar cumpri-los corre o risco de perder o comboio da evolução da web.

Temos que nos lembrar que o target de um site são os utilizadores e por isso as páginas têm de estar acessíveis indepentemente do sistema operativo e/ou browser usado pelo utilizador. É aqui que os web standards nos ajudam e nos trazem vantagens:

  • Separação da estrutura do HTML (semântica) do design da página, melhora a acessibilidade;
  • Possibilidade de disponibilizar o mesmo site para várias plataformas (dispositivos móveis, impressões, etc.), através do uso de várias CSS;
  • Código mais limpo e estruturado e, consequentemente, mais amigo dos motores de busca.
  • Páginas mais leves, o que resulta em temos de carregamento mais baixos;
  • Garantia de compatibilidade com browsers futuros. Agora, mais que nunca, os browsers caminham todos no sentido de respeitarem os web standards;

A ideia a reter é, simplesmente, respeitar os web standards.

Comércio Electrónico: Considerações a ter em conta

No desenvolvimento de qualquer site de comércio electrónico existem diversos aspectos a ter em conta se pretendermos que este tenha um bom desempenho. Estes aspectos assumem um papel fundamental se o site em questão possuir um grande catálogo de produtos/serviços e/ou um grande volume de tráfego e/ou encomendas.

Base de dados

No topo da lista dos aspectos a ter em conta, a meu ver, está a estrutura base usada para suportar todo o sistema. Falo, obviamente da base de dados. Aqui, penso que é importante referir que, independentemente do SGBD escolhido (SQL Server, Oracle, MySQL, etc.), a estrutura de base de dados a criar terá de ser pensada e preparada para suportar milhares de registos, bem como suportar milhares de acessos simultâneos.

Assim, cada campo de tabela, deve ser do tipo mais adequado aos dados que nele serão guardados. O seu tamanho máximo deverá também ser tido em conta… é um desperdício usar um varchar(255) para um campo que irá guardar um número de telefone que exige apenas 9 caracteres (ok, mais alguns caso queiramos guardar também o indicativo do país, por exemplo).

Ainda relativamente à base de dados, devem ser criados índices em campos onde seja comum serem efectuadas pesquisas e ordenações. Índices nos campos certos é um bom ponto de partida para uma boa performance de um site com estas características.

Navegação

Não menos importante que o ponto anterior será, já na fase de desenvolvimento propriamente dito, as questões relacionadas com a navegabilidade do site.
É importante não esquecer que o Internet Explorer não é o único browser existente. O que é desenvolvido especificamente para este browser pode não funcionar correctamente em outros browsers, o que pode levar a que utilizadores desistam de navegar num site não preparado para o browser que usam. Menos utilizadores, menos potenciais compradores, por isso, o desenvolvimento deve ser pensado para ser compatível com a maioria dos browsers existentes, ou pelo menos com os mais usados: Firefox, Opera, Google Chrome, etc.

Outro ponto de bastante relevância é a usabilidade que a interface do site proporciona ao utilizador. Quanto mais facilitada for a navegação, mais facilmente os utilizadores conseguem encontrar o que pretendem sem necessitarem de sobrecarregar o servidor com pedidos desnecessários. Se a interface com o utilizador for bem conseguida, os utilizadores sentir-se-ão à vontade para comprar no site e facilmente voltarão.

Onde possível, deverá ser aplicada a regra dos 3 cliques, ou seja, ser possível aceder a qualquer informação de um produto ou serviço com apenas 3 cliques, a partir de qualquer área do site. Para tornar isto possível, por vezes é adoptada uma solução de menus dinâmicos. Nestes casos deve-se dar (mais uma vez) especial atenção à compatibilidade entre browsers, uma vez que alguns sistemas de menus são bastante complexos e causam incompatibilidades entre os browsers.

Conclusão

Antes de passar ao desenvolvimento, há que idealizar estruturalmente os pontos chaves da aplicação, para que quando tudo estiver funcional não seja necessário efectuar intervenções gigantescas para melhorar a performance da mesma.

Durante o desenvolvimento há que ter sempre em mente questões relacionadas com a usabilidade e com a navegabilidade, para que a aplicação não se torne demasiado complexa para o utilizador.

Referências:
E-Commerce
Ecommerce Hosting Considerations
Basic ecommerce web site design and development

Windows Vista: Hibernate ou Sleep?

Na semana passada comecei a usar o Windows Vista como meu sistema operativo principal! Não porque me apeteceu, mas porque comprei um portátil e neste vinha instalado o Windows Vista! Pensei eu: já que está, deixa ficar! ;)

Quando pego num computador, a primeira coisa que faço é configurá-lo à minha maneira. Neste processo, uma das coisas que fazia no Windows XP, era activar a função Hibernate. É bastante útil, especialmente nos portáteis, para quando estamos a trabalhar e a bateria está “no casco”. Desta forma escusamos de estar a fechar todas as aplicações para as termos de abrir novamente quando iniciarmos novamente o sistema já com a bateria carregada ou ligados à tomada.

Tal e qual como no Windows XP, fui tentar activar esta funcionalidade. Tarefa que se revelou mais complicada do que parecia à partida. Após alguma pesquisa navegando por várias janelas do Vista, não consegui encontrar o local para a activar. Daí, comecei a procurar em sites e fóruns da especialidade. Até que descobri que para activar esta funcionalidade, bastava entrar na linha de comandos com direitos administrativos e digitar:

C:> powercfg -h on

Os meus problemas estavam resolvidos… após digitar o tal comando, vou todo contente ao Start Menu e voilá… continua na mesma sem a opção Hibernate! Fiquei encalhado… quando toda a gente dizia que este comando tinha resolvido o problema, a mim, aparentemente, não tinha alterado nada.

Após mais umas pesquisas, finalmente compreendo o porquê da coisa. E passo a explicar… no Vista existem três opções a este nível: Sleep, Hybrid Sleep e Hibernate. No meu portátil, inicialmente, apenas aparecia uma opção: Sleep. O que eu não sabia é que este Sleep era na verdade o Hybrid Sleep, que é um misto da funcionalidade de Stand By (em analogia com o Windows XP) e Hibernate. Ou seja, grava todas as aplicações que temos abertas e coloca o sistema quase desligado. Digo quase desligado porque, neste modo, o sistema continua a precisar de energia para se manter activo e com as aplicações abertas como as deixámos. Não era isto que queria, mas sim poder ter o portátil completamente desligado, poder andar com ele de um lado para o outro sem estar preocupado com a energia, e saber que quando o voltasse a ligar, as aplicações estariam tal e qual como as deixei.

Assim, para que pudesse ter acesso à função Hibernate, teria de desactivar o Hybrid Sleep. Feito isto, nas opções de energia, o sistema passa a disponibilizar duas opções: Sleep e Hibernate como estava habituado a ter no Windows XP.

E agora sim, voilá! Já lá está a opção que eu tanto queria! :)

Btw: está a revelar-se um excelente sistema operativo! Experimentem… ;)

Web Semântica

A Web Semântica é um projecto para a web que pretende fazer com que toda a sua informação esteja organizada de forma a que não só os humanos, mas principalmente as máquinas a possam compreender. Isto porque, actualmente, a maioria da informação está organizada de uma forma que apenas os humanos conseguem entender.

Vejamos o seguinte exemplo… vamos imaginar que pretendemos encontrar informações sobre o vinho do porto, mas apenas efectuamos a pesquisa pela palavra “porto”. Como resultado recebemos vários sites de turismo acerca da cidade do Porto, da Câmara do Porto, do Futebol Clube do Porto, etc., mas pouca informação acerca do que realmente era pretendido. Obviamente que podemos (devemos) restringir as pesquisas combinando a palavra “porto” com outras palavras-chave de forma a obtermos apenas os resultados pretendidos. Mas, ainda assim, os resultados são, por vezes, desoladores. Não é difícil a pesquisa devolver dezenas (ou mesmo centenas) de sites que não nos interessam, tornando difícil encontrarmos o que queremos.

Para um humano, filtrar os resultados de uma pesquisa é uma tarefa relativamente fácil, mas pode demorar algum (bastante!) tempo e tornar-se bastante aborrecida. Para que possamos colocar as máquinas a desempenhar esta tarefa, é necessário organizar a informação da web, de forma a que esta faça sentido para as máquinas. O que não deixa de ser curioso, já que o que se pretende é “agradar” às pessoas e não às máquinas! ;)

Com as informações devidamente organizadas, torna-se mais fácil a criação de sistemas e motores de busca mais inteligentes e flexíveis. E, desta forma, as informações poderão ser correctamente compreendidas pelas máquinas, poupando ao utilizador a execução das tarefas acima descritas, recebendo este imediatamente os resultados pretendidos.

Esta organização da informação é possível através do uso de XML, que permite descrever semânticamente os dados, categorizados de acordo com as definições do utilizador. A informação guardada desta forma, pode ser disponibilizada através de diversas plataformas, o que faz do XML uma linguagem bastante flexível. Assim, a informação seria devidamente identificada de forma a que diferentes sistemas a pudessem manipular e usar da forma que lhes for mais útil.

Para uma melhor compreensão de como é guardada e usada a informação através de XML, será bom abordar o tema… num próximo artigo! :)

Optimização de aplicações MySQL – parte II

Vimos na primeira parte deste artigo que uma das melhores formas de optimizar o acesso à informação no MySQL era o uso de índices. Para além do uso de índices, a optimização de joins entre tabelas é também bastante influente na performance do MySQL. Assim, este artigo tentará explicar como se comporta o MySQL na presença de joins e de que forma estes podem ser melhorados. Tudo em prol da optimização, claro! :)

Só para garantir que todos sabem do que estamos a falar, vou começar por definir um join. Um join, em termos matemáticos, é a intersecção de dois conjuntos. O que isto significa é que dois conjuntos são combinados e todos os valores comuns a ambos constituem o resultado do join.

Entendida a definição de join, é importante referir que ao efectuar um join é sempre necessário especificar uma condição de join. Caso contrário, o resultado obtido será o produto cartesiano das duas tabelas envolvidas no join, ou seja, cada registo na tabela A irá aparecer uma vez para cada registo da tabela B. Isto é o mesmo que dizer que o resultado vai ser composto por X * Y linhas, onde X é o número de registos da primeira tabela e Y o número de registos da segunda tabela.

Para completar as definições: a condição de join especifica quais as colunas em cada uma das tabelas que vão ser comparadas e como vão ser comparadas (na maioria dos casos as colunas são comparadas pela igualdade, mas nem sempre isto acontece).

Para ilustrar o que foi dito, deixo um pequeno exemplo:

Tabela de empregados (emp)

id nome dpto
1 Hugo Durães 3
2 Joaquim Ferreira 2
3 Filipa Gonçalves 3
4 André Cardoso 4

Tabela de departamentos (dpto)

id nome
1 Marketing
2 Vendas
3 Técnico

Resultado do join

A operação de join da tabelas de empregados com a tabela de departamentos, pelo campo dpto.id produziria o seguinte resultado:

emp.id emp.nome dpto.id dpto.nome
1 Hugo Durães 3 Técnico
2 Joaquim Ferreira 2 Vendas
3 Filipa Gonçalves 3 Técnico

O query SQL para obter este resultado seria:

SELECT emp.id, emp.nome, dpto.id, dpto.nome
FROM emp, dept
WHERE emp.dpto = dpto.id

Optimização de joins

A primeira coisa a ter em conta acerca da optimização da performance de joins é que esta pode diminuir consideravelmente à medida que a terceira, quarta ou mesmo quinta tabela é adicionada à consulta. Quanto mais tabelas são adicionadas, mais importante é garantir que a consulta está optimizada e testada.

Existem diversas razões para este decréscimo de performance:

  • Quanto mais tabelas estiverem envolvidas, mais registos terão de ser lidos e a probabilidade de estes ficarem em cache será cada vez menor, o que implica leituras a partir do disco rígido;
  • Quanto mais tabelas estiverem envolvidas, mais filtragens e ordenações têm de ser feitas. Estas são operações intensivas que consomem bastante CPU e memória, provocando assim um decréscimo da performance;
  • À medida que o tamando do resultado (número de linhas e colunas a serem devolvidos) aumenta, é cada vez menos provável que as operações de ordenação possam ser feitas em memória e mais provável que os dados tenham de ser escritos em disco rígido para que possam ser ordenados. O acesso ao disco é relativamente mais lento e consome também muito CPU;
  • Num ambiente interactivo, quanto mais tabelas estiverem envolvidas e quanto mais tempo demorar uma consulta, maior é a probabilidade de alguém efectuar actualizações aos registos nas tabelas que estão a ser lidas, o que faz com que porções do resultado tenham de ser lidos novamente de forma a garantir uma visualização consistente dos dados.

Construir bases de dados com bom desempenho

Relativamente aos joins, existem duas formas de maximizar a performance. Uma delas é desenhar a estrutura da base de dados de forma a minimizar o uso de joins. A outra forma consiste em maximizar a performance, optimizando as consultas.

Tanto de uma forma como outra, é bom saber de que forma determinada estrutura de tabelas e determinada consulta poderão afectar a performance da nossa aplicação. Para percebermos o que está a acontecer à medida que decorre o desenvolvimento, existe o comando EXPLAIN do MySQL. Este comando examina uma dada consulta, para verificar se esta está o mais optimizada possível, usando índices sempre que estes podem ser usados.

A sintaxe deste comando é a seguinte:

EXPLAIN SELECT *
FROM tabA, tabB, tabC
WHERE tabA.col1 = tabB.col1 AND tabA.col2 = tabC.col1;

Este comando devolve uma tabela de informação semelhante à representada em seguida:

+-------+------+---------------+------+---------+------+------+-------------+
| table | type | possible_keys | key  | key_len | ref  | rows | Extra       |
+-------+------+---------------+------+---------+------+------+-------------+
| tabA  | ALL  | NULL          | NULL |  NULL   | NULL | 1000 |             |
| tabB  | ALL  | NULL          | NULL |  NULL   | NULL | 1000 | Using where |
| tabC  | ALL  | NULL          | NULL |  NULL   | NULL | 1000 | Using where |
+-------+------+---------------+------+---------+------+------+-------------+

Mas o que significam todas as colunas devolvidas?

  • table – mostra o nome da tabela à qual o resultado diz respeito (para quando são efectuados JOINs entre tabelas);
  • type – tipo de join usado. Do melhor para o pior tipo temos: system, const, eq_ref, ref, range, index, all;
  • possible_keys – indica quais os índices que o MySQL pode usar para encontrar resultados nesta tabela;
  • key – índice usado na consulta, ou NULL caso não tenham sido usados índices;
  • key_len – tamanho do índice usado, caso exista;
  • ref – coluna(s) usada(s) com a key para devolver resultados;
  • rows – número de registos que o MySQL tem que examinar para executar a consulta;
  • extra – informação adicional acerca de como o MySQL vai executar a consulta. A evitar o aparecimento de “using filesort” e “using temporary“.

Sabendo isto, e analisando o resultado obtido, podemos dizer que a consulta efectuada está muito pouco optimizada. Não existem chaves possíveis (possible_keys) para usar, o que implica que o MySQL tenha de percorrer todos os registos da tabela (1000 no exemplo, mas imagine uma tabela com milhões de resultados).

De acordo com o manual do MySQL, as tabelas são listadas na mesma ordem que são lidas enquanto a consulta é processada. O MySQL resolve todos os joins através de um método multi-join que percorre todos os registos. Isto significa que o MySQL lê o primeiro registo da primeira tabela (tabA), de seguida procura um registo correspondente na segunda tabela (tabB), fazendo o mesmo para a terceira tabela (tabC) e assim sucessivamente. Após todas as tabelas serem processadas o MySQL devolve os registos encontrados e volta atrás. Não para a tabA, mas sim para a tabB para procurar se existem mais registos que façam match com a tabA. Caso existam, vai procurar novamente registos da tabC.

É importante reter que os valores da tabela precendente (como listado no resultado do EXPLAIN) são usados para pesquisa na tabela actual, ou seja, o MySQL usa os valores da tabA para encontrar registos na tabB e assim sucessivamente. Sabendo isto, onde adicionar índices para ajudar o MySQL nesta pesquisa? Para responder a esta pergunta é necessário saber o que é que o MySQL procura. O join da tabA com a tabB é feito pela condição “tabA.col1=tabB.col1″. O MySQL já possui o valor para o campo tabA.col1, logo precisa de saber o valor de tabB.col1 de modo a terminar a equação. Assim, um índice deverá ser criado neste campo. O resultado do EXPLAIN após a criação deste campo é o seguinte:

+-------+------+---------------+----------+---------+-----------+------+-------------+
| table | type | possible_keys | key      | key_len | ref       | rows | Extra       |
+-------+------+---------------+----------+---------+-----------+------+-------------+
| tabA  | ALL  | NULL          | NULL     |    NULL | NULL      | 1000 |             |
| tabB  | ref  | idx_col1      | idx_col1 |       5 | tabA.col1 |    1 | Using where |
| tabC  | ALL  | NULL          | NULL     |    NULL | NULL      | 1000 | Using where |
+-------+------+---------------+----------+---------+-----------+------+-------------+

Como é visível, o MySQL usa o idx_col1 para efectuar o join entre tabA e tabB. Agora, ao procurar ocorrências de tabA.col1 na tabB, em vez de percorrer todos os registos como anteriormente, usa o idx_col1 para apanhar apenas os valores pretendidos. Para a pesquisa na tabC continua a ser precisa a pesquisa em todos os registos, mas esta questão pode ser resolvida da mesma forma que a anterior. Neste caso a condição de join é “tabA.col2=tabC.col1″, o que faz com que o campo a ser indexado seja o tabC.col1, uma vez que o valor de tabA.col2 já é conhecido. Após a criação deste índice, o resultado do EXPLAIN é:

+-------+------+---------------+----------+---------+-----------+------+-------------+
| table | type | possible_keys | key      | key_len | ref       | rows | Extra       |
+-------+------+---------------+----------+---------+-----------+------+-------------+
| tabA  | ALL  | NULL          | NULL     |    NULL | NULL      | 1000 |             |
| tabB  | ref  | idx_col1      | idx_col1 |       5 | tabA.col1 |    1 | Using where |
| tabC  | ref  | idx_col1      | idx_col1 |       5 | tabA.col2 |    1 | Using where |
+-------+------+---------------+----------+---------+-----------+------+-------------+

Concluindo…

A tarefa de fazer com que os joins complexos passem a funcionar de uma forma simples, e com que a indexação seja feita nos campos correctos, torna-se relativamente fácil se forem tomados em conta os seguintes pontos:

  • Independentemente da complexidade da consulta, apenas é necessário abordar o join analisando duas tabelas de cada vez, na ordem pela qual estas são listadas pelo EXPLAIN;
  • Os valores das tabelas precedentes já são conhecidos; o esforço tem de ser concentrado em ajudar o MySQL a usar esses valores com índices das tabelas actuais, com vista a encontrar registos.

Para mais informações acerca do comando EXPLAIN, consultar o tópico respectivo do manual do MySQL.

Optimização de aplicações MySQL – parte I

Acontece frequentemente não se pensar muito bem na estrutura de uma base de dados para um site aquando da criação da mesma. O que é certo é que, mais tarde ou mais cedo, dependendo do crescimento do site em termos de informação, os erros de estruturação da base de dados vão-se fazer notar. E normalmente, quando damos conta, temos em mãos um sério problema para resolver, com algumas implicações.

Aconteceu-me recentemente isto que acabei de descrever, e, na tentativa de optimizar estrutura da informação da base de dados em questão, procurei saber mais acerca da indexação da informação e da forma como o MySQL usa esta indexação.

Índices: o que são?

Os índices podem ser entendidos como o turbo do MySQL. São uma versão organizada de campos específicos de uma tabela e são usados para facilitar a consulta de registos. Sem índices, o MySQL necessita percorrer todos os registos de uma tabela para encontrar o registo pretendido, o que não acontece quando se usam índices. Neste caso, o MySQL consegue “saltar” directamente para o registo pretendido. Com o seu uso, as consultas de informação tornam-se mais rápidas e eficazes, poupando tempo de processamento.

Tipos de Índices

Existem vários tipos de índices disponíveis no MySQL:

  • Índices normais – são o tipo de índice mais básico, e não possuem restrições tais como a unicidade;
  • Índices únicos – semelhantes aos índices normais, mas com uma diferença: todos os valores do(s) campo(s) indexado(s) apenas podem aparecer uma vez;
  • Chaves primárias – índices únicos com nome “PRIMARY” e com a particularidade de apenas poder existir um por tabela;
  • Índices full-text – usados pelo MySQL para pesquisas em campos de texto (para não fugir ao tema, sugiro a leitura da documentação do MySQL para mais detalhes sobre este tipo de índices).

Definição de Índices

Os índices podem ser aplicados individualmente a campos de uma tabela da base de dados (índices simples), ou então aplicados a vários campos de uma tabela (índices compostos).

Suponhamos que temos a seguinte tabela:

CREATE TABLE pessoas (
    id INT NOT NULL AUTO_INCREMENT,
    primeiro_nome CHAR(30) NOT NULL,
    ultimo_nome CHAR(30) NOT NULL,
    idade SMALLINT NOT NULL,
    PRIMARY KEY (id)
);

O objectivo é usar esta tabela para obter os valores do campo id para registos cujos valores do primeiro nome, último nome e idade são conhecidos. Um exemplo de consulta seria pesquisar todos os registos com o nome Hugo Durães e 24 anos de idade:

SELECT id FROM pessoas WHERE primeiro_nome='Hugo' AND ultimo_nome='Durães' AND idade=24;

Como não queremos que o MySQL faça uma pesquisa em todos os registos da tabela, o uso de índices é altamente recomendado.

Na minha opinião, o primeiro passo seria criar um índice para um dos campos da claúsula WHERE (primeiro_nome, ultimo_nome ou idade). Se o índice fosse criado no campo primeiro_nome, o MySQL iria limitar a pesquisa aos registos para os quais o campo primeiro_nome fosse ‘Hugo’. Com este resultado temporário, iria aplicar as restantes condições: primeiro iria eliminar todos os registos cujo ultimo_nome fosse diferente de ‘Durães’ e de seguida eliminaria os registos nos quais a idade fosse diferente de 24. Só após isto o MySQL poderia devolver os resultados da consulta.

A criação deste índice torna a consulta mais eficiente, mas ainda obriga o MySQL a trabalhar com registos não necessários. Poderíamos colocar o índice em qualquer outro dos campos, mas os resultados em termos de eficiência seriam muito idênticos.

É aqui que entram os índices compostos. Um índice composto é na verdade um array ordenado que contém concatenados os diferentes valores dos campos que pertencem ao índice composto.

Assim, para o exemplo mostrado acima, seria criando um índice composto pelos campos primeiro_nome, ultimo_nome e idade. Desta forma, o MySQL pode “saltar” imediatamente para o primeiro_nome correcto, depois para o ultimo_nome e finalmente para a idade correcta. A informação foi assim encontrada sem ser necessário percorrer qualquer registo da tabela.

Mas não será a criação deste índice composto a mesma coisa que criar um índice para cada campo da claúsula WHERE? Não, porque ao executar uma consulta, o MySQL apenas usa um índice. Se tiver que existir uma escolha entre vários índices, o MySQL usa normalmente o índice mais restritivo, ou seja, o que devolve o menor número de registos. Ainda assim, qualquer um destes índices nunca é tão restritivo como o índice composto pelos três campos da claúsula WHERE.

Da esquerda para a direita

Os índices múltiplos tem uma vantagem acrescida: são lidos da esquerda para a direita. Assim, e usando o exemplo acima, o índice será usado para consultas de acordo com a seguinte combinação:

  • primeiro_nome, ultimo_nome, idade
  • primeiro_nome, ultimo_nome
  • primeiro_nome

Assim, o índice criado é usado nas seguintes consultas (alguns exemplos):

SELECT * FROM pessoas WHERE primeiro_nome='Hugo';

SELECT * FROM pessoas
  WHERE primeiro_nome='Hugo' AND ultimo_nome='Durães';

SELECT * FROM pessoas
  WHERE primeiro_nome='Hugo' AND ultimo_nome='Durães' AND idade=24;

SELECT * FROM pessoas
  WHERE primeiro_nome='Hugo'
  AND (ultimo_nome='Durães' OR ultimo_nome='Fernandes') AND idade=24;

SELECT * FROM pessoas
  WHERE primeiro_nome='Hugo'
  AND ultimo_nome='Durães' AND idade > 20 AND idade < 26;

No entanto, o índice criado não é usado nas seguintes consultas (alguns exemplos):

SELECT * FROM pessoas WHERE ultimo_nome='Durães';

SELECT * FROM pessoas
  WHERE primeiro_nome='Hugo' OR ultimo_nome='Durães';

SELECT * FROM pessoas
  WHERE idade=24 AND primeiro_nome='Hugo';

Em que campos devem ser criados índices?

Um dos passos mais importantes é a escolha dos campos onde criar os índices. Existem dois locais fulcrais para a criação de índices: campos referenciados na claúsula WHERE e campos usados na claúsula JOIN.

Então basta seguir a “regra” e criar índices em todos os campos que estejam nessas condições? Quase, mas nem sempre. É necessário ter em conta o tipo de comparações que vão ser efectuadas. O MySQL apenas usa índices para comparações do tipo < , <=, =, >, >=, BETWEEN, IN e em algumas do tipo LIKE. Nas comparações do tipo LIKE, o MySQL não usa índices caso o primeiro caracter de pesquisa seja uma wildcard (% ou _).

Desvantagens dos índices

É bastante comum, em aplicações web, existirem mais pedidos simultâneos de consultas do que pedidos de actualização de informação, visto que o número de pessoas a consultar uma aplicação deste tipo é, normalmente, bem maior que o número de pessoas que actualizam a informação. Assim, o ideal será dar prioridade a estas consultas, deixando para segundo plano as actualizações de informação.

A criação de índices, com vista a indexar a informação, resulta numa melhoraria das consultas de informação. Mas, em contrapartida, estamos a dificultar os pedidos de actualização de informação. Logo, a definição dos índices tem de ser bem estudada, de modo a que seja aplicada apenas sobre pontos críticos.

O uso de índices provoca um aumento do espaço ocupado em disco. Assim, a criação de índices não estudada pode provocar um aumento exagerado do tamanho da informação indexada, podendo esta chegar ao seu limite físico em termos de tamanho em disco.

Concluindo…

O uso de índices é um dos aspectos mais importantes para optimizar bases de dados. Não importa o quão simples é uma tabela: uma pesquisa numa tabela não indexada com 1.000.000 de registos nunca será rápida e leve.

Este artigo tentou abordar os aspectos mais importantes relativos ao tema. No entanto, se pretender saber mais, aconselho a consulta do tópico Optimizing Database Structure do manual do MySQL.

Agora quero ver o MySQL a correr “sem espinhas”! ;)

Artigos relacionados:
Optimização de aplicações MySQL – parte II

Não usar refresh para redireccionamentos

Se pretender que o endereço http://www.exemplo.com/foo mostre o conteúdo que está em http://www.exemplo.com/bar, não deve usar técnicas de “refresh” como:

<meta http-equiv=refresh content="1; url=http://www.exemplo.org/bar">

Porquê? Simplesmente porque desta forma a acção do botão “Voltar” do browser será anulada. Um utilizador, ao pressionar o botão “Voltar”, vai fazer com que o “refresh” funcione novamente e será novamente redireccionado para a frente. Provavelmente esta situação vai deixar o utilizador aborrecido o que pode causar que este deixe de visitar o site.

Usar redireccionamentos HTTP

Ao ser usada a meta-tag “refresh” para criar um redireccionamento, o que temos é uma instrucção específica dentro do documento web. Neste caso, espera-se que o browser descarregue a página, analise o seu conteúdo, encontre a instrucção “refresh”, espere o tempo necessário (que pode ser de 0 – imediatamente – até X segundos) e que proceda para o novo endereço.

Por outro lado, um redireccionamento HTTP funciona de uma forma mais directa pois é feito a um outro nível. O servidor, quando recebe um pedido de um documento e está configurado para efectuar o redireccionamento desse documento para outro endereço, responde ao browser indicando-lhe que procure o documento na sua nova localização.

O redireccionamento HTTP é também uma forma muito mais correcta para fazer redireccionamentos, já que, para além do novo endereço, fornece mais informação ao browser: fornece informação acerca do propósito e do tipo de redireccionamento, o que permite ao browser agir de forma diferente dependendo do tipo de redireccionamento. Os tipos de redireccionamentos HTTP são os redireccionamentos permanentes (HTTP 301), redireccionamentos temporários (HTTP 307) e os redireccionamentos indefinidos (HTTP 302).

Deixo alguns links úteis onde se mostra como efectuar a configuração de servidores para usar redireccionamentos HTTP:

Introdução às Cascading Style Sheets (CSS)

As CSS ou, em português, folhas de estilo, são um mecanismo simples para estilizar um documento web, trabalhando em conjunto com liguagens de marcação, como o XHTML, com a finalidade de lhes conferir o aspecto por nós desejado.
Neste trabalho conjunto, as linguagens de marcação definem a estrutura geral do documento web no que diz respeito a cabeçalhos, tabelas, formulários, etc., ficando ao cargo das CSS a formatação de detalhes como tipos, tamanhos e cores das letras, fundos do documento entre outras formatações semelhantes.

Basicamente, podemos dizer que são uma lista de regras, para uma página web ou site. Estas regras podem ser aplicadas a páginas inteiras ou apenas a tags específicas. Por exemplo:

<h1 class="centered">Exemplo</h1>
<p>Este é um texto para exemplo</p>

Como é possível visualizar no exemplo, o elemento “h1″ aparece centrado em relação ao ecrã.

Porquê o uso das CSS?

As CSS são o método mais correcto para estilizar conteúdos web pelas seguintes razões:

  • Permitem uma maior precisão em mudanças de formatação, na medida em que podem ser usadas para estilizar documentos ou tags únicas dentro de um documento;
  • Estando as regras de estilo todas definidas num único local, possibilita que alterações efectuadas a este ficheiro afectem todas as páginas onde estas regras são incluídas;
  • Muitos dos elementos tradicionais e atributos do XHTML, especialmente dedicados à formatação dos conteúdos, estão a deixar de ser suportados, ou seja, estão a tornar-se “deprecated”.

Estrutura das CSS

Nas CSS, cada regra é composta por um selector, que indica onde vai ser aplicado o estilo definido pela regra, e por um conjunto de pares propriedade/valor que define o estilo. O formato básico é o seguinte:

selector { propriedade1: valor1; propriedade2: valor2; }

É visível no exemplo acima que o conjunto de pares de propriedades/valores está entre “{” e “}” — chavetas — e que os pares estão separados por ponto-e-vírgula “;”. Cada par propriedade/valor é separado por dois pontos “:”.

h1 { color: #000000; font-size: 105%; }

Neste último exemplo, o selector é o “h1″, que é o elemento XHTML para um cabeçalho de primeiro nível. O primeiro par propriedade/valor confere a cor preta ao cabeçalho (o código hexadecimal #000000 representa a cor preta). O segundo par dá ao cabeçalho um tamanho 5% maior que o tamanho normal para o texto.

Usando DIV e SPAN para aplicar CSS

Dois elementos muito úteis quando se trabalha com CSS são os elementos “div” e “span”, que podem ser vistos como tags “invisíveis”, já que quando usadas por si só têm pouco significado. No entanto, quando combinadas com regras de estilo, podem ser usadas como “contentores” para aplicar estilos.

No caso de querermos realçar uma palavra poderiamos usar o seguinte:

...uma <span class="negrito">palavra</span> poderiamos...

A definição da regra de estilo para este exemplo seria:

.negrito{ font-weight: bold; }

Para aplicar esta mesma regra a uma secção inteira de um documento poderiamos usar o seguinte:

<div class="negrito">
<h1>Exemplo</h1>
<p>Parágrafo 1...</p>
<p>Parágrafo 2...</p>
<p>Parágrafo 3...</p>
</div>

Há que ter em atenção que caso se definam estilos para os elementos “h1″ e “p”, estes irão sobrepor-se ao estilo definido pela regra “negrito”.

Formas de especificar as CSS

Para aplicar uma CSS a um documento XHTML, é necessário especificar as regras de estilo e de alguma forma associá-las ao documento, de forma a que os conteúdos assumam os estilos definidos.

A CSS pode ser especificada de três formas:

  • Style Sheets Externas – Estas regras de estilo são guardadas num documento separado e incluídas no nosso documento XHTML. O código seguinte deve ser colocado entre as tags head:

<link rel="stylesheet" type="text/css" href="style.css" />

  • Style Sheets Internas – Ao contrário das Style Sheets Externas, as internas são definidas no próprio documento, ficando apenas disponíveis nesse documento, sendo por isso impossível serem referenciadas por outros documentos. O código seguinte deve ser colocado entre as tags head:

<style type="text/css">
<!--
.exemplo { width: 90%; background-color: #CCCCCC; }
-->
</style>

  • Inline Styles – As regras são aplicadas directamente a um elemento XHTML. Esta forma de especificar as regras de estilo é a menos eficiente, visto que é impossível reutilizar estas regras como nos dois primeiros casos. Para além disso, esta forma de especificar as regras de estilo torna difícil possíveis alterações futuras ao aspecto geral de um site, já que obriga a verificar cada elemento nos quais são necessárias alterações.

<span style="color: #000000;">texto</span>

Como interagem entre si?

Depois de saber as três formas de aplicar estilos a um documento, é importante saber como é que estas três formas interagem entre si.

As CSS funcionam como uma hierarquia – estilos aplicados de uma forma geral para os aplicados de forma mais específica. Podemos dizer que as regras incluídas num documento afectam todo um documento ou mesmo todo um site (estilos aplicados de uma forma geral), mas que as regras aplicadas directamente num elemento apenas afectam esse elemento (estilos aplicados de uma forma específica).

Assim, se dois estilos podem ser aplicados ao mesmo elemento, é dada prioridade às regras aplicadas de forma específica, ou seja, se de uma forma geral for especificado que o texto de um site será vermelho, mas especificamente for especificado que este será azul, a cor mostrada será o azul.

Paralelamente a isto, existe também a existência de herança entre estilos, ou seja, se de uma forma geral for especificado que o texto de um site será azul, mas se especificamente for especificado que este será negrito, o texto irá ser mostrado com a cor azul e a negrito.

Concluindo…

Entre as diferentes formas de especificar a CSS a usar num site, recomenda-se o uso de style sheets externas. Desta forma conseguem-se centralizar num só local todas as regras de estilo, o que torna muito mais simples eventuais alterações que possam surgir aos estilos do site. Para além disto, estando as regras centralizadas num só local, o carregamento de um site é bastante mais rápido e leve, já que os browsers apenas lêem a CSS na primeira vez que o site é carregado, ficando depois guardada em cache, o que liberta o servidor web de um processamento mais pesado.