2012-01-24 8 views
10

Tengo una pregunta relacionada con el diseño del esquema HBase. El problema es bastante simple: estoy almacenando "notificaciones" en hbase, cada una de las cuales tiene un estado ("nuevo", "visto" y "leído"). Aquí están las API necesito proporcionar:Diseño del esquema de HBase para admitir mejor las consultas específicas

  • conseguir todas las notificaciones para un usuario
  • Obtener todos los "nuevos" notificaciones para un usuario
  • obtener el recuento de todos los "nuevos" notificaciones para un usuario
  • Actualizar el estado de una notificación
  • Actualizar el estado de todas las notificaciones de un usuario
  • obtener todos los "nuevos" notificaciones al otro lado de la base de datos
  • Notificaciones sho uld puede escanearse en orden cronológico inverso y permitir la paginación.

Tengo algunas ideas, y quería ver si una de ellas es claramente la mejor, o si me he perdido una buena estrategia por completo. Común a los tres, creo que tener una fila por notificación y tener la identificación de usuario en la clave de fila es el camino a seguir. Para obtener un orden cronológico de la paginación, también necesito tener una marca de tiempo invertida allí. Me gustaría guardar todos los elementos en una tabla (para no tener que combinar la ordenación para la llamada "obtener todos las notificaciones para un usuario") y no quiero escribir trabajos por lotes para tablas de índice secundarias (ya que las actualizaciones a el recuento y el estado deberían ser en tiempo real).

La forma más sencilla de hacerlo sería (1) la clave de fila es "userId_reverseTimestamp" y filtrar el estado en el lado del cliente. Esto parece ingenuo, ya que enviaremos muchos datos innecesarios a través de la red.

La siguiente posibilidad es (2) codificar el estado en la clave de fila también, por lo tanto, o bien "userId_reverseTimestamp_status" y luego hacer el filtrado de la expresión regular de la fila en los escaneos. El primer problema que veo es la necesidad de eliminar una fila y copiar los datos de notificación a una nueva fila cuando el estado cambia (lo que presumiblemente debería ocurrir exactamente dos veces por notificación). Además, dado que el estado es la última parte de la clave de fila, para cada usuario, estaremos escaneando muchas filas adicionales. ¿Es esto un gran golpe de rendimiento? Finalmente, para cambiar el estado, tendré que saber cuál era el estado anterior (para construir la clave de fila) o tendré que hacer otro análisis.

La última idea que tuve es que (3) tenga dos familias de columnas, una para los datos de notificación estáticos y otra como bandera para el estado, es decir, "s: leer" o "s: nuevo" con "s 'como el cf y el estado como el calificador. Habría exactamente uno por fila, y puedo hacer un MultipleColumnPrefixFilter o SkipFilter con ColumnPrefixFilter contra ese cf. Aquí también, tendría que eliminar y crear columnas en el cambio de estado, pero debería ser mucho más ligero que copiar filas enteras. Mi única preocupación es la advertencia en el libro de HBase de que a HBase no le va bien con "más de 2 o 3 familias de columnas", quizás si el sistema necesita ampliarse con más capacidades de consulta, la estrategia multi-cf no escalará. .

Parece que (1) tendría demasiada sobrecarga de red. (2) parece que habría desperdiciado el costo de copiar los datos y (3) podría causar problemas con demasiadas familias. Entre (2) y (3), ¿qué tipo de filtro debería proporcionar un mejor rendimiento? En ambos casos, el escaneo tendrá en cuenta cada fila para un usuario, que presumiblemente tiene notificaciones en su mayoría leídas, lo que tendría un mejor rendimiento. Creo que me inclino por (3): ¿hay otras opciones (o ajustes) que me he perdido?

+0

¿Las notificaciones indican 'nuevo' y 'leído' solo con una única transición posible de nueva a lectura? ¿Cuál es el volumen de estas notificaciones? – Gevorg

Respuesta

2

¡Has pensado mucho en esto y creo que los tres son razonables!

Desea que su clave principal sea el nombre de usuario concatenado con la marca de tiempo, ya que la mayoría de sus consultas son "por usuario". Esto ayudará con la fácil paginación con un escaneo y puede obtener información del usuario con bastante rapidez.

Creo que la clave de su problema es esta parte del cambio de estado. En general, algo así como "leer" -> "eliminar" -> "reescribir" introduce todo tipo de problemas de concurrencia. ¿Qué pasa si tu tarea falla entre? ¿Tiene datos en un estado inválido? ¿Dejarás caer un registro?

Le sugiero que trate la tabla como "solo anexar". Básicamente, haz lo que sugieres para el n. ° 3, pero en lugar de quitar la bandera, mantenlo allí. Si algo se ha leído, puede tener las tres "s: seen", "s: read" allí (si es nuevo, podemos suponer que está vacío). También podría ser elegante y poner una marca de tiempo en cada uno de los tres para mostrar cuándo se cumplió ese evento. No deberías ver mucho de un golpe de rendimiento al hacer esto y entonces no tienes que preocuparte por la concurrencia, ya que todas las operaciones son solo de escritura y atómicas.

Espero que esto sea útil. No estoy seguro de haber respondido todo, ya que su pregunta era muy amplia. Siga con preguntas adicionales y me encantaría elaborar o discutir algo más.

+0

Gppd señala que es solo de escritura. No tener que hackear las actualizaciones atómicas lo hace mucho menos complejo: mi filtro será "siempre que no haya estados no leídos". Otra opción que alguien sugirió es tener múltiples columnas por fila, donde una fila es todo para un usuario. Presumiblemente, las columnas se ordenan de manera similar a las filas. Mi pregunta es, ¿esto nos da algo? También sugirieron simplemente hacer un ValueFilter en el notif (para que el estado en vivo en los datos, que necesita ser actualizado, en lugar de un CF por separado). Creo que esto tendría un peor rendimiento. ¿Pensamientos? – dyross

1

Mi solución es:

No guardar el estado de notificaciones (visto, nuevo) en hbase para cada notificación. Para las notificaciones, use un esquema simple. Clave: userid_timestamp - column: notification_message.

Una vez que el cliente le pregunta a la API "Obtener todas las nuevas notificaciones", guarde la marca de tiempo (todas las notificaciones nuevas se envían). Clave: id de usuario - colimn: All_new_notifications_pushed_time

Cada notificación con marca de tiempo es menor que "Todas las notificaciones nuevas empujó" asumido "ven", y si grande asuma "Nuevo"

Para obtener todas las notificaciones nuevas: En primer lugar conseguir valor (indicación de fecha y hora) para All_new_notifications_pushed_time por el ID de usuario , luego realice la exploración de rango en la columna notification_message por clave: de current_timestamp a All_new_notifications_pushed_time.

Esto limitará significativamente las columnas afectadas, y la mayoría de ellas deberían estar en memstore.

Cuente las nuevas notificaciones en el cliente.

Cuestiones relacionadas