Qual é o impacto no desempenho do uso de CHAR vs VARCHAR em um campo de tamanho fixo?

58

Eu tenho uma coluna indexada que armazena um hash MD5. Assim, a coluna sempre armazenará um valor de 32 caracteres. Por qualquer motivo, isso foi criado como um varchar em vez de um char. Vale a pena migrar o banco de dados para convertê-lo em um caractere? Isso está no MySQL 5.0 com InnoDB.

Jason Baker
fonte
6
AVISO Esta pergunta e suas respostas foram escritas antes do InnoDB e utf8 serem os padrões.
Rick James

Respostas:

56

Uma pergunta semelhante foi feita antes

Implicações de desempenho dos tamanhos MySQL VARCHAR

Aqui está o trecho da minha resposta

Você deve perceber as vantagens e desvantagens de usar CHAR vs VARCHAR

Com os campos CHAR, o que você aloca é exatamente o que recebe. Por exemplo, CHAR (15) aloca e armazena 15 bytes, independentemente de como você coloca os caracteres no campo. A manipulação de strings é simples e direta, pois o tamanho do campo de dados é totalmente previsível.

Com os campos VARCHAR, você obtém uma história completamente diferente. Por exemplo, o VARCHAR (15) na verdade aloca dinamicamente até 16 bytes, até 15 para dados e, pelo menos, 1 byte adicional para armazenar o comprimento dos dados. Se você tiver a string 'hello' para armazenar que terá 6 bytes, não 5. A manipulação da string sempre deve executar alguma forma de verificação de comprimento em todos os casos.

A troca é mais evidente quando você faz duas coisas: 1. Armazenando milhões ou bilhões de linhas 2. Colunas de indexação que são CHAR ou VARCHAR

TRADEOFF # 1 Obviamente, o VARCHAR possui a vantagem, já que dados de comprimento variável produziriam linhas menores e, portanto, arquivos físicos menores.

TRADEOFF # 2 Como os campos CHAR exigem menos manipulação de sequência devido às larguras fixas, as pesquisas de índice no campo CHAR são, em média, 20% mais rápidas que as dos campos VARCHAR. Esta não é nenhuma conjectura da minha parte. O livro MySQL Database Design and Tuning executou algo maravilhoso em uma tabela MyISAM para provar isso. O exemplo no livro fez algo como o seguinte:

ALTER TABLE tblname ROW_FORMAT=FIXED;

Esta diretiva força todos os VARCHARs a se comportarem como CHARs. Eu fiz isso no meu trabalho anterior em 2007 e peguei uma tabela de 300 GB e acelerou as pesquisas de índice em 20%, sem alterar mais nada. Funcionou como publicado. No entanto, produziu uma tabela com quase o dobro de tamanho, mas isso simplesmente remonta ao tradeoff # 1.

Você pode analisar os dados armazenados para ver o que o MySQL recomenda para a definição de colunas. Basta executar o seguinte em qualquer tabela:

SELECT * FROM tblname PROCEDURE ANALYSE();

Isso percorrerá a tabela inteira e recomendará definições de coluna para cada coluna com base nos dados que ela contém, nos valores mínimos de campo, no máximo e em outros itens. Às vezes, você só precisa usar o bom senso ao planejar CHAR vs VARCHAR. Aqui está um bom exemplo:

Se você estiver armazenando endereços IP, a máscara para essa coluna terá no máximo 15 caracteres (xxx.xxx.xxx.xxx). Eu pularia imediatamente, CHAR(15)porque os comprimentos dos endereços IP não variariam muito e a complexidade adicional da manipulação de strings controlada por um byte adicional. Você ainda pode fazer um PROCEDURE ANALYSE()contra essa coluna. Pode até recomendar o VARCHAR. Meu dinheiro ainda estaria em CHAR sobre VARCHAR nesse caso.

Os problemas CHAR x VARCHAR podem ser resolvidos apenas através do planejamento adequado. Com grande poder vem uma grande responsabilidade (clichê, mas é verdade).

ATUALIZAR

Quando se trata do MD5, o cálculo strleninterno deve ser eliminado ao alternar todo o formato da linha. Não seria necessário alterar a definição do campo.

Se a chave MD5 for o único VARCHAR presente, eu a utilizaria e converteria o formato da linha da tabela em fixo . Se houver um número significativo de outros campos VARCHAR presentes, eles também serão beneficiados. Em troca, a tabela seria expandida para aproximadamente o dobro do seu tamanho. Mas as consultas devem acelerar cerca de 20% a mais sem ajuste adicional.

RolandoMySQLDBA
fonte
1
Eu acho que eu usaria um char (4) ou algo como um inteiro sem sinal para um endereço IP
Jack Douglas
@JackPDouglas Você está certo nesse ponto.
RolandoMySQLDBA
Os índices não são armazenados com um comprimento fixo, afinal? Não entendo como alterar o formato de armazenamento para comprimento fixo melhorou as pesquisas de índice. Você quer dizer que melhorou as varreduras de tabela?
Marcus Adams
1
@JackDouglas, por que não bite binary?
Pacerier
@Pacerier que seria melhor, eu concordo :)
Jack Douglas
19

Parece que você economizará 1 byte por valor ou cerca de 3% convertendo para a char. Provavelmente não vale a pena se você estiver armazenando o MD5 em hexadecimal de qualquer maneira - você pode economizar 50% usando um binary.

Agradecemos a Ovais (veja comentários) por apontar que char(32)pode usar muito mais do que 32 bytes se você estiver usando um conjunto de caracteres multibyte.

Agradecemos a Rick James por indicar que você deve usar a unhexfunção para converter a string hexadecimal em binária:

create table foo(bar varbinary(100));
insert into foo(bar) values(md5('a')); 
insert into foo(bar) values(unhex(md5('a'))); 
select length(bar) from foo;
| comprimento (bar) |
| ----------: |
| 32
| 16

db <> mexer aqui

Jack Douglas
fonte
Boa chamada para mudar para binário.
RThomas
Estou planejando converter isso em um binário. Agora que penso nisso, o tamanho não deve ser diferente apenas com base no fato de eu estar usando um byte ou um char, já que nossa codificação é utf-8. Ou eu estou errado?
Jason Baker
@ Jason - a codificação não se aplica a binary- ou eu entendi errado?
Jack Douglas
3
para uma coluna char (32) com um conjunto de caracteres utf-8, todo valor precisaria de 32x3 bytes para armazenamento. Por que você precisaria definir o valor do hash MD5 como utf-8. A conversão para binário (32) precisaria de 32 bytes por valor.
Ovais.tariq
1
Mudar para BINARYfaz muito pouco, a menos que você também use UNHEX(). Ou seja, você pode armazenar UNHEX(MD5(x))em um 16-byte BINARY(16)para economizar espaço significativa sobre o armazenamento MD5(x)em CHAR(32) CHARACTER SET ascii.
Rick James
15

Não vale a pena mudar na minha opinião. Se você examinar a documentação aqui, ela deve ilustrar a diferença entre os dois. No cenário de uso, um não oferece realmente nenhum benefício significativo sobre o outro, a menos que você esteja realmente preocupado com a sobrecarga extra relacionada ao tamanho da linha.

http://dev.mysql.com/doc/refman/5.0/en/char.html

Observe também o primeiro comentário na documentação que eu vinculo acima ... "O CHAR só acelerará o seu acesso se todo o registro tiver tamanho fixo. Ou seja, se você usar qualquer objeto de tamanho variável, é possível fazer todos eles tamanho da variável. Você não ganha velocidade usando um CHAR em uma tabela que também contém um VARCHAR "

RThomas
fonte
Essa "aceleração" se aplica ao MyISAM, não ao InnoDB.
21616 Rick Rick James