Construyendo un sistema de notificación [cerrado]

170

Estoy comenzando a construir un sistema de notificación de estilo Facebook para nuestra página (tipo de juego social) y ahora estoy investigando cuál sería la mejor manera de diseñar dicho sistema. No estoy interesado en cómo enviar notificaciones al usuario ni nada por el estilo (por ahora incluso). Estoy investigando cómo construir el sistema en el servidor (cómo almacenar notificaciones, dónde almacenarlas, cómo buscarlas, etc.).

Entonces ... algunos requisitos que tenemos:

  • en las horas pico tenemos alrededor de 1k usuarios concurrentes conectados (y muchos más invitados, pero no importan aquí ya que no tendrán notificaciones) que generarán muchos eventos
  • habrá diferentes tipos de notificaciones (el usuario A te ha agregado como amigo, el usuario B ha comentado tu perfil, al usuario C le ha gustado tu imagen, el usuario D te ha ganado en el juego X, ...)
  • la mayoría de los eventos generarán 1 notificación para 1 usuario (al usuario X le ha gustado su imagen), pero habrá casos en los que un evento generará muchas notificaciones (por ejemplo, es el cumpleaños del usuario Y)
  • las notificaciones deben agruparse juntas; si, por ejemplo, a cuatro usuarios diferentes les gusta alguna imagen, el propietario de esa imagen debería recibir una notificación que indique que a cuatro usuarios les gustó la imagen y no cuatro notificaciones separadas (al igual que FB)

Bien, lo que estaba pensando es que debería crear algún tipo de cola donde almacenaría eventos cuando ocurrieran. Entonces tendría un trabajo de fondo ( ¿ gearman ?) Que miraría esa cola y generaría notificaciones basadas en esos eventos. Este trabajo luego almacenaría notificaciones en la base de datos para cada usuario (por lo que si un evento afecta a 10 usuarios, habría 10 notificaciones separadas). Luego, cuando el usuario abría una página con la lista de notificaciones, leía todas esas notificaciones para él (pensamos limitar esto a las 100 últimas notificaciones) y las agrupaba y finalmente las mostraba.

Cosas que me preocupan con este enfoque:

  • complejo como el infierno :)
  • es la base de datos el mejor almacenamiento aquí (estamos usando MySQL) o debería usar otra cosa (redis parece ser una buena opción también)
  • ¿Qué debo guardar como notificación? ID de usuario, ID de usuario que inició el evento, tipo de evento (para que pueda agruparlos y mostrar el texto apropiado) pero luego no sé cómo almacenar los datos reales de la notificación (por ejemplo, URL y título de la imagen que me gustó). ¿Debo "hornear" esa información cuando genero la notificación, o debo almacenar la identificación del registro (imagen, perfil, ...) afectado y extraer la información de la base de datos cuando muestre la notificación?
  • el rendimiento debería estar bien aquí, incluso si tengo que procesar 100 notificaciones sobre la marcha cuando visualizo la página de notificaciones
  • posible problema de rendimiento en cada solicitud porque tendría que mostrar el número de notificaciones no leídas al usuario (lo que podría ser un problema en sí mismo ya que agruparía las notificaciones). Sin embargo, esto podría evitarse si generara la vista de notificaciones (donde están agrupadas) en segundo plano y no sobre la marcha

Entonces, ¿qué opinas sobre mi solución propuesta y mis preocupaciones? Comente si cree que debería mencionar algo más que sería relevante aquí.

Oh, estamos usando PHP para nuestra página, pero eso no debería ser un gran factor aquí, creo.

Jan Hančič
fuente
Cuánto tiempo le llevó construir este sistema de notificación como un solo hombre. Solo quiero tener una estimación para hacer las líneas de tiempo en consecuencia.
Shaharyar
@Shaharyar Creo que depende de la complejidad del sistema de notificación.
tyan
Usé el mismo sistema con MySQL para construir un sistema de notificación basado en prioridades. Lo bueno es que se escala a unos pocos miles de usuarios, si va más allá de eso, explota, especialmente con Android y GCM. Me gustaría conocer alternativas a MySQL como redis, rabbitMQ, Kafka que naturalmente exhiben una cola de mensajes, tipo de funcionalidad.
Ankit Marothi

Respuestas:

168

Una notificación es sobre algo (objeto = evento, amistad ...) que alguien (actor) le ha cambiado (verbo = agregado, solicitado ...) y lo ha informado al usuario (sujeto). Aquí hay una estructura de datos normalizada (aunque he usado MongoDB). Debe notificar a ciertos usuarios sobre los cambios. Entonces, son notificaciones por usuario ... lo que significa que si hubiera 100 usuarios involucrados, generaría 100 notificaciones.

╔═════════════╗      ╔═══════════════════╗      ╔════════════════════╗
║notification ║      ║notification_object║      ║notification_change ║
╟─────────────╢      ╟───────────────────╢      ╟────────────────────╢
║ID           ║—1:n—→║ID                 ║—1:n—→║ID                  ║
║userID       ║      ║notificationID     ║      ║notificationObjectID║
╚═════════════╝      ║object             ║      ║verb                ║
                     ╚═══════════════════╝      ║actor               ║
                                                ╚════════════════════╝

(Agregue campos de tiempo donde mejor le parezca)

Esto es básicamente para agrupar cambios por objeto, para que pueda decir "Tiene 3 solicitudes de amistad". Y la agrupación por actor es útil, por lo que podría decir "El usuario James Bond hizo cambios en su cama". Esto también brinda la capacidad de traducir y contar las notificaciones a su gusto.

Pero, dado que el objeto es solo una ID, necesitaría obtener toda la información adicional sobre el objeto que desea con llamadas separadas, a menos que el objeto realmente cambie y desee mostrar ese historial (por ejemplo, "el usuario cambió el título del evento a ... ")

Dado que las notificaciones están cerca del tiempo real para los usuarios en el sitio, las vincularía con el cliente nodejs + websockets con php que empuja la actualización a nodejs para todos los oyentes a medida que se agrega el cambio.

Artjom Kurapov
fuente
1
notify_object.object identifica el tipo de cambio, como una cadena "amistad" La referencia real al objeto cambiado con sus datos adicionales de los que hablo está en
notify_change.notificationObjectID
2
Esta puede ser una pregunta tonta, pero con esta configuración, ¿qué haces una vez que el usuario ha visto o actuado en la notificación? ¿Simplemente lo elimina de la base de datos o simplemente usa fechas para ver si el usuario ha iniciado sesión desde que se creó la notificación?
Jeffery Mills
44
Sé que este tema ya es bastante antiguo, sin embargo, estoy un poco confundido acerca de la primera tabla, ¿cuál es exactamente el propósito de esta tabla? ¿Cuál es la ventaja de tener esto como una tabla separada en lugar de poner el ID de usuario en la tabla notificación_objeto? En otras palabras, ¿cuándo creará una nueva entrada en la notificación y cuándo simplemente agregará un objeto y cambiará a una notificación existente con esta estructura?
Bas Goossen
3
@JefferyMills Podría tener un campo de estado como is_notification_readen la notificationtabla y marcarlo apropiadamente si es unread, reado deleted.
Kevin
2
También me costó entender algunos aspectos de esta solución e hice una pregunta por separado al respecto: dba.stackexchange.com/questions/99401/…
user45623
27

Esta es realmente una pregunta abstracta, así que supongo que tendremos que discutirla en lugar de señalar lo que debe o no debe hacer.

Esto es lo que pienso sobre sus preocupaciones:

  • Sí, un sistema de notificación es complejo, pero no tan infernal. Puede tener muchos enfoques diferentes para modelar e implementar tales sistemas, y pueden tener desde un nivel medio hasta un alto nivel de complejidad;

  • Pesonalmente, siempre trato de hacer cosas basadas en bases de datos. ¿Por qué? Porque puedo garantizar tener el control total de todo lo que está sucediendo, pero solo soy yo, puedes tener el control sin un enfoque basado en la base de datos; confía en mí, querrás controlar ese caso;

  • Permítame ejemplificar un caso real para usted, para que pueda comenzar desde algún lugar. El año pasado modelé e implementé un sistema de notificación en algún tipo de red social (no como Facebook, por supuesto). ¿La forma en que solía almacenar notificaciones allí? Tenía una notificationstabla, donde guardaba la generator_user_id(la identificación del usuario que está generando la notificación), la target_user_id(tipo de obvio, ¿no?), La notification_type_id(que hacía referencia a una tabla diferente con tipos de notificación) y todo esas cosas necesarias con las que necesitamos llenar nuestras tablas (marcas de tiempo, banderas, etc.). Mi notification_typestabla solía tener una relación con una notification_templatestabla, que almacenaba plantillas específicas para cada tipo de notificación. Por ejemplo, tenía un POST_REPLYtipo, que tenía una plantilla similar {USER} HAS REPLIED ONE OF YOUR #POSTS. A partir de ahí, acabo de tratar el{}como variable y #como enlace de referencia;

  • Sí, el rendimiento debe y debe estar bien. Cuando piensa en notificaciones, piensa en el servidor empujando de pies a cabeza. Si lo vas a hacer con solicitudes de ajax o lo que sea, tendrás que preocuparte por el rendimiento. Pero creo que es una segunda preocupación;

El modelo que he diseñado no es, por supuesto, el único que puedes seguir, ni tampoco el mejor. Espero que mi respuesta, al menos, te siga en la dirección correcta.

Daniel Ribeiro
fuente
¿Por qué no tendría control con algún otro almacén de datos?
Jan Hančič
Bueno, no dije eso. Lo que dije es que solo puedo garantizar el control de datos con un enfoque basado en bases de datos; Pero solo soy yo. Voy a reformular eso.
Daniel Ribeiro
@DanielRibeiro los marcadores de posición ({...}) en la plantilla de notificación deben reemplazar los datos de marcadores de posición de los diferentes conjuntos de tablas en la base de datos para los diferentes tipos de notificaciones. Por ejemplo, una plantilla es "A {usuario} le ha gustado tu foto", otra plantilla es "Tu {Pagename} tiene un nuevo me gusta". Etc. {PageName} y {user} y otros marcadores de posición se asignarán desde la tabla de base de datos diferente, entonces, ¿cuál debería ser el esquema para obtener el valor de marcadores de posición dinámicamente?
Ashish Shukla
DanielRibeiro cómo reemplazó los marcadores de posición según lo solicitado por @Ashish Shukla,
Shantaram Tupe
@AshishShukla, ¿ha utilizado o reemplazado marcadores de posición y cómo?
Shantaram Tupe
8
╔════════════════════╗
║notification        ║
╟────────────────────╢
║Username            ║
║Object              ║
║verb                ║
║actor               ║
║isRead              ║
╚════════════════════╝

Esta parece una buena respuesta en lugar de tener 2 colecciones. Puede consultar por nombre de usuario, objeto e isRead para obtener nuevos eventos (como 3 solicitudes de amistad pendientes, 4 preguntas, etc.)

Avíseme si hay algún problema con este esquema.

Kaphy
fuente
3
La respuesta principal utilizó una estructura de datos normalizada, lo que significa que no hay redundancias en las tablas. ¿Tu respuesta hace eso?
Aaron Hall
4

Personalmente, no entiendo muy bien el diagrama de la respuesta aceptada, por lo que adjuntaré una base de diagrama de base de datos sobre lo que podría aprender de la respuesta aceptada y otras páginas.

ingrese la descripción de la imagen aquí

Las mejoras son bien recibidas.

Jason Glez
fuente
Parece que message_template estaría en la tabla NotificationType. También parece que main_url estaría en la tabla de notificaciones, entonces podría eliminar la tabla Notification_Message. ¿Puede explicar la razón por la que tiene la tabla NotificationMessage sola?
Jeff Ryan