domingo, 29 de janeiro de 2012

Descobrindo dados pouco utilizados

Abaixo vou descrever uma forma de como poeríamos criar um script no Linux para pesquisar quais dados estão sendo menos utilizados no banco, desta forma, estes poeriam ser candidatos a popular uma tabela compactada melhorando a performance de consulta e economizando espaço em disco.

linux# grep "from " /var/log/mysql/mysqld.log | awk -Ffrom '{print $2}' | awk '{print $1}' | sort | uniq | cat > /var/lib/mysql/test/tablelist.txt

mysql> create table test.tableslog (tname varchar(250));
mysql> load data infile 'tablelist.txt' into table tableslog;

mysql> select t1.tname, t2.TABLE_NAME from test.tableslog as t1 LEFT join information_schema.TABLES as t2 on t1.tname = t2.TABLE_NAME group by t1.tname having t2.TABLE_NAME IS NULL

Espero que seja útil para vocês.

Visual Explain para o MySQL

Existem muitas ferramentas para se utilizar juntamente com o MySQL
seja para a coleta de dados ou informações sobre o funcionamento do banco de dados e sistema operacional onde estes operam.

No MySQL temos um comando bastante útil chamado EXPLAIN, este comando
mostra quais os caminhos que uma query percorre antes que lhe seja retornado um resultset. O EXPLAIN é uma ótima forma de descobrirmos problemas com nossas queries como por exemplo consultas que estão executando uma varredura total de uma tabela ou de um arquivo de index entre outras utilidades que veremos mais adiante com mais detalhes.

O Visual Explain, aliás, antes que você se decepcione, este utilitário não tem nada de recursos gráficos, apenas mostra os resultados do comando EXPLAIN
de forma mais intuitiva na forma de árvores.

Segue link para download e mais informações do programa:
Download Visual Explain

MySQL e o performance_schema

Nova versão do MySQL lançada e mais um recurso para facilitar a vida dos DBAs.
No MySQL já estamos acostumados a obter informações referentes a tabelas, bancos e características do servidor, usuários etc. Todas essas informações são conhecidas por metadatas e podem ser adquiridas executando queries nas tabelas de dois bancos principais; information_schema e mysql.

A partir da versão 5.5 mais precisamente 5.5.3-m3 temos outro banco, o performance_schema, aqui existem tabelas que podemos utilizar para obter informações de baixo nível referente a performance.

Foi adicionado também informações específicas ao funcionamento e performance do InnoDB.

Segue os links com mais informações no site oficial do mysql:

Estamos criando um artigo especialmente para explorar melhor este novo recurso do MySQL.

MySQL e o Query Cache

Uma forma bem simples e rápida de melhorar significantemente a performance de consultas no MySQL é saber utilizar corretamente o cache de resultados de suas consultas.

O query cache basicamente é um local na memória onde o MySQL armazena o texto de sua consulta "SELECT" juntamente com o resultado retornado por esta consulta, teoricamente, sempre que esta consulta for executada novamente não será necessário o mysqld fazer todo o trabalho para obter os dados resultantes sendo que estes já estarão disponíveis na memória.

Vamos aprender a utilizar o query cache, em primeiro lugar precisamos saber se está disponível, para isso vamos entrar o comando:

mysql> SELECT @@have_query_cache;
+--------------------+
| @@have_query_cache |
+--------------------+
| YES |
+--------------------+
1 row in set (0.00 sec)

Bem, isso já nos diz que o query cache está disponível.

Veremos agora como ele está definido:

mysql> show variables like '%query_cache%';
+------------------------------+--------+
| Variable_name | Value |
+------------------------------+--------+
| have_query_cache | YES |
| query_cache_limit | 524288 |
| query_cache_min_res_unit | 2048 |
| query_cache_size | 128000 |
| query_cache_type | ON |
| query_cache_wlock_invalidate | OFF |
+------------------------------+--------+
6 rows in set (0.00 sec)

O query_cache_size, nos diz o tamanho em bytes reservado para o query cache, neste caso 128Mb

O query_cache_type irá informar o tipo de cacheamento que estamos utilizando, ele pode ser:

0 ou OFF; Não armazena nem busca resultados do query cache

1 ou ON; Armazena no cache o resultado de todas as consultas válidas para o cacheamento, exceto quando seguidas pela cláusula SQL_NO_CACHE

2 ou DEMAND; Armazena apenas os resultados de consultas válidas e seguidas por SQL_CACHE

O query cache em ação:

Em primeiro lugar se o query_cache_size for 0 então vamos definir seu valor e conseqüentemente será habilitado.

mysql> SET GLOBAL query_cache_size=128000;

O que acabamos de fazer ?
Definimos nosso query cache para ter 128 Mb, isso quer dizer que teremos todo este espaço disponível para armazenar o resultado de nossas consultas. Esse valor escolhi apenas para testes e não significa que seja o valor ideal.

Vamos utilizar uma consulta simples para testar o query cache, estou utilizando um banco de dados específico que em breve estarei disponibilizando aqui no blog, por enquanto veja o resultado:

1- Primeira vez que executo meu SELECT com o query cache habilitado

mysql> SELECT pedido.id, pedido.data, cliente.nome, cliente.sobrenome, cliente.email FROM pedido, cliente WHERE cliente.id=pedido.cliente_id;

............

100 rows in set (0.11 sec)

Suprimi as linhas para economizar o espaço e o SELECT está bem intuitivo e básico para que você entenda como estamos pesquisando.
O resultado foi retornado em 0.11 segundos

2- Vamos executar novamente o mesmo SELECT e observar quanto tempo irá demorar para sair o resultado.

100 rows in set (0.00 sec)

Ótimo. Agora a mesma consulta demorou 0.00 segundos mas o que aconteceu ?

Bem, isso é o nosso query cache trabalhando para armazenar o resultado de nossa consulta, como foi dito antes, na segunda vez que executei o mesmo SELECT o resultado foi retirado diretamente do query cache e não foi necessário nenhuma execução interna para obte-los.

Vamos analisar algumas variáveis referentes ao query cache e aprender mais sobre elas.

Digite o comando:

root@localhost livraria> SHOW STATUS LIKE 'Qc%';
+-------------------------+--------+
| Variable_name | Value |
+-------------------------+--------+
| Qcache_free_blocks | 1 |
| Qcache_free_memory | 112400 |
| Qcache_hits | 1 |
| Qcache_inserts | 2 |
| Qcache_lowmem_prunes | 0 |
| Qcache_not_cached | 6 |
| Qcache_queries_in_cache | 1 |
| Qcache_total_blocks | 5 |
+-------------------------+--------+
8 rows in set (0.01 sec)

É importante observar que em Qcache_hits está mostrando quantas vezes utilizamos o conteúdo que está no query cache, e em Qcache_inserts temos a quantidades de resultados que foram inseridos em nosso cache, a cada nova query válida para para preencher o query cache este valor é incrementado.

Ao executar novamente a mesma consulta observamos que nosso Qcache_hits é incrementado.

Quando estamos utilizando o query cache temos que observar também o valor contido em Qcache_lowmem_prunes, esse valor quanto mais próximo de zero mais satisfatório para o bom uso do query cache. Essa variável é responsável por indicar quantas consultas foram "deletadas" do query cache devido a baixa memória definida.

O que não vai para o meu query cache?

1- Prepared Statements
2- Subqueries que se originam de uma OUTER query
3- Queries executadas dentro do corpo de uma função ou triggers
4- Todas as queries que utilize NO_CACHE como por exemplo, SELECT .... FROM .... NO_CACHE;
5- Resultados retornados por funções como SELECT NOW(), BENCHMARK etc.

Outra observação sobre o query cache é que a forma como o MySQL trara o query cache é totalmente case sensitive, ou seja, uma query SELECT * FROM LIVROS, é totalmente diferente de uma query Select * from LiVrOS e assim por diante.

O quanto devo reservar para meu query cache?

Isso irá depender de alguns fatores como: quanto de memória eu tenho no equipamento ? quantas consultas estou utilizando que serão executadas repetidamente ?

No próximo artigo veremos mais detalhes sobre o query cache e como definí-lo corretamente. Até lá.

Particionamento no MySQL parte I

Bem, após o grande bla bla bla sobre o futuro do MySQL o nosso bom e parrudo gerenciador de banco de dados ainda sobrevive, O MySQL 5.5 trouxe novos recursos e correções de alguns bugs existentes nas versões anteriores, um dos novos recursos é forma como o MySQL trata o particionamento agora mais flexível e intuitivo como aprenderemos a seguir.

Em primeiro lugar para quem nunca utilizou o particionamento de tabelas no MySQL saiba que isso é possível, podemos dividir em várias outras "tabelas lógicas" uma única tabela, porém antes do lançamento da versão 5.5 estávamos limitados ao particionamento apenas por campos numéricos se fosse necessário o uso de campos date ou strings tinhamos que lançar mão de funções e converter cada um dos tipos tratados, isso agora mudou. Vamos a um exemplo:

CREATE DATABASE part_exemplo; USE part_exemplo;

CREATE TABLE despesas (
data DATE NOT NULL,
categoria VARCHAR(30),
valor DECIMAL(10,2)
);

Após incluirmos alguns registros, poderíamos criar o particionamento já na criação da tabela, mas vamos fazer isso com esta já pronta e com alguns dados inseridos.

INSERT INTO despesas VALUES( '2010-01-03', 'alimentação', 500.50);
INSERT INTO despesas VALUES( '2010-01-04', 'combustível', 30);
INSERT INTO despesas VALUES( '2010-01-05', 'hospedagem', 210.50);
INSERT INTO despesas VALUES( '2010-01-06', 'comunicacao', 500.50);
INSERT INTO despesas VALUES( '2010-01-07', 'compra', 600.00);
INSERT INTO despesas VALUES( '2010-01-08', 'vestimenta', 300.30);
INSERT INTO despesas VALUES( '2010-01-09', 'manutenção', 120.50);
INSERT INTO despesas VALUES( '2010-01-10', 'software', 700.90);

Agora vamos criar a partição baseada nas colunas e utilizando o tipo LIST

ALTER TABLE despesas
PARTITION BY LIST COLUMNS (categoria)
(
PARTITION p0 VALUES IN ('alimentacao', 'combustivel'),
PARTITION p1 VALUES IN ('hospedagem', 'comunicacao'),
PARTITION p2 VALUES IN ('compra', 'vestimenta'),
PARTITION p3 VALUES IN ('manutencao', 'software')
);

Vejamos o resultado do nosso particionamente.

mysql> explain partitions select * from despesas\G

Esta query irá retornar se nossas partições estão criadas devidamente. Se você está seguindo nosso exemplo aqui terá que obter o seguinte resultado:

*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: despesas
partitions: p0,p1,p2,p3
type: ALL
possible_keys: NULL
key: NULL
key_len: NULL
ref: NULL
rows: 8
Extra:
1 row in set (0.00 sec)

Ótimo! Não precisamos mais ter que usar complexas funções para converter dados do tipo não numéricos para utilizar o particionamento.

Nos próximos posts aprenderemos como utilizar colunas DATE nesta nova versão do MySQL 5.5 bem como vários novos recursos implementados. Até lá.

MySQL Slap: Batendo forte em nosso banco de dados

Continuando com nossa análise sobre teste de desempenho no MySQL vamos aprender hoje a utilizar uma ferramenta bastante poderosa, o mysqlslap, com ele poderemos executar várias tarefas, utilizar vários usuários ao mesmo tempo simulando um load casual de um servidor. Vamos lá.

O mysqlslap é distribuído juntamente com a instalação do mysql, geralmente se você não encontrá-lo no diretório padrão tente fazer o download de alguma distribuição com o título "noinstall".

Se sua instalação MySQL está completa e foi feita de forma correta com todos os PATHs e diretórios padrão basta digitar mysqlslap e pressionar ENTER e serão mostrados os resultados de execução.

shell> mysqlslap --user=bench_user --password --auto-generate-sql
Enter password:
Benchmark
Average number of seconds to run all queries: 0.035 seconds
Minimum number of seconds to run all queries: 0.035 seconds
Maximum number of seconds to run all queries: 0.035 seconds
Number of clients running queries: 1
Average number of queries per client: 0


Vamos a um exemplo mais complexo, para isto vou criar uma tabela utilizando a maioria dos tipos de dados existentes no MySQL. Segue os comandos abaixo:

/* Cria o banco de dados */
CREATE DATABASE bench;

/* Cria a tabela */
CREATE TABLE bench (
id int not null auto_increment primary key,
c1 int,
c2 CHAR(30),
c3 VARCHAR(30),
c4 FLOAT(10,2),
c5 DECIMAL(10,2),
c6 TEXT
) ENGINE=MyISAM;

Bem, essa será nossa tabela de testes, após a criação do banco de dados e da tabela descrita acima vamos criar um arquivo .SQL para incluir uma query com INSERTs nesta tabela.

INSERT INTO bench.bench VALUES(null,100000,'AA','AA',789.89,789.89,'TEXTO AQUI');

Insira os comandos acima em um arquivo e grave-o como bench.sql, lembre-se que apenas será executado apenas um comando por linha, na query acima você pode incluir o tamanho máximo de cada tipo criado, quis deixar assim para facilitar o exemplo.

Agora vamos utilizar o que criamos acima para executar com o mysqlslap.

mysqlslap --user=user_bench -p --concurrency=10 --iterations=10 --query=bench.sql

Vamos entender os parâmetros acima:

--user e -p -> usuário e senha que será utilizado para performar o teste, neste caso deve ter as permissões INSERT na tabela em questão.

--concurrency -> a quantidade de conexões simultâneas que serão utilizadas para executar a query expecificada, por exemplo, neste caso estaremos fazendo com que 10 conexões executem cada uma 10 vezes a mesma query.

--iterations -> a quantidade de queries que serão executadas para cada conexão especificada em concurrency

Após pressionar ENTER obteremos o resultado do nosso teste, claro que em seu servidor os valores poderão ser diferentes.

Benchmark
Average number of seconds to run all queries: 0.125 seconds
Minimum number of seconds to run all queries: 0.124 seconds
Maximum number of seconds to run all queries: 0.127 seconds
Number of clients running queries: 10
Average number of queries per client: 10


Abaixo vamos ver mais algumas opções interessantes para utilizar com o mysqlslap:

--auto-generate-sql -> gera automaticamente um query para execução

--auto-generate-sql-add-auto-increment -> gera automaticamente tabelas que contém campos do tipo AUTO_INCREMENT

--burnin -> executa os testes no modo burn-in, ou seja, executa todos os parâmetros especificados de forma ininterrupta, implementado no MySQL 6.0

--engine -> utiliza um determinado ENGINE para executar os testes

Bem, aqui você já tem uma idéia de como utilizar o mysqlslap para testar suas queries e o desempenho do seu servidor. Para saber mais parâmetros sobre o mysqlslap utilize mysqlslap --help


Até a próxima

MariaDB: Transacional ou não ?

Para criar uma tabela no MariaDB podemos fazer como segue:

CREATE TABLE t1 (c1 VARCHAR(20)) TRANSACTIONAL=1 ENGINE=Maria;

Espere! O que o parâmetro TRANSACTIONAL estám fazendo ai ?

Bem, na atual versão estável 5.1.44 até a data que escrevo este artigo o storage Maria ainda não é transacional, mas quando criar suas tabelas pode habilitar o parâmetro TRANSACTIONAL=1 pois em um futuro próximo como o próprio criador do software diz em seu site será implementado o suporte a transações no Maria sendo assim quando estiver tudo pronto e o Maria se tornar cem por cento transacional você não precisará alterar tabela por tabela para habilitar este recurso.

Para saber mais detalhes sobre o Maria acesse o site http://askmonty.org/wiki/Maria

Em breve estarei escrevendo um pouco mais sobre os principais storages utilizados no Maria e suas capacidades. Até lá.