MySQL – innoDB e MyISAM

Quando nós viramos a nova versão do And After 2011 sofremos algun dias com problemas de performance – o MySQL que rodava no servidor funcionava normalmente por algum tempo (aleatório) e o consumo de processamento subia loucamente, o site parava de responder e logo o servidor morria. Um reset, e o ciclo voltava a acontecer…

Testei alguns métodos de cache: geração de html, cache de query mas nada foi muito satisfatório – ou quando resolvia o problema prejudicava um pouco a navegação e usabilidade do site (login, publicação de comentários, etc.).

O @fefurst e um pessoal do Twitter falou para eu verificar como estavam configuradas as tabelas do meu banco e falaram para eu tentar converter para MyISAM (o atual padrão de tabelas do mysql) – mas elas já estavam neste formato.

Alterei tudo para innoDB e o problema de performance acabou – o MySQL parou de derrubar o processador e o tempo de resposta voltou ao normal, ouf!

Principais diferenças entre innoDB e MyISAM

InnoDB usa proteção por registro (row locking) enquanto MyISAM usa proteção por tabelas (table locking), por isso em tabelas que são constantemente alteradas (é o caso de algumas tabelas do And After, com as tabelas de logs, comentários, etc) o InnoDB apresenta uma melhor performance do que o MyISAM – que em outros casos é melhor que InnoDB pois não funciona com transações.

Para tabelas usadas essencialmente para consultas (como tabela de localidades, ou de usuários de um sistema fechado) o MyISAM é o mais recomendado.

Dicas de performance com MySQL

Cache

Outra dica para performance – que o Chrisimplementou aqui no And After – é verificar a existência de querys complexas na sua aplicação e pensar em alguma forma de cache para estas querys. Usamos isso na tagcloud, acompanhem a lógica para confirmar a tagcloud:

  1. Percorrer todas as tags do banco
  2. Verificar quantos posts estão associados a cada tag
  3. Ordenar as tags de acordo com o número de posts que utilizam

Essa query é um pouco lenta, tem alguns innerjoins malucos e não é legal ter ela em quase todas as telas. Uma opção é configurar o cache do próprio MySQL, outra opção (que foi o que fizemos) é transformar esta query em uma rotina e criar uma segunda tabela para a tagcloud OU adicionar uma coluna na tabela de tags que representa a relevância daquela tag (no exemplo, o número de posts que a utilizam).

Indexação de buscas

Esta aí algo que eu não posso falar muito pois ainda não tenho familiaridade: sistemas de indexação de busca. Só conheço o Lucene – sei que funciona com o Zend (EN)  e tem como fazer isso funcionar com o Code Igniter (EN) – mas para aplicações com muita informação é uma opção que deve ser considerada com carinho.

Antes de sair por aí alterando coisas em produção verifique se está tudo certo com o agendamento de backup do seu MySQL.

Referências

Validação simples de formulário com jQuery

Normalmente opto pelo uso do jQuery Validate para validação de formulário (veja como ignorar o jQuery Validate), mas em uma tela que estava trabalhando eu tinha vários formulários que abriam em modal (usando o PrettyPhoto) e eram manipulados antes da abertura dependendo das ações do usuário naquela tela.

Ao invés de usar o validate achei que seria mais simples implementar uma função para validação, seguindo a seguinte regra:

  • Os elementos com a classe "required" devem ser obrigatórios
  • Adicionar a classe "error" aos elementos obrigatórios não preenchidos
  • Exibir um elemento com a mensagem de erro do formulário
  • Retornar true se passar na validação

A função ficou bastante simples e poderia ser escrita facilmente sem o uso do jQuery.

Validação sem plugin

function validateForm(form){
    var err = 0;
    form.children('.required').each(function(){
        if($(this).val() == '' || $(this).val() == null){
            $(this).addClass('error');
            form.children('.error_form').show();
            err++;
        }
    })
    if(err == 0)
        return true
}

 

Update

Seguindo a dica do Chris, a validação do exemplo acima pode ser substituída por uma expressão regular que verifica se a string é vazia no javascript, como no exemplo abaixo:

function notNull (x){
        if(a!="" && !(a.match(/^\s+$/))){
            return true;
        }else{
            return false;
        }
}

A função segue a seguinte lógica:

  1. Recebe como parâmetro um objeto do jQuery (optei por isso ao invés do id ou classe pois meu código já tinha uma variável com o form selecionado por questões de performance.
  2. Cria uma variável chamada err com valor 0, que irá contabilizar os erros (caso exitam).
  3. Percorre todos os elementos do formulário e busca pelos que tenham a classe "required", percorrendo todos estes e:

    1. Verifica se o valor do elemento é vazio ou null, se for adiciona a classe "error" no elemento e mostra o elemento com classe "error_form" que está no formulário (caso exista).
    2. Soma 1 a variável err.
  4. Se depois de percorrer todos os elementos com classe "required" nenhum erro for contabilizado retorna true.

 

Seria possível retornar também o número de erros em caso de false, modificando o final da função para:

    if(err == 0)
        return true
    else
        return err;

Sei que validação já é um assunto um pouco "manjado" de javascript e muita gente opta pelos plugins, resolvi compartilhar para mostrar como funciona e exeplificar que nem sempre é necessário a gente inserir plugin para tudo o que vamos fazer…

Seletor por classe no javascript

Vi alguns questionamentos em fóruns sobre seletor de classe em javascript e resolvi compartilhar uma solução do fórum, que extende o document para que seja possível fazer seleção de elementos por classe sem a necessidade de usar uma biblioteca (jQuery, por exemplo) para isso.

Já postei um exemplo em HTML5 da selector API (querySelector), mas como isso só é suportado por browsers modernos a solução abaixo quebra o galho (e se getElementsByClassName for nativa do navegador ele usa a função nativa).

Função getElementByClassName

A função abaixo permite que você user document.getElementByClassName('nomeDaClasse') para fazer seletores sem o uso de nenhuma biblioteca.

if (document.getElementsByClassName == undefined) {
    document.getElementsByClassName = function(className)
    {
        var hasClassName = new RegExp("(?:^|\\s)" + className + "(?:$|\\s)");
        var allElements = document.getElementsByTagName("*");
        var results = [];

        var element;
        for (var i = 0; (element = allElements[i]) != null; i++) {
            var elementClass = element.className;
            if (elementClass && elementClass.indexOf(className) != -1 && hasClassName.test(elementClass))
                results.push(element);
        }

        return results;
    }
}

 

Na maioria dos projetos uso jQuery, mas tem alguns que o javascript é tão simples que não é necessário os 29Kb – e esse seletor de classes já quebra o galho. 🙂

Solução de um colaborador do DevShed.

Backup do MySQL com shell script

Desde a migração do And After (final de dez 2010) para sua nova versão eu não tinha nenhuma rotina de backup do MySQL rodando – e já aprendi que nunca nesta vida devemos ficar sem backup do banco de dados de nossos projetos ou clientes.

Como eu sou "dummie" no Linux (veja você que cheguei a escrever como ver o IP no Linux) eu não sabia como fazer o agendamento de tarefas até descobrir como usar o crontab para agendar rotinas e executar shell scripts.

Ontem pesquisei um pouco sobre a melhor forma de executar um backup do MySQL e as recomendações em fóruns era de que a forma mais correta de fazer um backup sem a necessidade de derrubar o serviço é usando o mysqldump.

O MySQL dump

O mysqldump acessa sua base de dados como qualqer aplicação, por isso não é necessário derrubar o serviço antes do backup. Se você optar pela cópia de arquivos do sistema ao invés do dump (e não derrubar o mysql antes do backup) pode acontecer problema nos arquivos caso sua base seja alterada durante o backup.

O comando para gerar um .sql usando o  mysqdump é recebe algumas variáveis, um exemplo do comando (as variáveis estão explicadas abaixo do comando):

mysqldump -h localhost -u usuario -pSUASENHA nomedabase > meubackup.sql

Todos os itens sublinhados são variáveis e você deve alterar de acordo com as configurações do seu servidor.

localhost – o servidor para acessar o banco MySQL

usuario – o nome do usuário do banco

SUASENHA – password do usuário setado na variável acima (repare que não existe espaço entre -p e a senha digitada

nomedabase – nome da base que será exportada

meubackup.sql – nome do arquivo que será salvo (incluir a extensão .sql)

O arquivo gerado irá ser salvo no local em que o comando foi executado, portanto você deve primeiro ir até a pasta onde você quer salvar seu backup (usando o comando cd), por exemplo:

cd /var/backups/mysql

Feito isso você executa o comando mostrado anteriormente e será gerado o arquivo dentro de /var/backups/mysql.

Shell script para fazer o mysqldump

Sabendo os comandos para executar o dump do MySQL tudo o que você precisa agora é passar estes comando para um arquivo .sh (shell script).

Requisitos e/ou configurações

  • Ubuntu Server 10
  • gzip instalado (sudo apt-get install gzip)
  • MySQL rodando (com possibilidade de executar o mysqldump)
  • Usuário/senha com acesso a base mysql

Não estou dizendo que só vai funcionar na versão do SO que estou passando aí, mas como cada versão tem suas peculiaridades a informação pode ser importante para quem tiver problemas. 😉

#/bin/bash
U_PASTA="/var/backup/mysql"
U_DATA=$(/bin/date +%Y%m%d%H%M%S)
U_CAMINHO="backup-$U_DATA.sql"
U_HOST="localhost"
U_USER="root"
U_PASSWORD="password"
U_DATABASE="database"
#
cd $U_PASTA
mysqldump -h $U_HOST -u $U_USER -p$U_PASSWORD $U_DATABASE > $U_CAMINHO
gzip $U_CAMINHO
echo "Backup do MySQL executado"
 

No início do código as definições das variáveis para o comando mysqdump e também é salvo uma variável com informações de data, que é usada para nomear o arquivo sql gerado.

A lógica do código:

  1. Vai para o caminho especificado onde será salva a exportação do banco
  2. Executa o mysqldump com as configurações das variáveis
  3. Compacta o arquivo exportado (gzip, não mantém o arquivo descompactado)

Meu post anterior foi um tutorial sobre como usar crontab, com ele é possível você criar uma rotina que executa o seu shell script e mantém seus dados seguros. Para o And After criei uma tarefa diária, toda a madrugada uma cópia do banco é salva.

Cuidado!

O shell script deste post apenas gera os backups e não trata os backups antigos. Se você fizer a exportação diária do banco e não tratar de alguma forma seus backups consumirão todo o espaço do seu servidor.

Existem exemplos em que o próprio shell script e exclui os arquivos antigos, achei mais prático deixar isso a cargo do logrotate (que é assunto para outro post).

Como usar o crontab

Finalmente resolvi pesquisar como funciona o agendamento de tarefas no Linux para eu poder agendar o backup do MySQL (uso Ubuntu Server) e o funcionamento do crontab é bem mais simples do que eu pensava.

Como funciona o crontab

O funcionamento do crontab é simples: você tem um arquivo de texto em que cada linha representa uma tarefa agendada, cada linha é composta de 6 variáveis separadas por vírgula, explicadas abaixo.

O primeiro passo é no terminal digitar:

crontab -e

Este comando vai abrir seu arquivo de crontab ou criar um arquivo caso ele ainda não exista. No editor é só inserir uma linha com a rotina que você quer agendar, seguindo a estrutura abaixo abaixo:

*     *     *   *    *        comando a ser executaco
-     -     -   -    -
|     |     |   |    |
|     |     |   |    +----- dia da semana (0 - 6) (Domingo=0)
|     |     |   +------- mês (1 - 12)
|     |     +--------- dia do mês (1 - 31)
|     +----------- hora (0 - 23)
+------------- minuto (0 - 59)

O * representa "todos", por exemplo:

0 4 * * 0 rm /home/gserrano/backup/*

O exemplo acima remove todos os arquivos da pasta /home/gserrano/backup todo o domingo as 4 AM. Para a execução de comandos completos você pode usar shellscript, para isso o seu comando ficaria

0 4 * * 0 sh /home/exemplo.hs

O exemplo acima executa o "exemplo.sh" também as 4 da madrugada aos domingos.

Dicas

Você pode usar vírgula para executar um comando mais de uma vez. O exemplo abaixo vai executar o comando as 4 AM de domingo e quinta-feira:

0 4 * * 0,4 sh /home/exemplo.hs

Você pode usar / para indicar uma repetição dentro do intervalo que vem depois da barra. O exemplo abaixo vai executar o comando a cada 10 minutos, todo o dia:

/10 * * * * sh /home/exemplo.hs

Atenção:  o uso do / para intervalo não é suportado em todos os sistemas.

 

Tentei ser o mais direto e conciso neste pequeno tutorial, se você tiver dúvidas ou quiser corrigir ou acrescentar algo use os comentários.

Referências