Existem desvantagens em usar um varchar (255) genérico para todos os campos baseados em texto?

100

Eu tenho uma contactstabela que contém campos, como postcode, first name, last name, town, country, phone numberetc, todos os quais são definidos como VARCHAR(255)embora nenhum desses campos nunca vai chegar perto de ter 255 caracteres. (Se você está se perguntando, é assim porque as migrações do Ruby on Rails mapeiam campos String VARCHAR(255)por padrão e eu nunca me preocupei em substituí-lo).

Uma vez que VARCHAR armazenará apenas o número de caracteres reais do campo (junto com o comprimento do campo), há alguma vantagem distinta (desempenho ou não) em usar, digamos, VARCHAR(16)over VARCHAR(255)?

Além disso, a maioria desses campos possui índices. Um tamanho VARCHAR maior no campo afeta o tamanho ou o desempenho do índice?

Para sua informação, estou usando o MySQL 5.

Olly
fonte
2
@ceejayoz, afirmando que a resposta aceita está incorreta sem explicar por que realmente não ajuda. O que torna tudo ainda pior é que a resposta aceita pode mudar com o tempo e seu comentário confundirá as pessoas fazendo-as pensar que a nova resposta aceita está incorreta.
Gili
1
@Gili Excluiu meu comentário porque o OP aparentemente mudou sua aceitação. Pontos positivos, no futuro indicarei de que resposta estou falando e por quê.
ceejayoz
Algumas outras respostas para esta pergunta duplicada, stackoverflow.com/questions/1262174/…
James McMahon

Respostas:

129

No armazenamento, VARCHAR(255)é inteligente o suficiente para armazenar apenas o comprimento necessário em uma determinada linha, ao contrário do CHAR(255)que sempre armazenaria 255 caracteres.

Mas já que você marcou esta questão com o MySQL, mencionarei uma dica específica do MySQL: conforme as linhas são copiadas da camada do mecanismo de armazenamento para a camada SQL, os VARCHARcampos são convertidos CHARpara obter a vantagem de trabalhar com linhas de largura fixa. Portanto, as strings na memória são preenchidas até o comprimento máximo da VARCHARcoluna declarada .

Quando sua consulta gera implicitamente uma tabela temporária, por exemplo, durante a classificação ou GROUP BY, isso pode usar muita memória. Se você usar muitos VARCHAR(255)campos para dados que não precisam ser tão longos, isso pode tornar a tabela temporária muito grande.

Você também pode gostar de saber que esse comportamento de "preenchimento" significa que uma string declarada com o conjunto de caracteres utf8 chega a três bytes por caractere, mesmo para strings armazenadas com conteúdo de um byte (por exemplo, caracteres ascii ou latin1). Da mesma forma, o conjunto de caracteres utf8mb4 faz com que a string preencha até quatro bytes por caractere na memória.

Portanto, a VARCHAR(255)in utf8 que armazena uma string curta como "Sem opinião" leva 11 bytes no disco (dez caracteres de conjuntos de caracteres inferiores, mais um byte para o comprimento), mas ocupa 765 bytes na memória e, portanto, em tabelas temporárias ou resultados classificados.

Eu ajudei usuários do MySQL que, sem saber, criaram tabelas temporárias de 1,5 GB com frequência e ocuparam seu espaço em disco. Eles tinham muitas VARCHAR(255)colunas que, na prática, armazenavam strings muito curtas.

É melhor definir a coluna com base no tipo de dados que você pretende armazenar. Ele tem benefícios para impor restrições relacionadas ao aplicativo, como outras pessoas mencionaram. Mas tem os benefícios físicos de evitar o desperdício de memória que descrevi acima.

É difícil saber qual é o endereço postal mais longo, é claro, e é por isso que muitas pessoas escolhem um longo VARCHARque certamente é mais longo do que qualquer endereço. E 255 é comum porque é o comprimento máximo de a VARCHARpara o qual o comprimento pode ser codificado com um byte. Também era o VARCHARcomprimento máximo no MySQL anterior a 5.0.

Bill Karwin
fonte
6
Sempre pensei que 255era usado para que o comprimento da string pudesse caber em um único byte
BlueRaja - Danny Pflughoeft
3
@BlueRaja: Isso provavelmente era verdade para bancos de dados cuja estrutura de arquivo interno codificava o comprimento de uma string em um único byte, ou se eles codificavam strings curtas em um único byte. Mas não é mais verdade para a maioria dos bancos de dados.
Bill Karwin
7
@BlueRaja: InnoDB não armazena o comprimento do seguinte varchar, ele armazena uma série de deslocamentos de campo para todos os campos na linha. Esses deslocamentos de campo podem ser de 1 byte se o tamanho total da linha for inferior a 127 bytes ou 2 bytes. Consulte forge.mysql.com/wiki/MySQL_Internals_InnoDB
Bill Karwin
6
@BlueRaja: MyISAM (para aqueles que ainda o usam) armazena comprimentos de varchar, e estes podem ser armazenados em 1 ou 2 bytes. No entanto: "Ao enviar uma chave para o manipulador de index_read () ou records_in_range, sempre usamos um comprimento de 2 bytes para o VARCHAR para tornar as coisas mais simples." Consulte forge.mysql.com/wiki/MySQL_Internals_MyISAM
Bill Karwin
1
uma pergunta - classificar e agrupar em qualquer campo ou no próprio campo varchar?
Rohit Banga,
24

Além das considerações de tamanho e desempenho ao definir o tamanho de um varchar (e possivelmente mais importante, já que o armazenamento e o processamento ficam mais baratos a cada segundo), a desvantagem de usar varchar (255) "apenas porque" é a integridade dos dados reduzida .

Definir limites máximos para strings é uma boa coisa a se fazer para evitar que strings maiores do que o esperado entrem no RDBMS e causem saturações de buffer ou exceções / erros posteriormente ao recuperar e analisar valores do banco de dados que são maiores (mais bytes) do que o esperado.

Por exemplo, se você tem um campo que aceita cadeias de caracteres de dois caracteres para abreviações de países, não há razão concebível para esperar que seus usuários (neste contexto, programadores) insiram nomes completos de países. Como você não deseja que eles insiram "Antigua e Barbuda" (AG) ou "Ilha Heard e Ilhas McDonald" (HM), você não permite isso na camada de banco de dados. Além disso, é provável que alguns programadores ainda não tenham feito o RTFM da documentação de design ( que certamente existe ) para saber que não deve fazer isso.

Defina o campo para aceitar dois caracteres e deixe o RDBMS lidar com isso (seja graciosamente truncando ou desajeitadamente rejeitando seu SQL com um erro).

Exemplos de dados reais que não têm razão para exceder um determinado comprimento:

  • Os códigos postais canadenses têm o formato A1A1A1 e sempre têm 6 caracteres, mesmo para o Papai Noel (6 caracteres excluem o espaço que pode ser especificado para legibilidade).
  • endereços de e-mail - até 64 bytes antes do @, até 255 bytes depois. Nunca mais, para não quebrar a Internet.
  • Os números de telefone da América do Norte nunca têm mais de 10 dígitos (excluindo o código do país).
  • Os computadores que executam (versões recentes do) Windows não podem ter nomes de computador com mais de 63 bytes , embora mais de 15 não seja recomendado e prejudique o farm de servidores do Windows NT.
  • Abreviações de estado têm 2 caracteres (como os códigos de país exemplificados acima)
  • Os números de rastreamento da UPS podem ter 18, 12, 11 ou 9 caracteres. Os números de 18 caracteres começam com "1Z" e os números de 11 caracteres começam com "T", o que faz você se perguntar como eles entregam todos aqueles pacotes se não sabem a diferença entre letras e números.

E assim por diante...

Reserve um tempo para pensar sobre seus dados e seus limites. Se você é arquiteto, desenvolvedor ou programador, o trabalho é seu , afinal.

Usando um varchar (n) em vez de varchar (255), você elimina o problema onde os usuários (usuários finais, programadores, outros programas) inserem dados inesperadamente longos que voltarão para assombrar seu código mais tarde.

E eu não disse que você também não deveria implementar essa restrição no código de lógica de negócios usado por seu aplicativo.

shufler
fonte
5
Os códigos postais canadenses têm, na verdade, 7 dígitos, o espaço no meio é importante e deve ser mostrado nas etiquetas de envio. Os números de telefone da América do Norte podem ter mais de 10 dígitos, se houver uma extensão. Se você não consegue armazenar ramais de número de telefone, então não há problema com 10 dígitos, mas provavelmente você se arrependerá.
Kibbee
3
Definitivamente, é necessário restringir a integridade dos dados. Porém, ainda é fácil ser muito restritivo. Imponha restrições para os dados que você controla e impõe restrições razoáveis para os requisitos de dados que você não pode controlar. Suas restrições de número de telefone e e-mail são razoáveis ​​(supondo que você nunca se internacionalize). Sua exigência que diz que truncar um código de país de dois caracteres é a coisa "elegante" é insana. Você sabe que houve um erro, não trunque e aceite. Se você truncar, há uma probabilidade extremamente alta de terminar com um código de país incorreto.
coderjoe
A maioria dos aplicativos terá a validação de dados feita antes de enviá-los para o banco de dados ...
Cobby
2
Certo. A maioria. Mas sinto que aqui você está assumindo que um desenvolvedor que está desenvolvendo um novo aplicativo para um banco de dados existente está ciente das restrições aos dados (nem todos somos especialistas em todos os tipos de dados e como eles são implementados em todos os bancos de dados ) Só porque você pode validar dados em seu aplicativo, não significa que você o fez.
shufler
3
the design documentation (which surely exists)Hah. : D
Camilo Martin
14

Estou contigo. A atenção minuciosa aos detalhes é uma dor de cabeça e tem valor limitado.

Era uma vez, o disco era um bem precioso e costumávamos suar para otimizá-lo. O preço do armazenamento caiu por um fator de 1.000, tornando o tempo gasto na compressão de cada byte menos valioso.

Se você usar apenas campos CHAR, poderá obter linhas de comprimento fixo. Isso pode economizar um pouco de atualização real do disco se você escolheu tamanhos precisos para os campos. Você pode obter dados mais densamente compactados (menos E / S para varreduras de tabela) e atualizações mais rápidas (mais fácil localizar espaços abertos em um bloco para atualizações e inserções).

No entanto, se você superestimar seus tamanhos, ou se os tamanhos reais dos dados forem variáveis, você acabará perdendo espaço com campos CHAR. Os dados ficarão menos compactados (levando a mais E / S para grandes recuperações).

Geralmente, os benefícios de desempenho da tentativa de colocar um tamanho em campos variáveis ​​são mínimos. Você pode facilmente fazer o benchmark usando VARCHAR (255) em comparação com CHAR (x) para ver se você pode medir a diferença.

No entanto, às vezes, preciso fornecer uma dica "pequena", "média", "grande". Portanto, uso 16, 64 e 255 para os tamanhos.

S.Lott
fonte
13

Hoje em dia, não consigo imaginar que isso realmente importe mais.

Há uma sobrecarga computacional no uso de campos de comprimento variável, mas com os excessos das CPUs hoje, nem vale a pena considerar. O sistema de E / S é tão lento que torna inexistentes quaisquer custos computacionais para lidar com os varchars. Na verdade, o preço de um varchar computacionalmente é provavelmente uma vitória líquida sobre a quantidade de espaço em disco economizado usando campos de comprimento variável em vez de campos de comprimento fixo. Provavelmente, você tem maior densidade de linha.

Agora, a complexidade dos campos varchar é que você não pode localizar facilmente um registro por meio de seu número de registro. Quando você tem um tamanho de linha de comprimento fixo (com campos de comprimento fixo), é trivial calcular o bloco de disco para o qual um id de linha aponta. Com um tamanho de linha de comprimento variável, isso sai pela janela.

Então, agora você precisa manter algum tipo de índice de número de registro, assim como qualquer outra chave primária, OU você precisa fazer um identificador de linha robusto que codifica detalhes (como o bloco, etc.) no identificador. Se você fizer isso, porém, o id terá que ser recalculado se alguma vez a linha for movida no armazenamento persistente. Não é grande coisa, basta reescrever todas as entradas do índice e certificar-se de que você a) nunca o exponha ao consumidor ou b) nunca afirme que o número é confiável.

Mas, como temos campos varchar hoje, o único valor de varchar (16) sobre varchar (255) é que o banco de dados aplicará o limite de 16 caracteres no varchar (16). Se o modelo de banco de dados deve ser realmente representativo do modelo de dados físico, ter comprimentos de campos pode ser valioso. Se, no entanto, for simplesmente "armazenamento" em vez de um "modelo E armazenamento", não há necessidade de qualquer coisa.

Em seguida, você simplesmente precisa discernir entre um campo de texto que é indexável (como varchar) e algo que não é (como um campo de texto ou CLOB). Os campos indexáveis ​​tendem a ter um limite de tamanho para facilitar o índice, enquanto os campos CLOB não (dentro do razoável).

Will Hartung
fonte
5

Na minha experiência, se você permitir um tipo de dados de 255 caracteres, algum usuário estúpido (ou algum testador experiente) vai realmente preencher isso.

Então você terá todos os tipos de problemas, incluindo quanto espaço você permite para esses campos em relatórios e exibições na tela em seu aplicativo. Sem falar na possibilidade de exceder o limite por linha de dados em seu banco de dados (se você tivesse mais do que alguns desses campos de 255 caracteres).

É muito mais fácil escolher um limite razoável no início, depois aplicá-lo por meio do aplicativo e do banco de dados.

BradC
fonte
0

É uma boa prática alocar apenas um pouco além do que você precisa. Os números de telefone nunca seriam tão grandes.

Um dos motivos é que, a menos que você valide entradas grandes, sem dúvida alguém usará tudo o que existe. Então você pode ficar sem espaço em sua linha. Não tenho certeza sobre o limite do MySQL, mas 8060 é o tamanho máximo de linhas no MS SQL.

Um padrão mais normal seria 50 imho e, em seguida, aumentaria quando necessário.

pomba
fonte
Obrigado. Eu definitivamente concordo em ser uma boa prática. É o aspecto da performance que eu realmente gostaria de esclarecer
Olly
0

Em um contexto mysql, pode ser importante ao trabalhar com índices nas colunas varchar ditas, pois mysql tem um máximo. limite de 767 bytes por linha de índice.

Isso significa que ao adicionar um índice em várias colunas varchar 255 você pode chegar a este limite rapidamente / ainda mais rápido nas colunas utf8 ou utf8mb4 como apontado nas respostas acima

staabm
fonte