ÍNDICE possível em um campo VARCHAR no MySql

40

Estou trabalhando em um banco de dados MySql , com uma tabela como esta:

+--------------+
|  table_name  |
+--------------+
|    myField   |
+--------------+

... e eu preciso fazer muitas consultas como essa (com 5 a 10 cadeias na lista) :

SELECT myField FROM table_name
WHERE myField IN ('something', 'other stuff', 'some other a bit longer'...)

Serão cerca de 24.000.000 de linhas únicas

1) Devo usar as teclas FULLTEXTou e INDEXpara o meu VARCHAR(150)?
2) Se eu aumentar os caracteres de 150 para 220 ou 250 ... faria uma grande diferença? (Existe alguma maneira de calculá-lo?)
3) Como eu disse, eles serão únicos, portanto o myField deve ser uma CHAVE PRIMÁRIA . Não é raro adicionar uma CHAVE PRIMÁRIA a um campo que já é um VARCHAR INDEX / FULLTEXT?

Mark Tower
fonte
você não precisa usar o PRIMARY por exclusividade. Já existe UNIQUE para isso.
kommradHomer

Respostas:

62

SUGESTÃO # 1: Indexação Padrão

CREATE TABLE mytable
(
    id int not null auto_increment,
    myfield varchar(255) not null,
    primary key (id),
    key (myfield)
);

Se você indexar assim, poderá procurar a sequência inteira ou fazer pesquisas LIKE orientadas para a esquerda

SUGESTÃO # 2: Indexação FULLTEXT

CREATE TABLE mytable
(
    id int not null auto_increment,
    myfield varchar(255) not null,
    primary key (id),
    fulltext (myfield)
);

Você pode usar efetivamente pesquisas de palavras-chave individuais e de frases inteiras. Você precisará definir uma lista de palavras de parada personalizadas, porque o MySQL não indexará 543 palavras .

Aqui estão minhas outras postagens dos últimos dois anos nos índices FULLTEXT

SUGESTÃO # 3: Indexação de hash

CREATE TABLE mytable
(
    id int not null auto_increment,
    myfield varchar(255) not null,
    hashmyfield char(32) not null,
    primary key (id),
    key (hashmyfield)
);

Se você estiver procurando por um valor específico e esses valores puderem ter comprimentos muito além de 32 caracteres, você poderá armazenar o valor do hash:

INSERT INTO mytable (myfield,hashmyfield)
VALUES ('whatever',MD5('whatever'));

Dessa forma, basta pesquisar valores de hash para recuperar resultados

SELECT * FROM mytable WHERE hashmyfield = MD5('whatever');

De uma chance !!!

RolandoMySQLDBA
fonte
Não tenho reputação suficiente para votar na sua resposta, mas devo dizer que foi ÓTIMO. Obrigado pela explicação e pelos exemplos. Eu acho que a indexação de hash é a melhor para o meu caso, é uma solução incrível. Mas ainda há uma pergunta: qual você acha que será o limite de linhas para pesquisas rápidas na tabela? [usando como chave o VARCHAR (32) para pesquisas] #
Mark Tower
2
A opção de hash aqui ainda é um texto e 32 bytes para o que são realmente 16 bytes. Você pode usar um campo bigint com conv (left (md5 ('qualquer que seja')), 16), 16, -10). Não há um número numérico de 16 bytes, mas você pode encontrar metade do md5 suficiente e, em seguida, são apenas 8 bytes no índice
atxdba
11
Não é bom usar MD5 ou SHA1 para produzir seqüências de caracteres que serão indexadas. A distribuição de strings produzidas por funções de hash como MD5 ou SHA1 é aleatória em um espaço grande, o que diminui a eficiência do seu índice, o que pode retardar as instruções INSERT e SELECT. Aqui está o post explicando: code-epicenter.com/…
Sr.M
Peço desculpas, pois este é um tópico antigo, mas minha pergunta foi diretamente relacionada a isso, mas não consigo obter uma resposta clara para minhas necessidades lendo os artigos acima e outros artigos semelhantes. Meu cenário é: estou desenvolvendo um sistema de estoque muito rudimentar, que consiste em apenas uma tabela por enquanto. Ele é acessado externamente por meio de uma API, para que toda a configuração seja mantida em outro local - e é por isso que precisamos apenas de uma única tabela. As duas colunas em que estou pensando em indexar teriam aproximadamente 200 entradas exclusivas cada uma, com comprimento <20 caracteres. Devo considerar adicionar índices?
Mike
Essa pesquisa é orientada para a esquerda like 'a%'?
precisa saber é o seguinte
18

O MySQL permite que você defina o índice prefixado, o que significa que você define os primeiros N caracteres da string original a ser indexada, e o truque é escolher um número N que seja longo o suficiente para oferecer boa seletividade, mas curto o suficiente para economizar espaço. O prefixo deve ser longo o suficiente para tornar o índice quase tão útil quanto seria se você tivesse indexado a coluna inteira.

Antes de prosseguirmos, vamos definir alguns termos importantes. A seletividade do índice é a razão entre o total de valores indexados distintos e o número total de linhas . Aqui está um exemplo para a tabela de teste:

+-----+-----------+
| id  | value     |
+-----+-----------+
| 1   | abc       |
| 2   | abd       |
| 3   | adg       |
+-----+-----------+

Se indexarmos apenas o primeiro caractere (N = 1), a tabela de índice será semelhante à seguinte:

+---------------+-----------+
| indexedValue  | rows      |
+---------------+-----------+
| a             | 1,2,3     |
+---------------+-----------+

Nesse caso, a seletividade do índice é igual a IS = 1/3 = 0,33.

Vamos agora ver o que acontecerá se aumentarmos o número de caracteres indexados para dois (N = 2).

+---------------+-----------+
| indexedValue  | rows      |
+---------------+-----------+
| ab             | 1,2      |
| ad             | 3        |
+---------------+-----------+

Nesse cenário, IS = 2/3 = 0,66, o que significa que aumentamos a seletividade do índice, mas também aumentamos o tamanho do índice. O truque é encontrar o número mínimo N que resultará na seletividade máxima do índice .

Existem duas abordagens que você pode fazer cálculos para sua tabela de banco de dados. Farei uma demonstração no despejo deste banco de dados .

Digamos que desejemos adicionar a coluna last_name nos funcionários da tabela ao índice e queremos definir o menor número N que produzirá a melhor seletividade do índice.

Primeiro, vamos identificar os sobrenomes mais frequentes:

select count(*) as cnt, last_name from employees group by employees.last_name order by cnt

+-----+-------------+
| cnt | last_name   |
+-----+-------------+
| 226 | Baba        |
| 223 | Coorg       |
| 223 | Gelosh      |
| 222 | Farris      |
| 222 | Sudbeck     |
| 221 | Adachi      |
| 220 | Osgood      |
| 218 | Neiman      |
| 218 | Mandell     |
| 218 | Masada      |
| 217 | Boudaillier |
| 217 | Wendorf     |
| 216 | Pettis      |
| 216 | Solares     |
| 216 | Mahnke      |
+-----+-------------+
15 rows in set (0.64 sec)

Como você pode ver, o sobrenome Baba é o mais frequente. Agora, vamos encontrar os prefixos last_name mais frequentes , começando com prefixos de cinco letras.

+-----+--------+
| cnt | prefix |
+-----+--------+
| 794 | Schaa  |
| 758 | Mande  |
| 711 | Schwa  |
| 562 | Angel  |
| 561 | Gecse  |
| 555 | Delgr  |
| 550 | Berna  |
| 547 | Peter  |
| 543 | Cappe  |
| 539 | Stran  |
| 534 | Canna  |
| 485 | Georg  |
| 417 | Neima  |
| 398 | Petti  |
| 398 | Duclo  |
+-----+--------+
15 rows in set (0.55 sec)

Há muito mais ocorrências de cada prefixo, o que significa que precisamos aumentar o número N até que os valores sejam quase os mesmos que no exemplo anterior.

Aqui estão os resultados para N = 9

select count(*) as cnt, left(last_name,9) as prefix from employees group by prefix order by cnt desc limit 0,15;

+-----+-----------+
| cnt | prefix    |
+-----+-----------+
| 336 | Schwartzb |
| 226 | Baba      |
| 223 | Coorg     |
| 223 | Gelosh    |
| 222 | Sudbeck   |
| 222 | Farris    |
| 221 | Adachi    |
| 220 | Osgood    |
| 218 | Mandell   |
| 218 | Neiman    |
| 218 | Masada    |
| 217 | Wendorf   |
| 217 | Boudailli |
| 216 | Cummings  |
| 216 | Pettis    |
+-----+-----------+

Aqui estão os resultados para N = 10.

+-----+------------+
| cnt | prefix     |
+-----+------------+
| 226 | Baba       |
| 223 | Coorg      |
| 223 | Gelosh     |
| 222 | Sudbeck    |
| 222 | Farris     |
| 221 | Adachi     |
| 220 | Osgood     |
| 218 | Mandell    |
| 218 | Neiman     |
| 218 | Masada     |
| 217 | Wendorf    |
| 217 | Boudaillie |
| 216 | Cummings   |
| 216 | Pettis     |
| 216 | Solares    |
+-----+------------+
15 rows in set (0.56 sec)

São resultados muito bons. Isso significa que podemos criar um índice na coluna last_name, indexando apenas os 10 primeiros caracteres. Na definição da tabela, a coluna last_name é definida como VARCHAR(16), e isso significa que salvamos 6 bytes (ou mais, se houver caracteres UTF8 no sobrenome) por entrada. Nesta tabela, existem 1637 valores distintos multiplicados por 6 bytes e cerca de 9 KB, e imagine como esse número aumentaria se nossa tabela contivesse milhões de linhas.

Você pode ler outras maneiras de calcular o número de N em meus índices pré-fixados no MySQL .

Usar as funções MD5 e SHA1 para gerar valores que devem ser indexados também não é uma boa abordagem . Por quê? Leia no post Como escolher o tipo de dados correto para uma chave primária no banco de dados MySQL

Mr.M
fonte
Esta é uma resposta muito detalhada para uma pergunta diferente.
28515 mustaccio
11
Você está brincando comigo?
Mr.M
Você pode explicar o que está errado ou o que não pode ser aplicado à pergunta?
Mr.M
2
Hey MrD. Eu realmente gosto da sua resposta. Por quê ? Na minha velha resposta, eu disse em SUGESTÃO # 1: If you index like this, you can either look for the whole string or do left-oriented LIKE searches. Eu também disse em SUGESTÃO # 3: If you are looking for one specific value and those values could be lengths well beyond 32 characters, you could store the hash value:. Sua resposta demonstra adequadamente por que não se deve usar chaves enormes e indexar caracteres mais à esquerda, o que pode fazer a diferença no desempenho. Sua resposta pertence aqui. +1 para sua resposta e Bem-vindo ao DBA StackExchange.
RolandoMySQLDBA