ÍNDICE posible en un campo VARCHAR en MySql


40

Estoy trabajando en una base de datos MySql , con una tabla como esta:

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

... y necesito hacer muchas consultas como esta (con 5-10 cadenas en la lista) :

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

Habrá alrededor de 24,000,000 filas únicas

1) ¿Debo usar una tecla FULLTEXTo INDEXpara mi VARCHAR(150)?
2) Si aumento los caracteres de 150 a 220 o 250 ... ¿haría una gran diferencia? (¿Hay alguna forma de calcularlo?)
3) Como dije, serán únicos, por lo que myField debería ser una CLAVE PRIMARIA . ¿No es raro agregar una CLAVE PRIMARIA a un campo que ya es un ÍNDICE VARCHAR / FULLTEXT?


no necesita usar PRIMARY para ser único. Ya hay ÚNICO para eso.
kommradHomer

Respuestas:


62

SUGERENCIA # 1: Indexación estándar

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

Si indexa de esta manera, puede buscar la cadena completa o realizar búsquedas LIKE orientadas a la izquierda

SUGERENCIA # 2: indexación FULLTEXT

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

Puede utilizar eficazmente las búsquedas de palabras clave individuales, así como frases completas. Deberá definir una lista de palabras clave personalizada porque MySQL no indexará 543 palabras .

Aquí están mis otras publicaciones de los últimos dos años en índices FULLTEXT

SUGERENCIA # 3: Indexación 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)
);

Si está buscando un valor específico y esos valores podrían tener una longitud muy superior a 32 caracteres, puede almacenar el valor hash:

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

De esa manera, solo busca valores hash para recuperar resultados

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

Darle una oportunidad !!!


No tengo la reputación suficiente para votar tu respuesta, pero debo decir que fue GENIAL. Gracias por la explicación y los ejemplos. Creo que la indexación hash es la mejor para mi caso, es una solución increíble. Pero aún queda una pregunta: ¿cuál cree que será el límite de filas para búsquedas rápidas en la tabla? [utilizando como CLAVE el VARCHAR (32) para búsquedas]
Mark Tower

2
La opción de hash aquí sigue siendo un texto y 32 bytes para lo que realmente es 16 bytes. Puede usar un campo bigint con conv (left (md5 ('whatever'), 16), 16, -10). No hay un número numérico de 16 bytes, pero es posible que la mitad de md5 sea suficiente y solo
contenga

1
No es bueno usar MD5 o SHA1 para producir cadenas que se indexarán. La distribución de cadenas producidas por funciones hash como MD5 o SHA1 es aleatoria en un gran espacio que disminuye la eficiencia de su índice, lo que puede ralentizar las instrucciones INSERT y SELECT. Aquí hay una publicación que lo explica: code-epicenter.com/…
Mr.M

Pido disculpas ya que este es un hilo viejo, pero mi pregunta se relacionó directamente con esto, pero no puedo obtener una respuesta clara para mis necesidades leyendo los artículos anteriores y otros similares. Mi escenario es: estoy desarrollando un sistema de existencias muy rudimentario que consta de una sola tabla por ahora. Se accede externamente a través de una API, por lo que toda la configuración se lleva a cabo en otro lugar, razón por la cual solo necesitamos una sola tabla. Las dos columnas que estoy pensando en indexar, tendrían aproximadamente 200 entradas únicas cada una, de longitud <20 caracteres. ¿Debería considerar agregar índices?
Mike

¿Es orientado a la izquierda como la búsqueda like 'a%'?
Contador م

18

MySQL le permite definir un índice prefijado, lo que significa que define los primeros N caracteres de la cadena original a indexar, y el truco consiste en elegir un número N que sea lo suficientemente largo como para proporcionar una buena selectividad, pero lo suficientemente corto como para ahorrar espacio. El prefijo debe ser lo suficientemente largo como para que el índice sea casi tan útil como lo sería si indexara toda la columna.

Antes de continuar, definamos algunos términos importantes. La selectividad del índice es la relación del total de valores indexados distintos y el número total de filas . Aquí hay un ejemplo para la tabla de prueba:

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

Si indexamos solo el primer carácter (N = 1), la tabla de índice se verá como la siguiente tabla:

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

En este caso, la selectividad del índice es igual a IS = 1/3 = 0.33.

Veamos ahora qué sucederá si aumentamos el número de caracteres indexados a dos (N = 2).

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

En este escenario IS = 2/3 = 0.66, lo que significa que aumentamos la selectividad del índice, pero también hemos aumentado el tamaño del índice. El truco consiste en encontrar el número mínimo N que dará como resultado una selectividad de índice máxima .

Hay dos enfoques que puede hacer cálculos para su tabla de base de datos. Haré una demostración en el volcado de esta base de datos .

Digamos que queremos agregar la columna last_name en la tabla de empleados al índice, y queremos definir el número N más pequeño que produzca la mejor selectividad del índice.

Primero identifiquemos los apellidos más frecuentes:

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 puede ver, el apellido Baba es el más frecuente. Ahora vamos a encontrar los prefijos de apellido más frecuentes , comenzando con los prefijos 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)

Hay muchas más ocurrencias de cada prefijo, lo que significa que tenemos que aumentar el número N hasta que los valores sean casi los mismos que en el ejemplo anterior.

Aquí están los 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    |
+-----+-----------+

Aquí están los 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)

Estos son muy buenos resultados. Esto significa que podemos hacer que el índice en la columna last_name indexe solo los primeros 10 caracteres. En la columna de definición de tabla, last_name se define como VARCHAR(16), y esto significa que hemos guardado 6 bytes (o más si hay caracteres UTF8 en el apellido) por entrada. En esta tabla hay 1637 valores distintos multiplicados por 6 bytes que son aproximadamente 9 KB, e imagine cómo crecería este número si nuestra tabla contiene millones de filas.

Puede leer otras formas de calcular el número de N en mis índices indexados en MySQL .

Usar las funciones MD5 y SHA1 para generar valores que deberían indexarse ​​tampoco es un buen enfoque . ¿Por qué? Léalo en la publicación Cómo elegir el tipo de datos correcto para una clave primaria en la base de datos MySQL


Esta es una respuesta muy detallada a una pregunta diferente.
mustaccio

1
¿Me estás tomando el pelo?
Sr.M

¿Puedes explicar qué está mal o qué no se puede aplicar a la pregunta?
Sr.M

2
Hola MrD. En realidad me gusta tu respuesta. Por qué ? En mi respuesta anterior, dije en la sugerencia # 1: If you index like this, you can either look for the whole string or do left-oriented LIKE searches. También dije en la sugerencia # 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:. Su respuesta demuestra adecuadamente por qué uno no debe usar teclas enormes y debe indexar los caracteres más a la izquierda, lo que puede marcar una diferencia en el rendimiento. Tu respuesta pertenece aquí. +1 por su respuesta y Bienvenido a DBA StackExchange.
RolandoMySQLDBA
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.