Ejecuto un EXPLAIN
:
mysql> explain select last_name from employees order by last_name;
+----+-------------+-----------+------+---------------+------+---------+------+-------+----------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-----------+------+---------------+------+---------+------+-------+----------------+
| 1 | SIMPLE | employees | ALL | NULL | NULL | NULL | NULL | 10031 | Using filesort |
+----+-------------+-----------+------+---------------+------+---------+------+-------+----------------+
1 row in set (0.00 sec)
Los índices en mi tabla:
mysql> show index from employees;
+-----------+------------+---------------+--------------+---------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+-----------+------------+---------------+--------------+---------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| employees | 0 | PRIMARY | 1 | subsidiary_id | A | 6 | NULL | NULL | | BTREE | | |
| employees | 0 | PRIMARY | 2 | employee_id | A | 10031 | NULL | NULL | | BTREE | | |
| employees | 1 | idx_last_name | 1 | last_name | A | 10031 | 700 | NULL | | BTREE | | |
| employees | 1 | date_of_birth | 1 | date_of_birth | A | 10031 | NULL | NULL | YES | BTREE | | |
| employees | 1 | date_of_birth | 2 | subsidiary_id | A | 10031 | NULL | NULL | | BTREE | | |
+-----------+------------+---------------+--------------+---------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
5 rows in set (0.02 sec)
Hay un índice en last_name pero el optimizador no lo usa.
Así que hago:
mysql> explain select last_name from employees force index(idx_last_name) order by last_name;
+----+-------------+-----------+------+---------------+------+---------+------+-------+----------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-----------+------+---------------+------+---------+------+-------+----------------+
| 1 | SIMPLE | employees | ALL | NULL | NULL | NULL | NULL | 10031 | Using filesort |
+----+-------------+-----------+------+---------------+------+---------+------+-------+----------------+
1 row in set (0.00 sec)
¡Pero todavía no se usa el índice ! ¿Qué estoy haciendo mal aquí?
¿Tiene que ver con el hecho de que el índice es NON_UNIQUE
? Por cierto el apellido esVARCHAR(1000)
Actualización solicitada por @RolandoMySQLDBA
mysql> SELECT COUNT(DISTINCT last_name) DistinctCount FROM employees;
+---------------+
| DistinctCount |
+---------------+
| 10000 |
+---------------+
1 row in set (0.05 sec)
mysql> SELECT COUNT(1) FROM (SELECT COUNT(1) Count500,last_name FROM employees GROUP BY last_name HAVING COUNT(1) > 500) A;
+----------+
| COUNT(1) |
+----------+
| 0 |
+----------+
1 row in set (0.15 sec)
SELECT COUNT(DISTINCT last_name) DistinctCount FROM employees;
2)SELECT COUNT(1) FROM (SELECT COUNT(1) Count500,last_name FROM employees GROUP BY last_name HAVING COUNT(1) > 500) A;
. ¿Cuál es el resultado de cada recuento?SELECT COUNT(1) FullTableCount FROM employees;
y 2)SELECT * FROM (SELECT COUNT(1) Count500,last_name FROM employees GROUP BY last_name HAVING COUNT(1) > 500) A LIMIT 10;
.Respuestas:
PROBLEMA # 1
Mira la consulta
No veo una cláusula WHERE significativa, y tampoco MySQL Query Optimizer. No hay incentivo para usar un índice.
PROBLEMA # 2
Mira la consulta
Le dio un índice, pero el Opitmizer de consulta se hizo cargo. He visto este comportamiento antes ( ¿Cómo forzo a JOIN a usar un índice específico en MySQL? )
¿Por qué debería pasar esto?
Sin una
WHERE
cláusula, Query Optimizer se dice lo siguiente:WHERE
clausula?El Optimizador de consultas eligió el camino de menor resistencia.
Tendrás un pequeño shock, pero aquí está: ¿Sabías que el Optimizador de consultas manejará MyISAM de manera bastante diferente?
¿Probablemente estás diciendo HUH ???? CÓMO ????
MyISAM almacena los datos en un
.MYD
archivo y todos los índices en el.MYI
archivo.La misma consulta producirá un plan EXPLAIN diferente porque el índice vive en un archivo diferente de los datos. Por qué ? Aquí es por qué:
last_name
columna) ya están ordenados en el.MYI
last_name
desde el índice¿Cómo puede estar tan seguro de esto? He probado esta teoría de trabajo sobre cómo el uso de un almacenamiento diferente generará un plan EXPLICAR diferente (a veces mejor): ¿Debe un índice cubrir todas las columnas seleccionadas para que se use ORDER BY?
fuente
En realidad, el problema aquí es que esto parece un índice de prefijo. No veo la definición de la tabla en la pregunta, pero
sub_part
= 700? No ha indexado toda la columna, por lo que el índice no se puede usar para ordenar y tampoco es útil como índice de cobertura. Solo se podría usar para encontrar las filas que "podrían" coincidir conWHERE
ay la capa del servidor (sobre el motor de almacenamiento) tendría que filtrar aún más las filas coincidentes. ¿Realmente necesitas 1000 caracteres para un apellido?Actualización para ilustrar: Tengo una tabla de prueba de tabla con un poco más de 500 filas, cada una con el nombre de dominio de un sitio web en una columna
domain_name VARCHAR(254) NOT NULL
y sin índices.Con la columna completa indexada, la consulta usa el índice:
Entonces, ahora, soltaré ese índice y solo indexaré los primeros 200 caracteres de domain_name.
Voila
Tenga en cuenta también que el índice, con 200 caracteres, es más largo que el valor más largo en la columna ...
... pero eso no hace ninguna diferencia. Un índice declarado con una longitud de prefijo solo se puede usar para búsquedas, no para ordenar, y no como índice de cobertura, ya que no contiene el valor de columna completo, por definición.
Además, las consultas anteriores se ejecutaron en una tabla InnoDB, pero ejecutarlas en una tabla MyISAM arroja resultados prácticamente idénticos. La única diferencia en este caso es que el recuento de InnoDB
rows
está ligeramente desactivado (541) mientras que MyISAM muestra el número exacto de filas (563), que es un comportamiento normal ya que los dos motores de almacenamiento manejan inmersiones de índice de manera muy diferente.Todavía afirmaría que la columna last_name es probablemente más grande de lo necesario, pero aún es posible indexar toda la columna, si está utilizando InnoDB y ejecuta MySQL 5.5 o 5.6:
fuente
varchar(1000)
pero esto está más allá del índice máximo permitido para el índice, que es ~ 750EXPLAIN SELECT ...
, así comoSHOW CREATE TABLE ...
ySELECT @@VERSION;
desde que los cambios en el optimizador en todas las versiones pueden ser relevantes.Respondí porque un comentario no admitirá el formateo y RolandoMySQL DBA habló sobre gen_clust_index e innodb. Y esto es muy importante en una tabla basada en innodb. Esto va más allá del conocimiento normal de DBA porque necesita poder analizar el código C ..
SIEMPRE debe hacer SIEMPRE una CLAVE PRIMARIA o una CLAVE ÚNICA si está usando Innodb. Si no lo hace, innodb usará su propio ROW_ID generado que podría hacerle más daño que bien.
Intentaré explicarlo fácilmente porque la prueba se basa en el código C.
Primer problema
mutex_enter (& (dict_sys-> mutex));
Esta línea se asegura de que solo un hilo pueda acceder a dict_sys-> mutex al mismo tiempo. ¿Qué pasaría si el valor ya tuviera un mutexed? Sí, un subproceso tiene que esperar para obtener una característica aleatoria agradable, como el bloqueo de subprocesos o si tiene más tablas sin su propia PRIMARY KEY o UNIQUE KEY, entonces tendría una buena característica con innodb ' bloqueo de tabla ' no es esta la razón por la cual MyISAM fue reemplazado por InnoDB porque fuera de la característica agradable llamada bloqueo basado en registros / filas.
Segundo problema
(0 == (id% DICT_HDR_ROW_ID_WRITE_MARGIN))
los cálculos de módulo (%) son lentos, no son buenos si está insertando por lotes porque necesita ser recalculado cada vez ..., y porque DICT_HDR_ROW_ID_WRITE_MARGIN (valor 256) es una potencia de dos, esto podría hacerse mucho más rápido.
(0 == (id & (DICT_HDR_ROW_ID_WRITE_MARGIN - 1)))
Nota al margen si el compilador de C se configuró para optimizar y es un buen optimizador, el optimizador de C reparará el código "pesado" a la versión más ligera
el lema de la historia siempre crea tu propia CLAVE PRIMARIA o asegúrate de tener un índice ÚNICO cuando creas una tabla desde el principio
fuente
UNIQUE
sea suficiente; también debe incluir solo columnas que no sean NULL para que el índice único se promocione a PK.INSERT
se dedica a esta función. Sospecho que es insignificante. Contraste el esfuerzo de palear columnas, haga operaciones de BTree, incluyendo una división de bloques ocasional, varios mutexes en el buffer_pool, cosas de cambio de buffer, etc.