php - node - realtime bd
¿Cómo puedo mejorar este feed de noticias PHP/MySQL? (5)
Permítanme comenzar de inmediato diciendo que sé que esta no es la mejor solución. Sé que es kludgy y un truco de una función. ¡Pero es por eso que estoy aquí!
Esta pregunta / trabajo genera cierta discusión sobre Quora con Andrew Bosworth , creador de las noticias de Facebook.
Estoy construyendo una fuente de noticias . Está construido únicamente en PHP
y MySQL
.
El MySQL
El modelo relacional para la alimentación se compone de dos tablas. Una tabla funciona como un registro de actividad; de hecho, se llama activity_log
. La otra mesa es newsfeed
. Estas tablas son casi idénticas.
El esquema para el registro es activity_log(uid INT(11), activity ENUM, activity_id INT(11), title TEXT, date TIMESTAMP)
... y el esquema para la alimentación es newsfeed(uid INT(11), poster_uid INT(11), activity ENUM, activity_id INT(11), title TEXT, date TIMESTAMP)
.
Cada vez que un usuario hace algo relevante para el suministro de noticias, por ejemplo, haciendo una pregunta, se registrará en el registro de actividad inmediatamente.
Generando las noticias
Luego, cada X minutos (5 minutos en este momento, cambiarán a 15-30 minutos más tarde), ejecuto un trabajo cron que ejecuta el script a continuación. Esta secuencia de comandos recorre todos los usuarios de la base de datos, encuentra todas las actividades para todos los amigos de ese usuario y luego escribe esas actividades en la fuente de noticias.
Por el momento, el SQL
que elimina la actividad (llamado en ActivityLog::getUsersActivity()
) tiene un LIMIT 100
impuesto por razones de rendimiento *. * No es que sepa de lo que estoy hablando.
<?php
$user = new User();
$activityLog = new ActivityLog();
$friend = new Friend();
$newsFeed = new NewsFeed();
// Get all the users
$usersArray = $user->getAllUsers();
foreach($usersArray as $userArray) {
$uid = $userArray[''uid''];
// Get the user''s friends
$friendsJSON = $friend->getFriends($uid);
$friendsArray = json_decode($friendsJSON, true);
// Get the activity of each friend
foreach($friendsArray as $friendArray) {
$array = $activityLog->getUsersActivity($friendArray[''fid2'']);
// Only write if the user has activity
if(!empty($array)) {
// Add each piece of activity to the news feed
foreach($array as $news) {
$newsFeed->addNews($uid, $friendArray[''fid2''], $news[''activity''], $news[''activity_id''], $news[''title''], $news[''time'']);
}
}
}
}
Visualización de los feeds de noticias
En el código del cliente, cuando obtengo el feed de noticias del usuario, hago algo como:
$feedArray = $newsFeed->getUsersFeedWithLimitAndOffset($uid, 25, 0);
foreach($feedArray as $feedItem) {
// Use a switch to determine the activity type here, and display based on type
// e.g. User Name asked A Question
// where "A Question" == $feedItem[''title''];
}
Mejorando las noticias
Ahora perdone mi limitada comprensión de las mejores prácticas para desarrollar un suministro de noticias, pero entiendo que el enfoque que estoy usando es una versión limitada de lo que se llama abanderamiento en escritura , limitado en el sentido de que estoy ejecutando un trabajo cron. como un paso intermedio en lugar de escribir en las noticias de los usuarios directamente. Pero esto es muy diferente de un modelo de extracción, en el sentido de que el suministro de noticias del usuario no se compila en la carga, sino con regularidad.
Esta es una gran pregunta que probablemente merece una gran cantidad de ida y vuelta, pero creo que puede servir como piedra de toque para muchas conversaciones importantes que los nuevos desarrolladores como yo tenemos que tener. Solo estoy tratando de descubrir qué estoy haciendo mal, cómo puedo mejorar, o cómo debería comenzar desde cero y probar un enfoque diferente.
Otra cosa que me molesta acerca de este modelo es que funciona en base a lo reciente en lugar de relevancia. Si alguien puede sugerir cómo se puede mejorar esto para trabajar en la relevancia, yo sería todo oídos. Estoy usando la API de Directed Edge para generar recomendaciones, pero parece que para algo así como un avance de noticias, los recomendadores no funcionarán (¡ya que nada ha sido favorecido previamente!).
¿Agregarías palabras clave estadísticas? Hice una implementación (cruda) mediante la explosión del cuerpo de mi documento, pelando HTML, eliminando palabras comunes y contando las palabras más comunes. Hice eso hace unos años solo por diversión (como con cualquier proyecto de este tipo, la fuente se ha ido), pero funcionó para mi configuración temporal de prueba-blog / foro. Tal vez funcionará para su fuente de noticias ...
En lugar de ejecutar un trabajo cron, un script post-commit de algún tipo. No sé específicamente cuáles son las capacidades de PHP y MySQL en este sentido. Si recuerdo correctamente, MySQL InnoDB permite funciones más avanzadas que otras variedades, pero no recuerdo si hay elementos como activadores en la última versión.
de todos modos, una variedad simple que no depende de mucha magia de base de datos:
cuando el usuario X agrega contenido:
1) haga una llamada asíncrona desde su página PHP después de la confirmación de la base de datos (asíncrono por supuesto para que el usuario que visualiza la página no tenga que esperar)
La llamada inicia una instancia de tu script lógico.
2) el script lógico va solo a través de la lista de amigos [A, B, C] del usuario que cometió el nuevo contenido (¡en oposición a la lista de todos en el DB!) Y agrega la acción del usuario X a los feeds para cada de estos usuarios
Podrías simplemente almacenar estos feeds como archivos JSON verticales y anexar nuevos datos al final de cada uno. Mejor por supuesto, mantener los feeds en caché con una copia de seguridad en el sistema de archivos o BerkeleyDB o Mongo o lo que quieras.
Esta es solo una idea básica para los feeds basados en recencia, no relevancia. Usted PODRÍA almacenar los datos secuencialmente de esta manera y luego realizar un análisis adicional por usuario para filtrar por relevancia, pero este es un problema difícil en cualquier aplicación y probablemente no puede ser fácilmente abordado por un usuario web anónimo sin detallar conocimiento de sus requisitos;)
jsh
Estoy tratando de crear un feed de noticias estilo Facebook por mi cuenta. En lugar de crear otra tabla para registrar las actividades de los usuarios, calculé el ''borde'' de la UNIÓN de publicaciones, comentarios, etc.
Con un poco de matemáticas, calculo el ''borde'' usando un modelo de caída exponencial, con el tiempo transcurrido como variable independiente, teniendo en cuenta la cantidad de comentarios, "Me gusta", etc. que cada publicación tiene para formular la constante lambda. El borde disminuirá rápidamente al principio pero gradualmente se aplana a casi 0 después de unos días (pero nunca llegará a 0)
Al mostrar la alimentación, cada borde se multiplica usando RAND (). Las publicaciones con bordes más altos aparecerán con más frecuencia
De esta forma, las publicaciones más populares tienen una mayor probabilidad de aparecer en las noticias, por un tiempo más prolongado.
Pregunta realmente genial. En realidad estoy en medio de implementar algo como esto yo mismo. Entonces, voy a pensar en voz alta un poco.
Estos son los defectos que veo en mi mente con tu implementación actual:
Está procesando todos los amigos para todos los usuarios, pero terminará procesando los mismos usuarios muchas veces debido a que los mismos grupos de personas tienen amigos similares.
Si uno de mis amigos publica algo, no aparecerá en mis noticias durante al menos 5 minutos. Mientras que debería aparecer inmediatamente, ¿verdad?
Estamos leyendo todo el feed de noticias para un usuario. ¿No tenemos que tomar las nuevas actividades desde la última vez que hicimos crujir los registros?
Esto no se escala tan bien.
El suministro de noticias parece exactamente los mismos datos que el registro de actividad, me quedaría con esa tabla de registro de actividad.
Si modifica los registros de actividad en las bases de datos, le permitirá escalar más fácilmente. Puede fragmentar a los usuarios si lo desea, pero incluso si tiene 10 millones de registros de usuario en una tabla, mysql debería estar bien haciendo lecturas. Por lo tanto, cada vez que busque un usuario, sabrá a qué fragmento acceder desde los registros del usuario. Si archivas los registros más antiguos cada cierto tiempo y solo mantienes un nuevo conjunto de registros, no tendrás que fragmentar tanto. O tal vez incluso en absoluto. Puede administrar muchos millones de registros en MySQL si está sintonizado incluso moderadamente bien.
Utilizaría memcached para su tabla de usuarios y posiblemente incluso los registros mismos. Memcached permite entradas de memoria caché de hasta 1mb de tamaño, y si fue inteligente en la organización de sus claves, podría recuperar todos los registros más recientes de la memoria caché.
Esto sería más trabajo en lo que a arquitectura se refiere, pero le permitirá trabajar en tiempo real y escalar en el futuro ... especialmente cuando desee que los usuarios comiencen a comentar cada publicación. ;)
¿Has visto este artículo?
entre usted puede usar banderas de usuario y almacenamiento en caché. Digamos que tiene un nuevo campo para el usuario como last_activity. Actualice este campo cuando el usuario ingrese cualquier actividad. Mantenga una bandera, hasta el momento en que haya obtenido los feeds, digamos feed_updated_on.
Ahora actualiza la función $ user-> getAllUsers (); para devolver solo a los usuarios que tienen last_activity time después de feed_updated_on. Esto excluirá a todos los usuarios que no tengan ningún registro de actividad :). Proceso similar para los amigos de los usuarios.
También puede usar el almacenamiento en caché como Memcache o el almacenamiento en caché a nivel de archivo.
O use algunos DB nosql para almacenar todos los feeds como un solo documento.