uso tipos optimizar inner crear consultas con compuestos mysql performance indexing

optimizar - tipos de indices en mysql



Rendimiento de MySQL: tablas mĂșltiples vs. Ă­ndice en una sola tabla y particiones (4)

Me pregunto qué es más eficiente y más rápido en rendimiento:
Tener un índice en una tabla grande o varias tablas más pequeñas sin índices?

Dado que este es un problema bastante abstracto, permítanme hacerlo más práctico:
Tengo una tabla con estadísticas sobre usuarios (20,000 usuarios y aproximadamente 30 millones de filas en general). La tabla tiene alrededor de 10 columnas que incluyen user_id , actions , timestamps , etc.
Las aplicaciones más comunes son: Insertar datos por user_id y recuperar datos por ID_usuario (las SELECT nunca incluyen múltiples user_id''s ).

Ahora hasta ahora tengo un INDEX en el user_id y la consulta se ve algo así como

SELECT * FROM statistics WHERE user_id = 1

Ahora, con más y más filas, la mesa se vuelve más y más lenta. INSERT ralentizan porque el INDEX hace cada vez más grande; SELECT ralentizan, bueno, porque hay más filas para buscar.

Ahora me preguntaba por qué no tener una tabla de estadísticas para cada usuario y cambiar la sintaxis de la consulta por algo como esto:

SELECT * FROM statistics_1

donde 1 representa el user_id obviamente.
De esta forma, no se necesita ningún INDEX y hay mucha menos información en cada tabla, por lo que las INSERT y SELECT deberían ser mucho más rápidas.

Ahora mis preguntas de nuevo:
¿Hay desventajas en el mundo real para manejar tantas tablas (en mi caso 20,000) en lugar de usar una tabla con un INDEX ?
¿Mi enfoque realmente aceleraría las cosas o la búsqueda de la mesa eventualmente ralentizaría las cosas más que todo?


Crear 20,000 tablas es una mala idea. Necesitarás 40,000 tablas en poco tiempo, y luego más.

Llamé a este síndrome Metadata Tribbles en mi libro SQL Antipatterns . Verá que esto sucede cada vez que planea crear una "tabla por X" o una "columna por X".

Esto causa problemas reales de rendimiento cuando tienes decenas de miles de tablas. Cada tabla requiere que MySQL mantenga estructuras internas de datos, descriptores de archivos, un diccionario de datos, etc.

También hay consecuencias operativas prácticas. ¿Realmente desea crear un sistema que requiera la creación de una nueva tabla cada vez que se registre un nuevo usuario?

En cambio, te recomendaría que uses MySQL Partitioning .

Aquí hay un ejemplo de partición de la tabla:

CREATE TABLE statistics ( id INT AUTO_INCREMENT NOT NULL, user_id INT NOT NULL, PRIMARY KEY (id, user_id) ) PARTITION BY HASH(user_id) PARTITIONS 101;

Esto le da la ventaja de definir una tabla lógica, a la vez que divide la tabla en muchas tablas físicas para un acceso más rápido cuando consulta un valor específico de la clave de partición.

Por ejemplo, cuando ejecuta una consulta como su ejemplo, MySQL accede solo a la partición correcta que contiene el user_id específico:

mysql> EXPLAIN PARTITIONS SELECT * FROM statistics WHERE user_id = 1/G *************************** 1. row *************************** id: 1 select_type: SIMPLE table: statistics partitions: p1 <--- this shows it touches only one partition type: index possible_keys: NULL key: PRIMARY key_len: 8 ref: NULL rows: 2 Extra: Using where; Using index

El método de partición HASH significa que las filas se colocan en una partición por un módulo de la clave de partición entera. Esto significa que el mapa de muchos user_id a la misma partición, pero cada partición tendría solo 1 / Nth tantas filas en promedio (donde N es el número de particiones). Y define la tabla con un número constante de particiones, por lo que no tiene que expandirla cada vez que obtiene un nuevo usuario.

Puede elegir cualquier cantidad de particiones hasta 1024 (u 8192 en MySQL 5.6), pero algunas personas han informado problemas de rendimiento cuando alcanzan ese nivel.

Se recomienda utilizar un número primo de particiones. En caso de que los valores de su user_id sigan un patrón (como el uso solo de números pares), usar un número primo de particiones ayuda a distribuir los datos de manera más pareja.

Re sus preguntas en comentario:

¿Cómo podría determinar una cantidad razonable de particiones?

Para la partición HASH, si usa 101 particiones como las que muestro en el ejemplo anterior, entonces cualquier partición dada tiene aproximadamente el 1% de sus filas en promedio. Dijiste que tu tabla de estadísticas tiene 30 millones de filas, por lo que si utilizas esta partición, tendrías solo 300k filas por partición. Eso es mucho más fácil de leer para MySQL. Puede (y debe) usar también índices: cada partición tendrá su propio índice, y será solo el 1% del tamaño de todo el índice en toda la tabla sin particiones.

Entonces, la respuesta a cómo se puede determinar una cantidad razonable de particiones es: ¿qué tan grande es toda la tabla y qué tan grande quieres que sean las particiones en promedio?

¿No debería la cantidad de particiones crecer con el tiempo? Si es así: ¿cómo puedo automatizar eso?

La cantidad de particiones no necesariamente tiene que crecer si usa la partición HASH. Eventualmente puede tener 30 mil millones de filas en total, pero he encontrado que cuando el volumen de datos crece en órdenes de magnitud, eso exige una nueva arquitectura de todos modos. Si sus datos crecen tanto, es probable que necesite fragmentar en varios servidores y particionar en varias tablas.

Dicho esto, puede volver a particionar una tabla con ALTER TABLE:

ALTER TABLE statistics PARTITION BY HASH(user_id) PARTITIONS 401;

Esto tiene que reestructurar la tabla (como la mayoría de los cambios de ALTER TABLE), así que espere que tome un tiempo.

Es posible que desee controlar el tamaño de los datos y los índices en las particiones:

SELECT table_schema, table_name, table_rows, data_length, index_length FROM INFORMATION_SCHEMA.PARTITIONS WHERE partition_method IS NOT NULL;

Al igual que con cualquier tabla, desea que el tamaño total de los índices activos quepa en su grupo de búferes, porque si MySQL tiene que intercambiar partes de los índices dentro y fuera del grupo de búferes durante las consultas SELECCIONAR, el rendimiento se resiente.

Si usa particiones RANGE o LIST, entonces agregar, soltar, fusionar y dividir particiones es mucho más común. Ver http://dev.mysql.com/doc/refman/5.6/en/partitioning-management-range-list.html

Los animo a que lean la sección del manual sobre particionamiento , y también miren esta agradable presentación: Boost Performance With MySQL 5.1 Partitions .


Hay poco que agregar a la respuesta de Bill Karwins. Pero una sugerencia es: compruebe si se necesitan todos los datos para el usuario con todo detalle durante todo el tiempo.

Si desea dar estadísticas de uso o número de visitas o esas cosas, generalmente no obtendrá una granularidad de acciones únicas y segundos para, por ejemplo, el año 2009 desde la vista de hoy. Por lo tanto, podría compilar tablas de agregación y una tabla de archivo (no el archivo del motor, por supuesto) para tener los datos recientes sobre la base de acciones y una descripción general de las acciones anteriores.

Las viejas acciones no cambian, creo.

Y todavía puede entrar en detalles de la agregación con un week_id en la tabla de archivo, por ejemplo.


Para pasar de 1 tabla a 1 tabla por usuario, puede usar la partición para alcanzar una cierta cantidad de tablas / tamaño de tabla en algún punto intermedio.

También puede mantener las estadísticas de los usuarios para tratar de mover a los usuarios "activos" en 1 tabla para reducir la cantidad de tablas a las que debe acceder a lo largo del tiempo.

En resumidas cuentas, hay mucho que puede hacer, pero en gran medida tiene que crear prototipos y pruebas y simplemente evaluar los impactos en el rendimiento de varios cambios que está realizando.


Probablemente dependa del tipo de consultas que planee realizar con frecuencia, y la mejor manera de saberlo con certeza es simplemente implementar un prototipo de ambas y realizar algunas pruebas de rendimiento.

Dicho esto, esperaría que una sola tabla (grande) con un índice lo haga mejor en general porque la mayoría de los sistemas DBMS están muy optimizados para lidiar con la situación exacta de encontrar e insertar datos en tablas grandes. Si tratas de hacer muchas tablas pequeñas con la esperanza de mejorar el rendimiento, eres como pelear contra el optimizador (que generalmente es mejor).

Además, tenga en cuenta que una mesa es probablemente más práctica para el futuro. ¿Qué sucede si quiere obtener estadísticas agregadas sobre todos los usuarios? Tener 20 000 tablas haría esto muy difícil e ineficiente de ejecutar. Vale la pena considerar la flexibilidad de estos esquemas también. Si divides tus mesas de esa manera, es posible que estés diseñando en una esquina para el futuro.