Diseño de base de datos: ¿Almacenar estado o calcular estado cada vez?

17

Digamos que tengo una aplicación de base de datos relacional y un objeto "usuario" y un objeto "mensaje". Ahora quiero mostrar la cantidad de mensajes no leídos a este usuario.

¿Cuál es la mejor manera de archivar esto? ¿Introduzco un campo en el usuario y lo cuento si el usuario recibe un mensaje y lo disminuyo si lo lee? ¿O ejecuto una consulta cada vez para calcular el número de mensajes para el usuario que están marcados como no leídos?

Creo que el primer enfoque es más complicado y propenso a errores, pero funcionará mejor que el segundo enfoque.

¿Cómo se hace esto normalmente o cuál es el mejor enfoque?

ene
fuente
1
Depende de varios factores: ¿está su base de datos particionada? ¿Cuántas filas / usuario esperas? ¿Qué tamaño de DB total espera (o cuántos usuarios totales)? ¿Cuántas solicitudes por segundo espera? Todo esto no tiene que ser exacto, pero algunas ideas aproximadas ...
Omer Iqbal
10
+1 Esta es una pregunta clásica de base de datos relacional. ¿Normalizar o no normalizar? Esa es la pregunta. ¿Es más noble en el esquema sufrir las hondas y flechas de una escandalosa duplicación, o tomar disparadores y, al emplearlos, terminarlos?
Ross Patterson
Argumento si esto es un clásico Rel. db. pregunta, ya debería haber una respuesta en el sitio, esto debería cerrarse como DUP, o no tenemos una respuesta y esto debería dejarse abierto.
mattnz

Respuestas:

14

¿Cómo se hace esto normalmente o cuál es el mejor enfoque?

El mejor enfoque es probarlo primero sin un campo adicional, medir el rendimiento y si realmente resulta demasiado lento, intente optimizarlo. Esto podría significar cambiar a su primer enfoque utilizando un campo adicional, pero también debe considerar probar otras opciones, por ejemplo, poner un índice adicional en los campos combinados ("no leídos", "ID de usuario") en sus mensajes.

Doc Brown
fuente
2
El mejor enfoque es (ir primero con el método más simple). Las reglas generales son mejores que las específicas, fwiw. (+1 para "prueba").
DougM
9

La solución del libro de texto según la teoría de la base de datos sería no tener valores en su base de datos que dependan de los valores de otros datos, porque estas son dependencias transitivas . Tener campos que son valores calculados basados ​​en otros campos es una violación de la normalización, ya que conduce a información redundante.

Sin embargo, a veces difieren lo que dice el libro de texto y cuál es el método más práctico en la práctica. Contar el número de mensajes no leídos en cada página vista puede ser una operación bastante costosa. El almacenamiento en caché del número en la usertabla sería mucho mejor para el rendimiento. El costo sería que es posible que existan inconsistencias en la base de datos: es posible que un mensaje se elimine, agregue o lea sin recordar actualizar también el contador no leído.

Philipp
fuente
44
El problema de consistencia es fácil de lamer con disparadores que ajustan el contador en INSERTo DELETE. (O UPDATE, para dar cuenta del cambio de propietario de un mensaje). Un buen DBMS hará la operación y ejecutará los disparadores en la misma transacción, por lo que sucederá todo o nada.
Blrfl
4

El problema potencial es el rendimiento y aún no tiene un problema de rendimiento. Hay muchas cosas que puede hacer dependiendo de la base de datos de elección para manejar esto en la solución # 1: indexación, hardware, almacenamiento en caché, etc. Todo esto depende de la frecuencia con la que el usuario necesita obtener un recuento actual de mensajes no leídos. Muchas de estas opciones no requieren una codificación personalizada en el lado de la aplicación, por lo que puede implementarlas con un cambio de código o muy poco. Hace que sea más fácil crecer con la aplicación.

Una vez que un usuario se conecta / inicia sesión, obtener el recuento de la base de datos una vez no es tan malo. ¿Su aplicación mantendrá una lista constantemente actualizada de mensajes como el correo electrónico? Obtener un recuento no leído desde aquí no requiere otro viaje a la base de datos y, de todos modos, obtener nuevos mensajes va a hacer un viaje de db.

¿Viaja a la base de datos cada vez que se lee un mensaje para marcar el IsRead? campo es suficiente sin un nuevo cálculo de otro campo.

Con la solución n. ° 2 (mantener un recuento en un campo / en el disco), ¿necesitará una rutina para reconstruir / recalcular periódicamente este campo cuando haya un problema? Y siempre hay problemas. ¿Vas a envolver todo esto en una transacción? Cada vez que alguien le envía un mensaje a otra persona, podría fallar porque no puede actualizar el UnreadCount del usuario receptor debido a un bloqueo de la tabla Usuario. ¿O va a crear una tabla separada para este campo?

JeffO
fuente
+1 por mencionar los problemas de rendimiento al mantener actualizados los campos de conteo
winkbrace
0

La forma en que lo haría sería ejecutando una consulta cada vez, es decir, su segundo enfoque. Solo asegúrese de agregar un índice en su tabla de mensajes en la columna que actúe como una clave externa a la tabla de usuarios para mejorar el rendimiento de su consulta.

Luego, como dice Doc, mida el rendimiento de este enfoque y luego podrá saber si necesita tomar un camino diferente.

Jose B
fuente