MySQL - comprimento e desempenho do varchar

19

Declarar VARCHARtamanho faz sentido para o desempenho? Existe alguma diferença (em velocidade) entre VARCHAR(50)e VARCHAR(255)? Ou definir comprimento é restrição lógica / de design?

Sonique
fonte

Respostas:

31

Essa é uma "pergunta de exame / entrevista" muito comum. Vou responder da melhor maneira possível:

Nos formatos de linha padrão para InnoDB e MyISAM (dinâmico / compacto), a VARCHAR(50)e a VARCHAR(255)armazenam o texto da string da mesma maneira - 1 byte para o comprimento e a string real com entre 1 e 4 bytes por caractere (dependendo da codificação e o caractere real armazenado).

De fato, se bem me lembro, lembro de alguém modificando o dicionário de dados com um editor hexadecimal para mudar algo como a VARCHAR(50)para a VARCHAR(100), para que isso pudesse ser feito dinamicamente (normalmente, isso requer uma reconstrução da tabela). E isso foi possível, porque os dados reais não foram afetados por essa alteração.

Isso não é verdade VARCHAR(256), pois sempre são necessários 2 bytes (pelo menos) para o comprimento.

Então, isso significa que devemos sempre fazer VARCHAR(255), não devemos? Não . Existem várias razões.

Embora o InnoDB possa armazenar um varchar de maneira dinâmica, isso não é verdade para outros mecanismos. O MyISAM possui um formato de tamanho de linha fixo e as tabelas MEMORY são sempre de tamanho fixo. Devemos nos preocupar com esses outros motores? Sim, deveríamos, porque mesmo se não as usarmos diretamente, as tabelas MEMORY são muito usadas para resultados intermediários (tabelas temporárias na memória) e, como os resultados não são conhecidos antecipadamente, a tabela deve ser criada com o tamanho máximo possível - VARCHAR(255)se esse é o nosso tipo. Se você puder pensar no espaço desperdiçado, se estivermos usando a 'utf8' charsetcodificação do MySQL , MEMORY reservará 2 bytes para o comprimento + 3 * 255 bytes por linha(para valores que podem levar apenas alguns bytes no InnoDB). Isso é quase 1 GB em uma tabela de 1 milhão - apenas para o VARCHAR. Isso não apenas causa estresse desnecessário na memória, como também pode provocar as ações a serem executadas no disco, potencialmente diminuindo a velocidade milhares de vezes. Tudo isso devido a uma seleção ruim de seu tipo de dados definido (independentemente do conteúdo).

Também tem algumas consequências para o InnoDB. O tamanho do índice é restrito a 3072 bytes e os índices de coluna única, a 767 bytes *. Portanto, é muito provável que você não consiga indexar completamente umVARCHAR(255) campo (supondo que você use utf8 ou qualquer outra variável de codificação de comprimento).

Além disso, o tamanho máximo de linha embutida para o InnoDB é meia página (em torno de 8000 bytes) e os campos de comprimento variável, como BLOB ou varchar, podem ser armazenados fora da página se não caberem na meia página . Isso tem algumas consequências no desempenho (algumas vezes boas, outras ruins, dependendo do uso) que não podem ser ignoradas. Isso causou alguma estranheza entre os formatos COMPACT e DYNAMIC. Veja, por exemplo: erro 1118: tamanho da linha muito grande. utf8 innodb

Por último, mas não menos importante, como o @ypercube me lembrou, pode ser necessário mais de 1 byte para o comprimento, mesmo se você estiver usando VARCHAR(255), porque a definição está em caracteres, enquanto o comprimento armazena bytes. Por exemplo, REPEAT('ñ', 255)tem mais de 2 ^ 255 bytes em utf8, portanto, seria necessário mais de 1 byte para armazenar seu comprimento:

mysql> SELECT LENGTH(REPEAT('ñ', 255));
+---------------------------+
| LENGTH(REPEAT('ñ', 255))  |
+---------------------------+
|                       510 |
+---------------------------+
1 row in set (0.02 sec)

mysql> SELECT CHAR_LENGTH(REPEAT('ñ', 255));
+--------------------------------+
| CHAR_LENGTH(REPEAT('ñ', 255))  |
+--------------------------------+
|                            255 |
+--------------------------------+
1 row in set (0.00 sec)

Portanto, o conselho geral é usar o menor tipo possível , pois isso pode potencialmente criar problemas de desempenho ou gerenciamento. A VARCHAR(100)é melhor que VARCHAR(255)(embora a VARCHAR(20)seja melhor), mesmo que você não saiba o tamanho exato. Tente ser conservador porque, a menos que a tabela seja muito grande, você sempre poderá alterar a definição posteriormente.

Atualização: como a popularidade explosiva de cadeias de comprimento variável, por exemplo, com o uso de emojis, a Oracle tem buscado melhorar o desempenho para esses casos. Nas versões mais recentes do MySQL (5.6, 5.7), o InnoDB foi definido como o mecanismo padrão para tabelas temporárias intrínsecas e explícitas, o que significa que os campos de comprimento variável são agora cidadãos de primeira classe. Isso significa que pode haver menos razões para ter comprimentos de caracteres muito restritos (mas eles ainda existem).

(*) Segunda atualização : large_prefix_index agora está habilitado por padrão nas versões mais recentes do MySQL (8.0), mas isso ainda é verdadeiro para versões mais antigas ou se você estiver usando formatos de arquivo / linha lagacy innodb (que não sejam dinâmicos ou compactados), mas agora por padrão, os índices de coluna única podem ter até esses 3072 bytes.

jynus
fonte
pequena atualização: O MySQL-8.0.13 + usa TempTable por padrão para tabelas temporárias que possuem armazenamento eficiente para varchars.
danblack 14/01
0

Esqueça o prefixo de 1 a 2 bytes ativado VARCHARs.

  • Ela afeta o desempenho em uma quantidade minúscula.
  • É "2" mais frequentemente do que a regra óbvia diz.

A pergunta sobre 255 foi feita e respondida várias vezes.

  • Muito tempo VARCHARspode levar ao fracasso de CREATE TABLE.
  • Tabelas temporárias podem se transformar em MEMORYtabelas, com VARCHARstransformadas em VARCHAR. Isso significa, por exemplo, que VARCHAR(255) CHARACTER SET utf8mb4deseja um comprimento fixo de 1020 bytes. (Isso falhará e degenerará usando o MyISAM.)

Conclusão: não use cegamente 255 (ou 256); faça o que faz sentido para o esquema.

Rick James
fonte