¿Qué es más eficiente: varias tablas MySQL o una tabla grande?

103

Guardo varios detalles de usuario en mi base de datos MySQL. Originalmente, se configuró en varias tablas, lo que significa que los datos están vinculados con UserIds y se generan a través de llamadas a veces complicadas para mostrar y manipular los datos según sea necesario. Al configurar un nuevo sistema, casi tiene sentido combinar todas estas tablas en una gran tabla de contenido relacionado.

  • ¿Será esto una ayuda o un obstáculo?
  • ¿Consideraciones de velocidad al llamar, actualizar o buscar / manipular?

Aquí hay un ejemplo de algunas de las estructuras de mi tabla:

  • usuarios: ID de usuario, nombre de usuario, correo electrónico, contraseña cifrada, fecha de registro, ip
  • user_details: datos de cookies, nombre, dirección, detalles de contacto, afiliación, datos demográficos
  • user_activity - contribuciones, última conexión, última visita
  • user_settings - configuración de visualización del perfil
  • user_interests: variables a las que se puede orientar publicidad
  • user_levels - derechos de acceso
  • user_stats - hits, recuentos

Editar: He votado a favor de todas las respuestas hasta ahora, todas tienen elementos que esencialmente responden a mi pregunta.

La mayoría de las tablas tienen una relación 1: 1 que fue la principal razón para desnormalizarlas.

¿Habrá problemas si la tabla abarca más de 100 columnas cuando es probable que una gran parte de estas celdas permanezcan vacías?

Peter Craig
fuente
Esta otra pregunta también podría ser útil
Mosty Mostacho

Respuestas:

65

Varias tablas ayudan de las siguientes formas / casos:

(a) si diferentes personas van a desarrollar aplicaciones que involucran diferentes tablas, tiene sentido dividirlas.

(b) Si desea otorgar diferentes tipos de autoridades a diferentes personas para diferentes partes de la recopilación de datos, puede ser más conveniente dividirlas. (Por supuesto, puede considerar la definición de vistas y darles la autorización correspondiente).

(c) Para mover datos a diferentes lugares, especialmente durante el desarrollo, puede tener sentido usar tablas que resulten en tamaños de archivo más pequeños.

(d) Una huella más pequeña puede brindarle comodidad mientras desarrolla aplicaciones sobre la recopilación de datos específicos de una sola entidad.

(e) Es una posibilidad: lo que pensaba que era un dato de valor único puede llegar a ser realmente valores múltiples en el futuro. por ejemplo, el límite de crédito es un campo de valor único a partir de ahora. Pero mañana, puede decidir cambiar los valores como (fecha de, fecha a, valor de crédito). Las tablas divididas pueden resultar útiles ahora.

Mi voto sería a favor de varias tablas, con datos debidamente divididos.

Buena suerte.

usuario115905
fuente
3
@RohitKhatri: Hasta donde yo sé, tener varias tablas aumentará el rendimiento en la mayoría de los casos.
Hari Harker
1
@HariHarker Gracias por tu respuesta, pero me di cuenta de que depende de tu patrón de acceso.
Rohit Khatri
Hasta hace poco, siempre almacenaba todos los datos en una tabla, pero ahora que lo pienso, tiene muchas ventajas dividir los datos en términos de rendimiento (dependiendo del caso de uso, por supuesto), semántica (algunos datos se agrupan mejor en un mesa diferente) y desarrollo. Por ejemplo, estoy desarrollando un sistema ERP personalizado en este momento sobre un sistema heredado. Tuve que expandir las tablas de la base de datos anterior con columnas adicionales. Decidí crear nuevas tablas para los nuevos datos. Algunas funciones nuevas son útiles para el sistema heredado y ahora puedo integrarlas fácilmente sin tener que volver a escribir demasiadas consultas anteriores
Ogier Schelvis
35

Combinar las tablas se llama desnormalizar.

Puede (o no) ayudar hacer algunas consultas (que hacen muchas JOINs) para que se ejecuten más rápido a expensas de crear un infierno de mantenimiento.

MySQLes capaz de utilizar un único JOINmétodo, a saber NESTED LOOPS.

Esto significa que para cada registro en la tabla conducida, MySQLubica un registro coincidente en la tabla conducida en un bucle.

Localizar un registro es una operación bastante costosa que puede tardar docenas de veces más que el escaneo de un registro puro.

Mover todos sus registros a una tabla le ayudará a deshacerse de esta operación, pero la tabla en sí aumenta de tamaño y la exploración de la tabla lleva más tiempo.

Si tiene muchos registros en otras tablas, el aumento de la exploración de la tabla puede sobrepasar los beneficios de la exploración secuencial de los registros.

El infierno de mantenimiento, por otro lado, está garantizado.

Quassnoi
fuente
1
Si tiene 10000 usuarios y está haciendo una unión con una base de datos configurada con claves externas correctamente, entonces solo debería necesitar la búsqueda intensa haciendo algo como seleccionar * de usuarios donde nombre = "bob". Una vez que tenga bob, entonces está usando un índice para encontrar las tablas unidas a bob, que es significativamente más rápido porque está usando la identificación de bob. Esto sucede independientemente de si está haciendo una combinación en su consulta o consultando a Bob y luego consultando una tabla por separado. Por supuesto, con suerte, su segunda consulta se basa en la identificación de Bob y no en otra cosa.
Rudy García
17

¿Son todas relaciones 1: 1? Quiero decir, si un usuario pudiera pertenecer a, digamos, diferentes niveles de usuario, o si los intereses de los usuarios están representados como varios registros en la tabla de intereses del usuario, entonces la fusión de esas tablas estaría fuera de discusión de inmediato.

Con respecto a las respuestas anteriores sobre la normalización, debe decirse que las reglas de normalización de la base de datos han ignorado por completo el rendimiento, y solo están mirando lo que es un diseño de base de datos ordenado. Eso es a menudo lo que quiere lograr, pero hay ocasiones en las que tiene sentido desnormalizar activamente en pos del rendimiento.

En general, diría que la pregunta se reduce a cuántos campos hay en las tablas y con qué frecuencia se accede a ellos. Si la actividad del usuario a menudo no es muy interesante, entonces podría ser una molestia tenerla siempre en el mismo registro, por razones de rendimiento y mantenimiento. Si se accede a algunos datos, como la configuración, por ejemplo, con mucha frecuencia, pero simplemente contienen demasiados campos, es posible que tampoco sea conveniente fusionar las tablas. Si solo está interesado en la ganancia de rendimiento, puede considerar otros enfoques, como mantener la configuración separada, pero guardarla en una variable de sesión propia para que no tenga que consultar la base de datos con mucha frecuencia.

David Hedlund
fuente
Tengo que estar completamente en desacuerdo con su comentario de que la normalización solo se centra en la pulcritud y no tiene en cuenta el rendimiento. Existe una compensación en ambos escenarios y la desnormalización pone en riesgo la integridad de los datos. Yo diría que la normalización de su base de datos en realidad mejora el rendimiento general de la base de datos en lugar de tener un aumento de rendimiento rápido e insignificante de una tabla desnormalizada.
Rudy García
Dado que la discusión es específicamente sobre relaciones 1: 1, dividir las tablas no es una tarea de normalización , ¿verdad? Si no hay información duplicada, es normal incluso cuando se trata de una sola tabla. (Bueno, puede que no satisfaga la 3NFnormalización, así que benefíciese de una segunda tabla para resolver eso, pero eso no parece ser a lo que OP se refiere con respecto a las otras tablas.)
ToolmakerSteve
14

¿ Todas esas tablas tienen una 1-to-1relación? Por ejemplo, ¿cada fila de usuario solo tendrá una fila correspondiente en user_statso user_levels? Si es así, podría tener sentido combinarlos en una sola tabla. Sin embargo, si la relación no es así 1 to 1 , probablemente no tendría sentido combinarlos (desnormalizarlos).

Tenerlos en tablas separadas frente a una tabla probablemente tendrá poco efecto en el rendimiento, a menos que tenga cientos de miles o millones de registros de usuarios. La única ganancia real que obtendrá es simplificar sus consultas combinándolas.

ETA:

Si lo que le preocupa es tener demasiadas columnas , piense qué cosas suele usar juntas y combínelas , dejando el resto en una tabla separada (o varias tablas separadas si es necesario).

Si observa la forma en que usa los datos, supongo que encontrará que aproximadamente el 80% de sus consultas usan el 20% de esos datos y el 80% restante de los datos se usa solo ocasionalmente. Combine ese 20% de uso frecuente en una tabla y deje el 80% que no usa con frecuencia en tablas separadas y probablemente tendrá un buen compromiso.

Eric Petroelje
fuente
Sí, cada tabla solo tiene 1 fila para cada usuario, simplemente para evitar el dolor de cabeza de administrar una gran cantidad de datos duplicados. Por eso estoy pensando que una mesa se adapta. Si los datos del usuario abarcan varias filas, esperaría tener esas tablas separadas de la tabla de usuario principal.
Peter Craig
1
Si cada tabla tiene una relación de 1 a 1, una tabla sería más fácil de usar. En ese caso, no es necesario dividir la mesa. Dividir la tabla sugiere que hay más de 1 fila, lo que podría llevar a un caso en el que otro desarrollador los trataría de esa manera.
Richard L
Pensamiento muy interesante que aplica 80/20 al diseño de tablas de bases de datos. Me hizo pensar también en el diseño de la clase OOP (soy principalmente un desarrollador de Java) y me pregunto si lo mismo podría ser efectivo allí (coloque la funcionalidad de la aplicación primaria al 80% en una clase y el resto en otras clases).
Zack Macomber
1
@ZackMacomber: no, la división de clases debe basarse en la localidad de referencia . El beneficio de dividirse en varias clases es dibujar un borde alrededor de una unidad de funcionalidad más pequeña, de modo que sea más fácil de comprender / probar / cambiar, y aclarar dónde interactúa esa unidad con otras unidades de funcionalidad. El objetivo es mantener la mayoría de las conexiones (referencias, llamadas) dentro de una unidad, con pocas conexiones entre unidades . La definición de varias interfaces que implementa la clase, con diferentes interfaces por caso de uso, puede ser un primer paso útil hacia esa división.
ToolmakerSteve
@ToolmakerSteve Buenos pensamientos +1
Zack Macomber
9

La creación de una tabla masiva va en contra de los principios de las bases de datos relacionales. No los combinaría todos en una sola tabla. Obtendrá varias instancias de datos repetidos. Si su usuario tiene tres intereses, por ejemplo, tendrá 3 filas, con los mismos datos de usuario solo para almacenar los tres intereses diferentes. Definitivamente opte por el enfoque de tablas múltiples 'normalizadas'. Vea esta página Wiki para la normalización de la base de datos.

Editar: He actualizado mi respuesta, ya que ha actualizado su pregunta ... Estoy de acuerdo con mi respuesta inicial incluso más ahora desde ...

Es probable que una gran parte de estas celdas permanezcan vacías

Si, por ejemplo, un usuario no tenía ningún interés, si normaliza, entonces simplemente no tendrá una fila en la tabla de intereses para ese usuario. Si tiene todo en una tabla masiva, entonces tendrá columnas (y aparentemente muchas de ellas) que contienen solo NULL.

He trabajado para una empresa de telefonía donde ha habido toneladas de tablas, obtener datos podría requerir muchas uniones. Cuando el rendimiento de la lectura de estas tablas era crítico, se creaban procedimientos que podían generar una tabla plana (es decir, una tabla desnormalizada) que no requeriría uniones, cálculos, etc. a los que pudieran apuntar los informes. Estos se usaron luego junto con un agente de servidor SQL para ejecutar el trabajo en ciertos intervalos (es decir, una vista semanal de algunas estadísticas se ejecutaría una vez a la semana y así sucesivamente).


fuente
Me gusta este enfoque, porque los datos desnormalizados solo existen temporalmente, como una instantánea de un momento en el tiempo. Sin problemas de inserción / modificación / eliminación, simplemente deséchelo cuando haya terminado.
ToolmakerSteve
7

¿Por qué no utilizar el mismo enfoque que hace Wordpress al tener una tabla de usuarios con información básica de usuario que todos tienen y luego agregar una tabla "user_meta" que puede ser básicamente cualquier par clave, valor asociado con la identificación del usuario? Entonces, si necesita encontrar toda la metainformación del usuario, puede agregarla a su consulta. Tampoco siempre tendría que agregar la consulta adicional si no es necesaria para cosas como iniciar sesión. El beneficio de este enfoque también deja su mesa abierta para agregar nuevas funciones a sus usuarios, como almacenar su identificador de Twitter o cada interés individual. Tampoco tendrá que lidiar con un laberinto de ID asociados porque tiene una tabla que rige todos los metadatos y la limitará a una sola asociación en lugar de 50.

Wordpress hace esto específicamente para permitir que se agreguen funciones a través de complementos, lo que permite que su proyecto sea más escalable y no requerirá una revisión completa de la base de datos si necesita agregar una nueva función.

Rudy García
fuente
La wp_usermetatabla de Wordpress crece geométricamente. Cada usuario agrega X filas a la wp_usermetatabla, una fila por cada pieza de metainformación que queremos mantener para ese usuario. Si mantiene 8 campos personalizados para cada usuario, eso significa que wp_usermeta tendrá users * 8filas de largo. Esto parece estar causando problemas de rendimiento, pero no estoy seguro de si ese es el problema o no…
thirdender
1
Pude ver cómo esto podría causar problemas de rendimiento si tiene decenas de miles de usuarios. Básicamente, la base de datos tendría que buscar entre 10000 * 8 entradas en la metatabla del usuario para encontrar las que está buscando. Sin embargo, si solo consulta los metadatos cuando sea necesario, creo que su rendimiento sería mejor. Si siempre solicita los metadatos, incluso cuando no los necesita, es posible que tenga problemas. Si siempre necesita los metadatos, tal vez dividir las tablas no sea el mejor enfoque.
Rudy García
1
Ayer mismo tratamos con un tema de WP que cargaba a todos los usuarios (usando get_users()) solo para calcular la paginación. Una vez que corregimos el código para usar una SELECT COUNT(…)consulta para la paginación, el tiempo de carga de la página pasó de 28 segundos a aproximadamente 400 ms. Todavía me pregunto cómo se compara el rendimiento con las tablas unidas o una sola tabla plana ... He tenido problemas para encontrar métricas de rendimiento en la web.
Thirdender
Pensando en mi comentario anterior, parecería que dividir la tabla sigue siendo eficiente a menos que por alguna razón, como el ejemplo anterior de paginación, necesite seleccionar todos los usuarios. Aunque si está recuperando toda la metainformación, todavía tendría 80k entradas en la tabla usermeta. Eso es mucho para buscar. Quizás alguien podría probar cuál es un mejor enfoque ejecutando un script en ambas implementaciones y ejecutarlo 100 veces para obtener el promedio, podría hacer eso.
Rudy García
1
Leí esto nuevamente hoy y me di cuenta de que mi comentario sobre 10000 * 8 entradas es cierto, sin embargo, la forma en que funciona una base de datos debería hacer que no sea un problema. Si por alguna razón estuviera capturando a los 10000 usuarios Y también su metainformación, sería ridículo. No puedo pensar en ningún escenario en el que quieras esto. Sin embargo, una base de datos recuperará fácilmente el meta de un solo usuario con la velocidad del rayo debido a las claves externas y la indexación. Suponiendo que su modelo db esté configurado correctamente.
Rudy García
5

Creo que esta es una de esas situaciones de "depende". Tener varias tablas es más limpio y probablemente teóricamente mejor. Pero cuando tiene que unir 6-7 tablas para obtener información sobre un solo usuario, puede comenzar a repensar ese enfoque.

Tundey
fuente
1

Yo diría que depende de lo que realmente signifiquen las otras tablas. ¿Un user_details contiene más de 1 usuario / más y así sucesivamente? El nivel de normalización que mejor se adapte a sus necesidades depende de sus demandas.

Si tiene una tabla con un buen índice, probablemente sea más rápido. Pero por otro lado probablemente sea más difícil de mantener.

Para mí, parece que podría omitir User_Details, ya que probablemente sea una relación 1 a 1 con los usuarios. ¿Pero el resto son probablemente muchas filas por usuario?

Richard L
fuente